From 202596ad3868caa5a214ef4bb85e70c688d9ccce Mon Sep 17 00:00:00 2001 From: yocvito Date: Sun, 1 Jan 2023 02:17:40 +0100 Subject: [PATCH 001/231] add buggy extra config UI for bad usb (for implementing BLE and UART) --- applications/main/bad_usb/bad_usb_app.c | 50 ++++- applications/main/bad_usb/bad_usb_app_i.h | 22 +- .../main/bad_usb/bad_usb_custom_event.h | 11 + applications/main/bad_usb/bad_usb_script.c | 8 +- applications/main/bad_usb/bad_usb_script.h | 6 +- .../bad_usb/scenes/bad_usb_scene_config.c | 198 ++++++++++++++++-- .../bad_usb/scenes/bad_usb_scene_config.h | 2 + .../main/bad_usb/scenes/bad_usb_scene_error.c | 6 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 2 +- .../main/gpio/scenes/gpio_scene_start.c | 8 +- firmware/targets/f7/ble_glue/gap.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 2 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 21 +- 13 files changed, 302 insertions(+), 36 deletions(-) create mode 100644 applications/main/bad_usb/bad_usb_custom_event.h diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index bb29d3be5..d537f7728 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -5,8 +5,14 @@ #include #include +// ble hid +#include +#include + #define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME +#define BAD_USB_LOGFILE_PATH BAD_USB_APP_BASE_FOLDER "/debug.log" + static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); BadUsbApp* app = context; @@ -54,6 +60,16 @@ static void bad_usb_save_settings(BadUsbApp* app) { BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(!storage_file_open(file, BAD_USB_LOGFILE_PATH, FSAM_WRITE, FSOM_OPEN_APPEND)) { + FURI_LOG_E("BadUsbApp", "Can't open debug log file"); + storage_file_close(file); + app->debug_file = NULL; + } else { + app->debug_file = file; + } + app->bad_usb_script = NULL; app->file_path = furi_string_alloc(); @@ -86,9 +102,19 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); - app->submenu = submenu_alloc(); + // app->submenu = submenu_alloc(); + // view_dispatcher_add_view( + // app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + + app->variable_item_list = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + app->view_dispatcher, + BadUsbAppViewConfig, + variable_item_list_get_view(app->variable_item_list)); + app->menu_idx = 0; + for(int i = 0; i < NUM_CONF_OPT; i++) { + app->menu_opt_idx[i] = 0; + } app->bad_usb_view = bad_usb_alloc(); view_dispatcher_add_view( @@ -96,6 +122,10 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + // default injection mode is USB + app->mode = BadUsbModeUSB; + F_DEBUG(app, "Starting BadUSB app\r\n"); + if(furi_hal_usb_is_locked()) { app->error = BadUsbAppErrorCloseRpc; scene_manager_next_scene(app->scene_manager, BadUsbSceneError); @@ -121,6 +151,10 @@ void bad_usb_app_free(BadUsbApp* app) { app->bad_usb_script = NULL; } + // BLE HID + F_DEBUG(app, "Stopping BLE HID"); + furi_hal_bt_hid_stop(); + // Views view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -130,8 +164,12 @@ void bad_usb_app_free(BadUsbApp* app) { widget_free(app->widget); // Submenu + // view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + // submenu_free(app->submenu); + + // Variable Item List view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); - submenu_free(app->submenu); + variable_item_list_free(app->variable_item_list); // View dispatcher view_dispatcher_free(app->view_dispatcher); @@ -147,6 +185,12 @@ void bad_usb_app_free(BadUsbApp* app) { furi_string_free(app->file_path); furi_string_free(app->keyboard_layout); + if(app->debug_file) { + storage_file_close(app->debug_file); + storage_file_free(app->debug_file); + } + furi_record_close(RECORD_STORAGE); + free(app); } diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index eda67eae5..09778a8f1 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -3,6 +3,10 @@ #include "bad_usb_app.h" #include "scenes/bad_usb_scene.h" #include "bad_usb_script.h" +#include "bad_usb_custom_event.h" +#include "bad_usb_script.h" + +#include #include #include @@ -20,11 +24,16 @@ #define BAD_USB_APP_SCRIPT_EXTENSION ".txt" #define BAD_USB_APP_LAYOUT_EXTENSION ".kl" +#define NUM_CONF_ITEM 3 // modify here if you add new item in config menu +#define NUM_CONF_OPT BadUsbModeNb // for now, scaled on hid mode (ble or usb) + typedef enum { BadUsbAppErrorNoFiles, BadUsbAppErrorCloseRpc, } BadUsbAppError; +#define F_DEBUG(app, s) do { if(app->debug_file) { storage_file_write(app->debug_file, s, strlen(s)); } } while(0) + struct BadUsbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -32,17 +41,26 @@ struct BadUsbApp { NotificationApp* notifications; DialogsApp* dialogs; Widget* widget; - Submenu* submenu; + + //Submenu* submenu; + VariableItemList* variable_item_list; + uint8_t menu_idx; + uint8_t menu_opt_idx[NUM_CONF_OPT]; BadUsbAppError error; FuriString* file_path; FuriString* keyboard_layout; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; + BadUsbMode mode; + + File *debug_file; }; typedef enum { BadUsbAppViewError, BadUsbAppViewWork, BadUsbAppViewConfig, -} BadUsbAppView; \ No newline at end of file + // for ble hid related information + BadUsbAppViewConfigBle +} BadUsbAppView; diff --git a/applications/main/bad_usb/bad_usb_custom_event.h b/applications/main/bad_usb/bad_usb_custom_event.h new file mode 100644 index 000000000..95773b805 --- /dev/null +++ b/applications/main/bad_usb/bad_usb_custom_event.h @@ -0,0 +1,11 @@ +#pragma once + +typedef enum { + BadUsbCustomEventKeyboardLayout, + BadUsbCustomEventModeUSB, + BadUsbCustomEventModeBLE, + BadUsbCustomEventModeUART, + BadUsbCustomEventConfigBle, + BadUsbCustomEventErrorBack, + BadUsbCustomEventNb, +} BadUsbCustomEvent; \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index aad79a329..9389d6044 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -8,6 +8,10 @@ #include "bad_usb_script.h" #include + +#include +#include + #define TAG "BadUSB" #define WORKER_TAG TAG "Worker" #define FILE_BUFFER_LEN 16 @@ -720,7 +724,7 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou storage_file_free(layout_file); } -void bad_usb_script_toggle(BadUsbScript* bad_usb) { +void bad_usb_script_toggle(BadUsbScript* bad_usb, BadUsbMode mode) { furi_assert(bad_usb); furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); } @@ -728,4 +732,4 @@ void bad_usb_script_toggle(BadUsbScript* bad_usb) { BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { furi_assert(bad_usb); return &(bad_usb->st); -} +} \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index 1e4d98fe7..399c4a06c 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -29,6 +29,8 @@ typedef struct { char error[64]; } BadUsbState; +typedef enum { BadUsbModeUSB, BadUsbModeBLE, BadUsbModeUART, BadUsbModeNb } BadUsbMode; + BadUsbScript* bad_usb_script_open(FuriString* file_path); void bad_usb_script_close(BadUsbScript* bad_usb); @@ -39,9 +41,7 @@ void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); -void bad_usb_script_toggle(BadUsbScript* bad_usb); - -BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); +void bad_usb_script_toggle(BadUsbScript* bad_usb, BadUsbMode mode); #ifdef __cplusplus } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c index 2a9f2f76c..4dfe0eabd 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -1,53 +1,219 @@ #include "../bad_usb_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" +#include "../bad_usb_script.h" -enum SubmenuIndex { - SubmenuIndexKeyboardLayout, +// enum SubmenuIndex { +// SubmenuIndexKeyboardLayout, +// SubmenuIndexInjectionMode, +// SubmenuIndexBleConfig +// }; + +enum VariableListIndex { + VariableListIndexKeyboardLayout, + VariableListIndexInjectionMode, + VariableListIndexBleConfig }; -void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { +typedef struct BadUsbConfigItem { + const char* name; + uint8_t num_options_menu; + const char* options_menu[NUM_CONF_OPT]; +} BadUsbConfigItem; + +const BadUsbCustomEvent bad_usb_custom_events_mode[NUM_CONF_OPT] = { + BadUsbCustomEventModeUSB, + BadUsbCustomEventModeBLE, + BadUsbCustomEventModeUART +}; + +#define NUM_BAD_USB_CONFIG_ITEMS 3 +const BadUsbConfigItem bad_usb_config_items[NUM_BAD_USB_CONFIG_ITEMS] = { + {"Keyboard layout", 1, {""}}, + {"Injection mode", 3, {"USB", "BLE", "UART"}}, + {"BLE Config", 1, {""}}, +}; + +// void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { +// BadUsbApp* bad_usb = context; +// view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +// } + +void bad_usb_scene_config_var_list_callback(void* context, uint32_t index) { BadUsbApp* bad_usb = context; view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); } +void bad_usb_scene_config_var_list_enter_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + + F_DEBUG(bad_usb, "[+] bad_usb_scene_config_var_list_enter_callback\r\n"); + + if (index == VariableListIndexKeyboardLayout) { + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, BadUsbCustomEventKeyboardLayout); + } else if (index == VariableListIndexInjectionMode) { + int menu_opt_idx = bad_usb->menu_opt_idx[index]; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, bad_usb_custom_events_mode[menu_opt_idx]); + } else if (index == VariableListIndexBleConfig) { + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, BadUsbCustomEventConfigBle); + } + + F_DEBUG(bad_usb, "[-] bad_usb_scene_config_var_list_enter_callback\r\n"); +} + +void bad_usb_scene_config_var_list_change_callback(VariableItem *item) { + furi_assert(item); + BadUsbApp* bad_usb = variable_item_get_context(item); + furi_assert(bad_usb); + + F_DEBUG(bad_usb, "[+] bad_usb_scene_config_var_list_change_callback\r\n"); + BadUsbConfigItem *menu_item = &bad_usb_config_items[bad_usb->menu_idx]; + uint8_t item_index = variable_item_get_current_value_index(item); + furi_assert(item_index < menu_item->num_options_menu); + variable_item_set_current_value_text(item, menu_item->options_menu[item_index]); + bad_usb->menu_opt_idx[bad_usb->menu_idx] = item_index; + F_DEBUG(bad_usb, "[-] bad_usb_scene_config_var_list_change_callback\r\n"); +} + void bad_usb_scene_config_on_enter(void* context) { BadUsbApp* bad_usb = context; - Submenu* submenu = bad_usb->submenu; + //Submenu* submenu = bad_usb->submenu; + char toggle_label[32]; + VariableItemList *item_list = bad_usb->variable_item_list; + VariableItem *item = NULL; - submenu_add_item( - submenu, - "Keyboard layout", - SubmenuIndexKeyboardLayout, - bad_usb_scene_config_submenu_callback, - bad_usb); + char debug_buf[256]; - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + F_DEBUG(bad_usb, "[+] BadUsbSceneConfig on_enter\r\n"); + + F_DEBUG(bad_usb, "Setting up variable item list\r\n"); + variable_item_list_set_enter_callback( + item_list, bad_usb_scene_config_var_list_enter_callback, bad_usb); + + for (int i=0; imenu_opt_idx[i]); + variable_item_set_current_value_text( + item, bad_usb_config_items[i].options_menu[bad_usb->menu_opt_idx[i]]); + + snprintf(debug_buf, sizeof(debug_buf), "Added item %s, index %d, opt_idx %d\r\n", + bad_usb_config_items[i].name, i, bad_usb->menu_opt_idx[i]); + F_DEBUG(bad_usb, debug_buf); + } + + variable_item_list_set_selected_item( + item_list, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + + // submenu_add_item( + // submenu, "Keyboard layout", + // SubmenuIndexKeyboardLayout, + // bad_usb_scene_config_submenu_callback, + // bad_usb); + + // furi_assert(bad_usb->bad_usb_script); + // // switch for choising Bad-USB or Bad-BLE + // BadUsbScriptMode hid_mode = bad_usb_script_get_hid_mode(bad_usb->bad_usb_script); + // furi_assert(hid_mode < BadUsbScriptModeNb); + // snprintf(toggle_label, sizeof(toggle_label), "Toggle Mode: %s", ducky_mode_names[hid_mode]); + // submenu_add_item( + // submenu, + // toggle_label, + // SubmenuIndexInjectionMode, + // bad_usb_scene_config_submenu_callback, + // bad_usb); + + // submenu_add_item( + // submenu, "BLE Config", + // SubmenuIndexBleConfig, + // bad_usb_scene_config_submenu_callback, + // bad_usb); + + // submenu_set_selected_item( + // submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); + + F_DEBUG(bad_usb, "[-] BadUsbSceneConfig on_enter\r\n"); } bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + // BadUsbApp* bad_usb = context; + // bool consumed = false; + + // if(event.type == SceneManagerEventTypeCustom) { + // scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + // consumed = true; + // if(event.event == SubmenuIndexKeyboardLayout) { + // scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + // } else if (event.event == SubmenuIndexInjectionMode) { + // BadUsbScriptMode hid_mode = bad_usb_script_get_hid_mode(bad_usb->bad_usb_script); + // if (hid_mode == BadUsbScriptModeUSB) { + // bad_usb_script_set_hid_mode(bad_usb->bad_usb_script, BadUsbScriptModeBLE); + // } else { + // bad_usb_script_set_hid_mode(bad_usb->bad_usb_script, BadUsbScriptModeUSB); + // } + // scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfig); + // } else if (event.event == SubmenuIndexBleConfig) { + // //scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleHid); + // } else { + // furi_crash("Unknown key type"); + // } + // } + + // return consumed; + furi_assert(context); BadUsbApp* bad_usb = context; bool consumed = false; + char debug_buf[256]; + F_DEBUG(bad_usb, "[+] BadUsbSceneConfig on_event\r\n"); if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); consumed = true; - if(event.event == SubmenuIndexKeyboardLayout) { + if(event.event == BadUsbCustomEventKeyboardLayout) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, bad_usb->menu_idx); scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else if(event.event == BadUsbCustomEventModeUSB) { + F_DEBUG(bad_usb, "mode usb triggered !\r\n"); + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, bad_usb->menu_idx); + bad_usb->mode = BadUsbModeUSB; + } else if (event.event == BadUsbCustomEventModeBLE) { + F_DEBUG(bad_usb, "mode ble triggered !\r\n"); + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, bad_usb->menu_idx); + bad_usb->mode = BadUsbModeBLE; + } else if (event.event == BadUsbCustomEventModeUART) { + F_DEBUG(bad_usb, "mode uart triggered !\r\n"); + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, bad_usb->menu_idx); + bad_usb->mode = BadUsbModeUART; + } else if (event.event == BadUsbCustomEventConfigBle) { + //scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBleHid); } else { furi_crash("Unknown key type"); } + } else if(event.type == SceneManagerEventTypeTick) { + bad_usb->menu_idx = variable_item_list_get_selected_item_index(bad_usb->variable_item_list); + snprintf(debug_buf, sizeof(debug_buf), "menu_idx: %d\r\n", bad_usb->menu_idx); + F_DEBUG(bad_usb, debug_buf); + consumed = true; } + F_DEBUG(bad_usb, "[-] BadUsbSceneConfig on_event\r\n"); return consumed; } void bad_usb_scene_config_on_exit(void* context) { BadUsbApp* bad_usb = context; - Submenu* submenu = bad_usb->submenu; + //Submenu* submenu = bad_usb->submenu; + + //submenu_reset(submenu); + + VariableItemList *item_list = bad_usb->variable_item_list; + variable_item_list_reset(item_list); - submenu_reset(submenu); } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 423aedc51..f2af3bfcc 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -3,3 +3,5 @@ ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) ADD_SCENE(bad_usb, config, Config) ADD_SCENE(bad_usb, config_layout, ConfigLayout) +// BLE HID config +// ADD_SCENE(bad_usb, config_ble_hid, ConfigBleHid) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 866339513..686f71147 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,8 +1,8 @@ #include "../bad_usb_app_i.h" -typedef enum { - BadUsbCustomEventErrorBack, -} BadUsbCustomEvent; +// typedef enum { +// BadUsbCustomEventErrorBack, +// } BadUsbCustomEvent; static void bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 2971c01e9..e5ed52e93 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -19,7 +19,7 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); consumed = true; } else if(event.event == InputKeyOk) { - bad_usb_script_toggle(app->bad_usb_script); + bad_usb_script_toggle(app->bad_usb_script, app->mode); consumed = true; } } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 08b77238f..c06d7b5c6 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -1,3 +1,6 @@ +#ifndef __GPIO_SCENE_START_H__ +#define __GPIO_SCENE_START_H__ + #include "../gpio_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" @@ -35,7 +38,8 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind } } -static void gpio_scene_start_var_list_change_callback(VariableItem* item) { +static void +gpio_scene_start_var_list_change_callback(VariableItem* item) { GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -117,3 +121,5 @@ void gpio_scene_start_on_exit(void* context) { GpioApp* app = context; variable_item_list_reset(app->var_item_list); } + +#endif // __GPIO_SCENE_START_H__ \ No newline at end of file diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 8ef037d6b..9f166e154 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -313,7 +313,7 @@ static void gap_init_svc(Gap* gap) { // Initialize GATT interface aci_gatt_init(); // Initialize GAP interface - // Skip fist symbol AD_TYPE_COMPLETE_LOCAL_NAME + // Skip first symbol AD_TYPE_COMPLETE_LOCAL_NAME char* name = gap->service.adv_name + 1; aci_gap_init( GAP_PERIPHERAL_ROLE, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 0857fe4ee..6dfbe5880 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -73,7 +73,7 @@ FuriHalBtProfileConfig profile_config[FuriHalBtProfileNumber] = { .supervisor_timeout = 0, }, }, - }, + } }; FuriHalBtProfileConfig* current_profile = NULL; diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index ab3855f42..5b5a90ed7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -139,9 +139,24 @@ void furi_hal_bt_hid_start() { hid_svc_start(); } // Configure HID Keyboard - kb_report = malloc(sizeof(FuriHalBtHidKbReport)); - mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); - consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); + // + // this will also be called by Bad-usb now, so we need to prevent memory leak + // I dont know for now, how apps and mains interacts together, so lets add some + // protection in case a crash in one doesn't affect the other + if(kb_report) + memset(kb_report, 0, sizeof(FuriHalBtHidKbReport)); + else + kb_report = malloc(sizeof(FuriHalBtHidKbReport)); + + if(mouse_report) + memset(mouse_report, 0, sizeof(FuriHalBtHidMouseReport)); + else + mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); + if(consumer_report) + memset(consumer_report, 0, sizeof(FuriHalBtHidConsumerReport)); + else + consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); + // Configure Report Map characteristic hid_svc_update_report_map( furi_hal_bt_hid_report_map_data, sizeof(furi_hal_bt_hid_report_map_data)); From b0c2cba2a05000c5fddbb977281d82fcfa104c7a Mon Sep 17 00:00:00 2001 From: yocvito Date: Thu, 5 Jan 2023 14:20:02 +0100 Subject: [PATCH 002/231] Adds basic bad BLE app (cannot modify adv name yet, cannot efficiently send keys with harcoded timeouts between press/release) --- applications/main/bad_ble/application.fam | 18 + applications/main/bad_ble/bad_ble_app.c | 161 ++++ applications/main/bad_ble/bad_ble_app.h | 11 + applications/main/bad_ble/bad_ble_app_i.h | 50 ++ applications/main/bad_ble/bad_ble_script.c | 783 ++++++++++++++++++ applications/main/bad_ble/bad_ble_script.h | 49 ++ .../main/bad_ble/bad_ble_settings_filename.h | 3 + applications/main/bad_ble/badusb_10px.png | Bin 0 -> 576 bytes .../bad_ble/images/ActiveConnection_50x64.png | Bin 0 -> 3842 bytes .../main/bad_ble/images/Clock_18x18.png | Bin 0 -> 1083 bytes .../main/bad_ble/images/Error_18x18.png | Bin 0 -> 1083 bytes .../main/bad_ble/images/EviSmile1_18x21.png | Bin 0 -> 3645 bytes .../main/bad_ble/images/EviSmile2_18x21.png | Bin 0 -> 3649 bytes .../main/bad_ble/images/EviWaiting1_18x21.png | Bin 0 -> 13020 bytes .../main/bad_ble/images/EviWaiting2_18x21.png | Bin 0 -> 12913 bytes .../main/bad_ble/images/Percent_10x14.png | Bin 0 -> 3624 bytes .../main/bad_ble/images/SDQuestion_35x43.png | Bin 0 -> 1950 bytes .../main/bad_ble/images/Smile_18x18.png | Bin 0 -> 1080 bytes .../main/bad_ble/images/UsbTree_48x22.png | Bin 0 -> 3653 bytes .../main/bad_ble/images/badusb_10px.png | Bin 0 -> 576 bytes .../main/bad_ble/images/keyboard_10px.png | Bin 0 -> 147 bytes .../main/bad_ble/scenes/bad_ble_scene.c | 30 + .../main/bad_ble/scenes/bad_ble_scene.h | 29 + .../bad_ble/scenes/bad_ble_scene_config.c | 52 ++ .../bad_ble/scenes/bad_ble_scene_config.h | 5 + .../scenes/bad_ble_scene_config_layout.c | 47 ++ .../main/bad_ble/scenes/bad_ble_scene_error.c | 83 ++ .../scenes/bad_ble_scene_file_select.c | 51 ++ .../main/bad_ble/scenes/bad_ble_scene_work.c | 54 ++ applications/main/bad_ble/switch_ble.py | 24 + .../main/bad_ble/views/bad_ble_view.c | 237 ++++++ .../main/bad_ble/views/bad_ble_view.h | 21 + 32 files changed, 1708 insertions(+) create mode 100644 applications/main/bad_ble/application.fam create mode 100644 applications/main/bad_ble/bad_ble_app.c create mode 100644 applications/main/bad_ble/bad_ble_app.h create mode 100644 applications/main/bad_ble/bad_ble_app_i.h create mode 100644 applications/main/bad_ble/bad_ble_script.c create mode 100644 applications/main/bad_ble/bad_ble_script.h create mode 100644 applications/main/bad_ble/bad_ble_settings_filename.h create mode 100644 applications/main/bad_ble/badusb_10px.png create mode 100644 applications/main/bad_ble/images/ActiveConnection_50x64.png create mode 100644 applications/main/bad_ble/images/Clock_18x18.png create mode 100644 applications/main/bad_ble/images/Error_18x18.png create mode 100644 applications/main/bad_ble/images/EviSmile1_18x21.png create mode 100644 applications/main/bad_ble/images/EviSmile2_18x21.png create mode 100644 applications/main/bad_ble/images/EviWaiting1_18x21.png create mode 100644 applications/main/bad_ble/images/EviWaiting2_18x21.png create mode 100644 applications/main/bad_ble/images/Percent_10x14.png create mode 100644 applications/main/bad_ble/images/SDQuestion_35x43.png create mode 100644 applications/main/bad_ble/images/Smile_18x18.png create mode 100644 applications/main/bad_ble/images/UsbTree_48x22.png create mode 100644 applications/main/bad_ble/images/badusb_10px.png create mode 100644 applications/main/bad_ble/images/keyboard_10px.png create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene.c create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene.h create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config.c create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config.h create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_error.c create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_file_select.c create mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_work.c create mode 100644 applications/main/bad_ble/switch_ble.py create mode 100644 applications/main/bad_ble/views/bad_ble_view.c create mode 100644 applications/main/bad_ble/views/bad_ble_view.h diff --git a/applications/main/bad_ble/application.fam b/applications/main/bad_ble/application.fam new file mode 100644 index 000000000..ac4106eac --- /dev/null +++ b/applications/main/bad_ble/application.fam @@ -0,0 +1,18 @@ +App( + appid="bad_ble", + name="Bad BLE", + apptype=FlipperAppType.EXTERNAL, + entry_point="bad_ble_app", + cdefines=["APP_BAD_BLE"], + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + # icon="A_BadUsb_14", + order=70, + fap_category="Main", + fap_icon="badusb_10px.png", + fap_icon_assets="images", + fap_libs=["assets"], +) diff --git a/applications/main/bad_ble/bad_ble_app.c b/applications/main/bad_ble/bad_ble_app.c new file mode 100644 index 000000000..47ea7b4d5 --- /dev/null +++ b/applications/main/bad_ble/bad_ble_app.c @@ -0,0 +1,161 @@ +#include "bad_ble_app_i.h" +#include "bad_ble_settings_filename.h" +#include +#include +#include +#include + +#include +#include + +#define BAD_BLE_SETTINGS_PATH BAD_BLE_APP_BASE_FOLDER "/" BAD_BLE_SETTINGS_FILE_NAME + +static bool bad_ble_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + BadBleApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool bad_ble_app_back_event_callback(void* context) { + furi_assert(context); + BadBleApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void bad_ble_app_tick_event_callback(void* context) { + furi_assert(context); + BadBleApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +static void bad_ble_load_settings(BadBleApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + furi_string_push_back(app->keyboard_layout, chr); + } + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + +static void bad_ble_save_settings(BadBleApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + storage_file_write( + settings_file, + furi_string_get_cstr(app->keyboard_layout), + furi_string_size(app->keyboard_layout)); + storage_file_write(settings_file, "\n", 1); + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + +BadBleApp* bad_ble_app_alloc(char* arg) { + BadBleApp* app = malloc(sizeof(BadBleApp)); + + app->bad_ble_script = NULL; + + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); + if(arg && strlen(arg)) { + furi_string_set(app->file_path, arg); + } + + bad_ble_load_settings(app); + + app->gui = furi_record_open(RECORD_GUI); + app->notifications = furi_record_open(RECORD_NOTIFICATION); + app->dialogs = furi_record_open(RECORD_DIALOGS); + + app->view_dispatcher = view_dispatcher_alloc(); + view_dispatcher_enable_queue(app->view_dispatcher); + + app->scene_manager = scene_manager_alloc(&bad_ble_scene_handlers, app); + + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, bad_ble_app_tick_event_callback, 500); + view_dispatcher_set_custom_event_callback( + app->view_dispatcher, bad_ble_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, bad_ble_app_back_event_callback); + + // Custom Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBleAppViewError, widget_get_view(app->widget)); + + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBleAppViewConfig, submenu_get_view(app->submenu)); + + app->bad_ble_view = bad_ble_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadBleAppViewWork, bad_ble_get_view(app->bad_ble_view)); + + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + + Bt* bt = furi_record_open(RECORD_BT); + app->bt = bt; + if(!furi_string_empty(app->file_path)) { + app->bad_ble_script = bad_ble_script_open(app->file_path, bt); + bad_ble_script_set_keyboard_layout(app->bad_ble_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadBleSceneWork); + } else { + furi_string_set(app->file_path, BAD_BLE_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadBleSceneFileSelect); + } + + return app; +} + +void bad_ble_app_free(BadBleApp* app) { + furi_assert(app); + + if(app->bad_ble_script) { + bad_ble_script_close(app->bad_ble_script); + app->bad_ble_script = NULL; + } + + // Views + view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewWork); + bad_ble_free(app->bad_ble_view); + + // Custom Widget + view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewError); + widget_free(app->widget); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfig); + submenu_free(app->submenu); + + // View dispatcher + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + + // Close records + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_BT); + + bad_ble_save_settings(app); + + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); + + free(app); +} + +int32_t bad_ble_app(void* p) { + BadBleApp* bad_ble_app = bad_ble_app_alloc((char*)p); + + view_dispatcher_run(bad_ble_app->view_dispatcher); + + bad_ble_app_free(bad_ble_app); + return 0; +} diff --git a/applications/main/bad_ble/bad_ble_app.h b/applications/main/bad_ble/bad_ble_app.h new file mode 100644 index 000000000..11954836e --- /dev/null +++ b/applications/main/bad_ble/bad_ble_app.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BadBleApp BadBleApp; + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_ble/bad_ble_app_i.h b/applications/main/bad_ble/bad_ble_app_i.h new file mode 100644 index 000000000..1fd172988 --- /dev/null +++ b/applications/main/bad_ble/bad_ble_app_i.h @@ -0,0 +1,50 @@ +#pragma once + +#include "bad_ble_app.h" +#include "scenes/bad_ble_scene.h" +#include "bad_ble_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/bad_ble_view.h" + +#define BAD_BLE_APP_BASE_FOLDER ANY_PATH("BadUsb") +#define BAD_BLE_APP_PATH_LAYOUT_FOLDER BAD_BLE_APP_BASE_FOLDER "/layouts" +#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt" +#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl" + +typedef enum { + BadBleAppErrorNoFiles, + BadBleAppErrorCloseRpc, +} BadBleAppError; + +struct BadBleApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + Submenu* submenu; + + BadBleAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadBle* bad_ble_view; + BadBleScript* bad_ble_script; + + Bt* bt; +}; + +typedef enum { + BadBleAppViewError, + BadBleAppViewWork, + BadBleAppViewConfig, +} BadBleAppView; \ No newline at end of file diff --git a/applications/main/bad_ble/bad_ble_script.c b/applications/main/bad_ble/bad_ble_script.c new file mode 100644 index 000000000..6489b54f7 --- /dev/null +++ b/applications/main/bad_ble/bad_ble_script.c @@ -0,0 +1,783 @@ +#include +#include +#include +#include +#include +#include +#include +#include "bad_ble_script.h" +#include + +#include + +#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") + +#define TAG "BadBle" +#define WORKER_TAG TAG "Worker" +#define FILE_BUFFER_LEN 16 + +#define SCRIPT_STATE_ERROR (-1) +#define SCRIPT_STATE_END (-2) +#define SCRIPT_STATE_NEXT_LINE (-3) + +#define BADBLE_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + +typedef enum { + WorkerEvtToggle = (1 << 0), + WorkerEvtEnd = (1 << 1), + WorkerEvtConnect = (1 << 2), + WorkerEvtDisconnect = (1 << 3), +} WorkerEvtFlags; + + +typedef enum { + LevelRssi122_100, + LevelRssi99_80, + LevelRssi79_60, + LevelRssi59_40, + LevelRssi39_0, + LevelRssiNum, +} LevelRssiDelays; + +const uint8_t bt_hid_delays[LevelRssiNum] = { + 75, // LevelRssi122_100 + 50, // LevelRssi99_80 + 35, // LevelRssi79_60 + 25, // LevelRssi59_40 + 10, // LevelRssi39_0 +}; + +struct BadBleScript { + BadBleState st; + FuriString* file_path; + uint32_t defdelay; + uint16_t layout[128]; + FuriThread* thread; + uint8_t file_buf[FILE_BUFFER_LEN + 1]; + uint8_t buf_start; + uint8_t buf_len; + bool file_end; + FuriString* line; + + FuriString* line_prev; + uint32_t repeat_cnt; + + File* debug_file; + Bt* bt; +}; + +typedef struct { + char* name; + uint16_t keycode; +} DuckyKey; + +static const DuckyKey ducky_keys[] = { + {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, + {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, + {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, + {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, + {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, + + {"CTRL", KEY_MOD_LEFT_CTRL}, + {"CONTROL", KEY_MOD_LEFT_CTRL}, + {"SHIFT", KEY_MOD_LEFT_SHIFT}, + {"ALT", KEY_MOD_LEFT_ALT}, + {"GUI", KEY_MOD_LEFT_GUI}, + {"WINDOWS", KEY_MOD_LEFT_GUI}, + + {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, + {"DOWN", HID_KEYBOARD_DOWN_ARROW}, + {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, + {"LEFT", HID_KEYBOARD_LEFT_ARROW}, + {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, + {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, + {"UPARROW", HID_KEYBOARD_UP_ARROW}, + {"UP", HID_KEYBOARD_UP_ARROW}, + + {"ENTER", HID_KEYBOARD_RETURN}, + {"BREAK", HID_KEYBOARD_PAUSE}, + {"PAUSE", HID_KEYBOARD_PAUSE}, + {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, + {"DELETE", HID_KEYBOARD_DELETE}, + {"BACKSPACE", HID_KEYPAD_BACKSPACE}, + {"END", HID_KEYBOARD_END}, + {"ESC", HID_KEYBOARD_ESCAPE}, + {"ESCAPE", HID_KEYBOARD_ESCAPE}, + {"HOME", HID_KEYBOARD_HOME}, + {"INSERT", HID_KEYBOARD_INSERT}, + {"NUMLOCK", HID_KEYPAD_NUMLOCK}, + {"PAGEUP", HID_KEYBOARD_PAGE_UP}, + {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, + {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, + {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, + {"SPACE", HID_KEYBOARD_SPACEBAR}, + {"TAB", HID_KEYBOARD_TAB}, + {"MENU", HID_KEYBOARD_APPLICATION}, + {"APP", HID_KEYBOARD_APPLICATION}, + + {"F1", HID_KEYBOARD_F1}, + {"F2", HID_KEYBOARD_F2}, + {"F3", HID_KEYBOARD_F3}, + {"F4", HID_KEYBOARD_F4}, + {"F5", HID_KEYBOARD_F5}, + {"F6", HID_KEYBOARD_F6}, + {"F7", HID_KEYBOARD_F7}, + {"F8", HID_KEYBOARD_F8}, + {"F9", HID_KEYBOARD_F9}, + {"F10", HID_KEYBOARD_F10}, + {"F11", HID_KEYBOARD_F11}, + {"F12", HID_KEYBOARD_F12}, +}; + +static const char ducky_cmd_comment[] = {"REM"}; +static const char ducky_cmd_id[] = {"ID"}; +static const char ducky_cmd_delay[] = {"DELAY "}; +static const char ducky_cmd_string[] = {"STRING "}; +static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; +static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; +static const char ducky_cmd_repeat[] = {"REPEAT "}; +static const char ducky_cmd_sysrq[] = {"SYSRQ "}; + +static const char ducky_cmd_altchar[] = {"ALTCHAR "}; +static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; +static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; + +static const char ducky_cmd_lang[] = {"DUCKY_LANG"}; + +static const uint8_t numpad_keys[10] = { + HID_KEYPAD_0, + HID_KEYPAD_1, + HID_KEYPAD_2, + HID_KEYPAD_3, + HID_KEYPAD_4, + HID_KEYPAD_5, + HID_KEYPAD_6, + HID_KEYPAD_7, + HID_KEYPAD_8, + HID_KEYPAD_9, +}; + +/** + * @brief Wait until there are enough free slots in the keyboard buffer + * + * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) +*/ +static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { + uint32_t start = furi_get_tick(); + uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; + while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { + furi_delay_ms(100); + + if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { + break; + } + } +} + +static bool ducky_get_number(const char* param, uint32_t* val) { + uint32_t value = 0; + if(sscanf(param, "%lu", &value) == 1) { + *val = value; + return true; + } + return false; +} + +static uint32_t ducky_get_command_len(const char* line) { + uint32_t len = strlen(line); + for(uint32_t i = 0; i < len; i++) { + if(line[i] == ' ') return i; + } + return 0; +} + +static bool ducky_is_line_end(const char chr) { + return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +} + +static void ducky_numlock_on() { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); + furi_delay_ms(25); + furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } +} + +static bool ducky_numpad_press(const char num) { + if((num < '0') || (num > '9')) return false; + + uint16_t key = numpad_keys[num - '0']; + bt_hid_hold_while_keyboard_buffer_full(1, -1); + FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); + + furi_hal_bt_hid_kb_press(key); + FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); + furi_delay_ms(25); + furi_hal_bt_hid_kb_release(key); + + return true; +} + +static bool ducky_altchar(const char* charcode) { + uint8_t i = 0; + bool state = false; + + FURI_LOG_I(WORKER_TAG, "char %s", charcode); + + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); + + while(!ducky_is_line_end(charcode[i])) { + state = ducky_numpad_press(charcode[i]); + if(state == false) break; + i++; + } + + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); + return state; +} + +static bool ducky_altstring(const char* param) { + uint32_t i = 0; + bool state = false; + + while(param[i] != '\0') { + if((param[i] < ' ') || (param[i] > '~')) { + i++; + continue; // Skip non-printable chars + } + + char temp_str[4]; + snprintf(temp_str, 4, "%u", param[i]); + + state = ducky_altchar(temp_str); + if(state == false) break; + i++; + } + return state; +} + +static bool ducky_string(BadBleScript* bad_ble, const char* param) { + uint32_t i = 0; + while(param[i] != '\0') { + uint16_t keycode = BADBLE_ASCII_TO_KEY(bad_ble, param[i]); + if(keycode != HID_KEYBOARD_NONE) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(keycode); + FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); + furi_delay_ms(25); + furi_hal_bt_hid_kb_release(keycode); + } + i++; + } + return true; +} + +static uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) { + for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { + size_t key_cmd_len = strlen(ducky_keys[i].name); + if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && + (ducky_is_line_end(param[key_cmd_len]))) { + return ducky_keys[i].keycode; + } + } + if((accept_chars) && (strlen(param) > 0)) { + return (BADBLE_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF); + } + return 0; +} + +static int32_t + ducky_parse_line(BadBleScript* bad_ble, FuriString* line, char* error, size_t error_len) { + uint32_t line_len = furi_string_size(line); + const char* line_tmp = furi_string_get_cstr(line); + bool state = false; + + if(line_len == 0) { + return SCRIPT_STATE_NEXT_LINE; // Skip empty lines + } + + FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); + + // General commands + if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { + // REM - comment line + return (0); + } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + // ID - executed in ducky_script_preload + return (0); + } else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) { + // DUCKY_LANG - ignore command to retain compatibility with existing scripts + return (0); + } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { + // DELAY + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + uint32_t delay_val = 0; + state = ducky_get_number(line_tmp, &delay_val); + if((state) && (delay_val > 0)) { + return (int32_t)delay_val; + } + if(error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return SCRIPT_STATE_ERROR; + } else if( + (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || + (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { + // DEFAULT_DELAY + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_get_number(line_tmp, &bad_ble->defdelay); + if(!state && error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { + // STRING + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_string(bad_ble, line_tmp); + if(!state && error != NULL) { + snprintf(error, error_len, "Invalid string %s", line_tmp); + } + return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { + // ALTCHAR + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + ducky_numlock_on(); + state = ducky_altchar(line_tmp); + if(!state && error != NULL) { + snprintf(error, error_len, "Invalid altchar %s", line_tmp); + } + return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if( + (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) || + (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { + // ALTSTRING + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + ducky_numlock_on(); + state = ducky_altstring(line_tmp); + if(!state && error != NULL) { + snprintf(error, error_len, "Invalid altstring %s", line_tmp); + } + return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { + // REPEAT + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + state = ducky_get_number(line_tmp, &bad_ble->repeat_cnt); + if(!state && error != NULL) { + snprintf(error, error_len, "Invalid number %s", line_tmp); + } + return (state) ? (0) : SCRIPT_STATE_ERROR; + } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { + // SYSRQ + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + uint16_t key = ducky_get_keycode(bad_ble, line_tmp, true); + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_bt_hid_kb_press(key); + FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); + furi_delay_ms(25); + furi_hal_bt_hid_kb_release(key); + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + return (0); + } else { + // Special keys + modifiers + uint16_t key = ducky_get_keycode(bad_ble, line_tmp, false); + if(key == HID_KEYBOARD_NONE) { + if(error != NULL) { + snprintf(error, error_len, "No keycode defined for %s", line_tmp); + } + return SCRIPT_STATE_ERROR; + } + if((key & 0xFF00) != 0) { + // It's a modifier key + line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; + key |= ducky_get_keycode(bad_ble, line_tmp, true); + } + FURI_LOG_I(WORKER_TAG, "Special key pressed %x\r\n", key); + furi_hal_bt_hid_kb_press(key); + FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); + furi_delay_ms(25); + furi_hal_bt_hid_kb_release(key); + return (0); + } +} + +static bool ducky_script_preload(BadBleScript* bad_ble, File* script_file) { + uint8_t ret = 0; + uint32_t line_len = 0; + + furi_string_reset(bad_ble->line); + + do { + ret = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN); + for(uint16_t i = 0; i < ret; i++) { + if(bad_ble->file_buf[i] == '\n' && line_len > 0) { + bad_ble->st.line_nb++; + line_len = 0; + } else { + if(bad_ble->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]); + } + line_len++; + } + } + if(storage_file_eof(script_file)) { + if(line_len > 0) { + bad_ble->st.line_nb++; + break; + } + } + } while(ret > 0); + + storage_file_seek(script_file, 0, true); + furi_string_reset(bad_ble->line); + + return true; +} + +static int32_t ducky_script_execute_next(BadBleScript* bad_ble, File* script_file) { + int32_t delay_val = 0; + + if(bad_ble->repeat_cnt > 0) { + bad_ble->repeat_cnt--; + delay_val = ducky_parse_line( + bad_ble, bad_ble->line_prev, bad_ble->st.error, sizeof(bad_ble->st.error)); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val < 0) { // Script error + bad_ble->st.error_line = bad_ble->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur - 1U); + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_ble->defdelay); + } + } + + furi_string_set(bad_ble->line_prev, bad_ble->line); + furi_string_reset(bad_ble->line); + + while(1) { + if(bad_ble->buf_len == 0) { + bad_ble->buf_len = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN); + if(storage_file_eof(script_file)) { + if((bad_ble->buf_len < FILE_BUFFER_LEN) && (bad_ble->file_end == false)) { + bad_ble->file_buf[bad_ble->buf_len] = '\n'; + bad_ble->buf_len++; + bad_ble->file_end = true; + } + } + + bad_ble->buf_start = 0; + if(bad_ble->buf_len == 0) return SCRIPT_STATE_END; + } + for(uint8_t i = bad_ble->buf_start; i < (bad_ble->buf_start + bad_ble->buf_len); i++) { + if(bad_ble->file_buf[i] == '\n' && furi_string_size(bad_ble->line) > 0) { + bad_ble->st.line_cur++; + bad_ble->buf_len = bad_ble->buf_len + bad_ble->buf_start - (i + 1); + bad_ble->buf_start = i + 1; + furi_string_trim(bad_ble->line); + delay_val = ducky_parse_line( + bad_ble, bad_ble->line, bad_ble->st.error, sizeof(bad_ble->st.error)); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line + return 0; + } else if(delay_val < 0) { + bad_ble->st.error_line = bad_ble->st.line_cur; + if(delay_val == SCRIPT_STATE_NEXT_LINE) { + snprintf( + bad_ble->st.error, sizeof(bad_ble->st.error), "Forbidden empty line"); + FURI_LOG_E( + WORKER_TAG, "Forbidden empty line at line %u", bad_ble->st.line_cur); + } else { + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur); + } + return SCRIPT_STATE_ERROR; + } else { + return (delay_val + bad_ble->defdelay); + } + } else { + furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]); + } + } + bad_ble->buf_len = 0; + if(bad_ble->file_end) return SCRIPT_STATE_END; + } + + return 0; +} + +static void bad_ble_hid_state_callback(BtStatus status, void* context) { + furi_assert(context); + BadBleScript* bad_ble = (BadBleScript*)context; + bool state = (status == BtStatusConnected); + + if(state == true) + furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect); + else + furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtDisconnect); +} + +static int32_t bad_ble_worker(void* context) { + BadBleScript* bad_ble = context; + + BadBleWorkerState worker_state = BadBleStateInit; + int32_t delay_val = 0; + + // init ble hid + bt_disconnect(bad_ble->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH); + + //bt_set_adv_name(bad_ble->bt, "Keyboard K99"); + + furi_hal_bt_modify_profile_adv_name("Keyboard K99", BtProfileHidKeyboard); + + if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(TAG, "Failed to switch to HID profile"); + return -1; + } + + furi_hal_bt_start_advertising(); + + FURI_LOG_I(WORKER_TAG, "Init"); + File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + bad_ble->line = furi_string_alloc(); + bad_ble->line_prev = furi_string_alloc(); + + bt_set_status_changed_callback(bad_ble->bt, bad_ble_hid_state_callback, bad_ble); + while(1) { + if(worker_state == BadBleStateInit) { // State: initialization + if(storage_file_open( + script_file, + furi_string_get_cstr(bad_ble->file_path), + FSAM_READ, + FSOM_OPEN_EXISTING)) { + if((ducky_script_preload(bad_ble, script_file)) && (bad_ble->st.line_nb > 0)) { + worker_state = BadBleStateNotConnected; // Ready to run + } else { + worker_state = BadBleStateScriptError; // Script preload error + } + } else { + FURI_LOG_E(WORKER_TAG, "File open error"); + worker_state = BadBleStateFileError; // File open error + } + bad_ble->st.state = worker_state; + + } else if(worker_state == BadBleStateNotConnected) { // State: ble not connected + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadBleStateIdle; // Ready to run + } else if(flags & WorkerEvtToggle) { + worker_state = BadBleStateWillRun; // Will run when ble is connected + } + bad_ble->st.state = worker_state; + + } else if(worker_state == BadBleStateIdle) { // State: ready to start + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { // Start executing script + DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_ble->buf_len = 0; + bad_ble->st.line_cur = 0; + bad_ble->defdelay = 0; + bad_ble->repeat_cnt = 0; + bad_ble->file_end = false; + storage_file_seek(script_file, 0, true); + worker_state = BadBleStateRunning; + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBleStateNotConnected; // ble disconnected + } + bad_ble->st.state = worker_state; + + } else if(worker_state == BadBleStateWillRun) { // State: start on connection + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { // Start executing script + DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + delay_val = 0; + bad_ble->buf_len = 0; + bad_ble->st.line_cur = 0; + bad_ble->defdelay = 0; + bad_ble->repeat_cnt = 0; + bad_ble->file_end = false; + storage_file_seek(script_file, 0, true); + // extra time for PC to recognize Flipper as keyboard + furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + worker_state = BadBleStateRunning; + } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution + worker_state = BadBleStateNotConnected; + } + bad_ble->st.state = worker_state; + + } else if(worker_state == BadBleStateRunning) { // State: running + uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); + delay_val -= delay_cur; + if(!(flags & FuriFlagError)) { + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtToggle) { + worker_state = BadBleStateIdle; // Stop executing script + furi_hal_bt_hid_kb_release_all(); + } else if(flags & WorkerEvtDisconnect) { + worker_state = BadBleStateNotConnected; // ble disconnected + furi_hal_bt_hid_kb_release_all(); + } + bad_ble->st.state = worker_state; + continue; + } else if( + (flags == (unsigned)FuriFlagErrorTimeout) || + (flags == (unsigned)FuriFlagErrorResource)) { + if(delay_val > 0) { + bad_ble->st.delay_remain--; + continue; + } + bad_ble->st.state = BadBleStateRunning; + delay_val = ducky_script_execute_next(bad_ble, script_file); + if(delay_val == SCRIPT_STATE_ERROR) { // Script error + delay_val = 0; + worker_state = BadBleStateScriptError; + bad_ble->st.state = worker_state; + } else if(delay_val == SCRIPT_STATE_END) { // End of script + delay_val = 0; + worker_state = BadBleStateIdle; + bad_ble->st.state = BadBleStateDone; + furi_hal_bt_hid_kb_release_all(); + continue; + } else if(delay_val > 1000) { + bad_ble->st.state = BadBleStateDelay; // Show long delays + bad_ble->st.delay_remain = delay_val / 1000; + } + } else { + furi_check((flags & FuriFlagError) == 0); + } + + } else if( + (worker_state == BadBleStateFileError) || + (worker_state == BadBleStateScriptError)) { // State: error + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } + } + } + + // release all keys + bt_hid_hold_while_keyboard_buffer_full(6, 3000); + + // stop ble + bt_set_status_changed_callback(bad_ble->bt, NULL, NULL); + + bt_disconnect(bad_ble->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(bad_ble->bt); + + if(!bt_set_profile(bad_ble->bt, BtProfileSerial)) { + FURI_LOG_E(TAG, "Failed to switch to Serial profile"); + } + + storage_file_close(script_file); + storage_file_free(script_file); + furi_string_free(bad_ble->line); + furi_string_free(bad_ble->line_prev); + + FURI_LOG_I(WORKER_TAG, "End"); + + return 0; +} + +static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) { + furi_assert(bad_ble); + memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout)); + memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout))); +} + +BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt) { + furi_assert(file_path); + + BadBleScript* bad_ble = malloc(sizeof(BadBleScript)); + bad_ble->file_path = furi_string_alloc(); + furi_string_set(bad_ble->file_path, file_path); + bad_ble_script_set_default_keyboard_layout(bad_ble); + + bad_ble->st.state = BadBleStateInit; + bad_ble->st.error[0] = '\0'; + + bad_ble->bt = bt; + + bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble); + furi_thread_start(bad_ble->thread); + return bad_ble; +} //-V773 + +void bad_ble_script_close(BadBleScript* bad_ble) { + furi_assert(bad_ble); + furi_record_close(RECORD_STORAGE); + furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd); + furi_thread_join(bad_ble->thread); + furi_thread_free(bad_ble->thread); + furi_string_free(bad_ble->file_path); + free(bad_ble); +} + +void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) { + furi_assert(bad_ble); + + if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_ble->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_ble_script_set_default_keyboard_layout(bad_ble); + } + storage_file_free(layout_file); +} + +void bad_ble_script_toggle(BadBleScript* bad_ble) { + furi_assert(bad_ble); + furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtToggle); +} + +BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) { + furi_assert(bad_ble); + return &(bad_ble->st); +} diff --git a/applications/main/bad_ble/bad_ble_script.h b/applications/main/bad_ble/bad_ble_script.h new file mode 100644 index 000000000..f1553e1db --- /dev/null +++ b/applications/main/bad_ble/bad_ble_script.h @@ -0,0 +1,49 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct BadBleScript BadBleScript; + +typedef enum { + BadBleStateInit, + BadBleStateNotConnected, + BadBleStateIdle, + BadBleStateWillRun, + BadBleStateRunning, + BadBleStateDelay, + BadBleStateDone, + BadBleStateScriptError, + BadBleStateFileError, +} BadBleWorkerState; + +typedef struct { + BadBleWorkerState state; + uint16_t line_cur; + uint16_t line_nb; + uint32_t delay_remain; + uint16_t error_line; + char error[64]; +} BadBleState; + +BadBleScript* bad_ble_script_open(FuriString* file_path, Bt *bt); + +void bad_ble_script_close(BadBleScript* bad_ble); + +void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path); + +void bad_ble_script_start(BadBleScript* bad_ble); + +void bad_ble_script_stop(BadBleScript* bad_ble); + +void bad_ble_script_toggle(BadBleScript* bad_ble); + +BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_ble/bad_ble_settings_filename.h b/applications/main/bad_ble/bad_ble_settings_filename.h new file mode 100644 index 000000000..73245ad5f --- /dev/null +++ b/applications/main/bad_ble/bad_ble_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_BLE_SETTINGS_FILE_NAME ".BadBle.settings" diff --git a/applications/main/bad_ble/badusb_10px.png b/applications/main/bad_ble/badusb_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..037474aa3bc9c2e1aca79a68483e69980432bcf5 GIT binary patch literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000&vafZq~f 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 literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/Error_18x18.png b/applications/main/bad_ble/images/Error_18x18.png new file mode 100644 index 0000000000000000000000000000000000000000..16a5a74d96686c9ff2d9d96984a285f3885cffbe GIT binary patch literal 1083 zcmbVLO=#0l91nDeA53H@GKcca5EPcrrb`o6$JVsAu+G{QR&T!My{=(RUY5MA>A)Sl zdD4@f#M8iCym|7VCs7c=gNnj#9z6JU@F>)meoPNz2Ls9f|6cyT-~an|dGX5V(KAOm zjvFl&tO}E3@q0MIzVO5rW@4P?YIKP-Xd4EYn?t0ILD7XPxPl?-ti8fB9GBQ|sx?|G zEtocOMHt(Nk?S)w$IZ+}KD1Xc1$DgQcp3i3(`P(zP=;SlmE@A2#Z9NM8Q`VO#j3rz zY8!~3y$og|lM%R>LJ+wvFEpbJ-{Uoz9$!m5=$X*f4Bro`Rw{!m2{6z_MX+UA2D%|4 zSci7KJ_S@+RU}!H6itw2GijKb1_lq$+y$s%R;>KM89Qb8CZ)b9N$qx9Y$rt$tVoJs z7?P|?swyxGA?$b*MuHbk4jC*Q+JWO!hj<`ngmtn`Gdv5mpM&d{N_)g!IH(k>nG``^ zQbbvD-8iwHbx14tZy5Vpht-acr3wzodSJ7LG$w~&R=k59#fB^z^J?I*uE3T>>~$A= zv}k2`_D4hxGLuL*QZ`HpN(v?gZCb}d+E%e($Qrg470Wh8L!SNcdRo!)nwHX%a#B%p z*|~I9OY7;JrO#Vx(vXMPq8C!=*?8#NVZH}g?Le%V4KSo6s1ni|jzPIeC<&Xy2dXNj zz{L`@9WTDQ6nCkgw1op_1EYLET+l1C>Fg7Np-(rEjMD;|PN}R0nkLjCM1rR3EG3vi zX~a_KYA3hc1AOxR-^6tGo!e{*7ot=XaSLN&)^x7*$R z_;8nL#iBJ=jXt&Rz8&Mh$jFComtIimxkqQi literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/EviSmile1_18x21.png b/applications/main/bad_ble/images/EviSmile1_18x21.png new file mode 100644 index 0000000000000000000000000000000000000000..987af32587ca7fbada8810abd0cabf788c9c04f9 GIT binary patch literal 3645 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 V0A_Pz|1^geur#waEi!h!{2vr@XiES9 literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/EviSmile2_18x21.png b/applications/main/bad_ble/images/EviSmile2_18x21.png new file mode 100644 index 0000000000000000000000000000000000000000..7e28c9f018a0f2cb572e5f5ce1db2e5c71511dd9 GIT binary patch literal 3649 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 NjisGMnc1oH{{vTjXlVcd literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/EviWaiting1_18x21.png b/applications/main/bad_ble/images/EviWaiting1_18x21.png new file mode 100644 index 0000000000000000000000000000000000000000..d39d2173329d5317fc2cdfb18738922a5eeec6fe GIT binary patch literal 13020 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%0DogpcYr`$ z++qJ}>3@a%wq1X@`yto-TOabSio8kl{&AQ7JzRfg^#9`P_cZuloB>JwpGp2Leg7lZ zf8_eN6!^Em|B0^u$n|e2@Na?t6J7t`SN77L~l0$9an+)c;9a+DZRZZS8{^eP~v1|B4dJsqoRU?V`GD5l`ss3 nQ0V!FVYmG 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 literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/SDQuestion_35x43.png b/applications/main/bad_ble/images/SDQuestion_35x43.png new file mode 100644 index 0000000000000000000000000000000000000000..9b9c9a58e3257f926677533f8cc99ffb19dd74f5 GIT binary patch literal 1950 zcmcIlTWs7!6g5TAQV0(rqPBcsIS921UeDNG@7i=zCA)d7sMC-vG>8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/images/Smile_18x18.png b/applications/main/bad_ble/images/Smile_18x18.png new file mode 100644 index 0000000000000000000000000000000000000000..d2aae0dc37f4fd3453e3254e4a9cd33e753019b9 GIT binary patch literal 1080 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 O0000gN=z( mnGc5;aHhO3XW%q4U|?wcz_Hx?k)a$=ErX}4pUXO@geCyYPARJZ literal 0 HcmV?d00001 diff --git a/applications/main/bad_ble/scenes/bad_ble_scene.c b/applications/main/bad_ble/scenes/bad_ble_scene.c new file mode 100644 index 000000000..351bb1e79 --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene.c @@ -0,0 +1,30 @@ +#include "bad_ble_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const bad_ble_scene_on_enter_handlers[])(void*) = { +#include "bad_ble_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 bad_ble_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "bad_ble_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 bad_ble_scene_on_exit_handlers[])(void* context) = { +#include "bad_ble_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers bad_ble_scene_handlers = { + .on_enter_handlers = bad_ble_scene_on_enter_handlers, + .on_event_handlers = bad_ble_scene_on_event_handlers, + .on_exit_handlers = bad_ble_scene_on_exit_handlers, + .scene_num = BadBleSceneNum, +}; diff --git a/applications/main/bad_ble/scenes/bad_ble_scene.h b/applications/main/bad_ble/scenes/bad_ble_scene.h new file mode 100644 index 000000000..25b19fc4b --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) BadBleScene##id, +typedef enum { +#include "bad_ble_scene_config.h" + BadBleSceneNum, +} BadBleScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers bad_ble_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "bad_ble_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "bad_ble_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "bad_ble_scene_config.h" +#undef ADD_SCENE diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config.c b/applications/main/bad_ble/scenes/bad_ble_scene_config.c new file mode 100644 index 000000000..d1bbac5ee --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config.c @@ -0,0 +1,52 @@ +#include "../bad_ble_app_i.h" +#include "furi_hal_power.h" + +enum SubmenuIndex { + SubmenuIndexKeyboardLayout, +}; + +void bad_ble_scene_config_submenu_callback(void* context, uint32_t index) { + BadBleApp* bad_ble = context; + view_dispatcher_send_custom_event(bad_ble->view_dispatcher, index); +} + +void bad_ble_scene_config_on_enter(void* context) { + BadBleApp* bad_ble = context; + Submenu* submenu = bad_ble->submenu; + + submenu_add_item( + submenu, + "Keyboard layout", + SubmenuIndexKeyboardLayout, + bad_ble_scene_config_submenu_callback, + bad_ble); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(bad_ble->scene_manager, BadBleSceneConfig)); + + view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfig); +} + +bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) { + BadBleApp* bad_ble = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_ble->scene_manager, BadBleSceneConfig, event.event); + consumed = true; + if(event.event == SubmenuIndexKeyboardLayout) { + scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout); + } else { + furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_ble_scene_config_on_exit(void* context) { + BadBleApp* bad_ble = context; + Submenu* submenu = bad_ble->submenu; + + submenu_reset(submenu); +} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config.h b/applications/main/bad_ble/scenes/bad_ble_scene_config.h new file mode 100644 index 000000000..addd8d9fa --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config.h @@ -0,0 +1,5 @@ +ADD_SCENE(bad_ble, file_select, FileSelect) +ADD_SCENE(bad_ble, work, Work) +ADD_SCENE(bad_ble, error, Error) +ADD_SCENE(bad_ble, config, Config) +ADD_SCENE(bad_ble, config_layout, ConfigLayout) diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c new file mode 100644 index 000000000..aabb1b900 --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c @@ -0,0 +1,47 @@ +#include "../bad_ble_app_i.h" +#include "furi_hal_power.h" +#include + +static bool bad_ble_layout_select(BadBleApp* bad_ble) { + furi_assert(bad_ble); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_ble->keyboard_layout)) { + furi_string_set(predefined_path, bad_ble->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_BLE_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_BLE_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_ble->dialogs, bad_ble->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_ble_scene_config_layout_on_enter(void* context) { + BadBleApp* bad_ble = context; + + if(bad_ble_layout_select(bad_ble)) { + bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout); + } + scene_manager_previous_scene(bad_ble->scene_manager); +} + +bool bad_ble_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadBleApp* bad_ble = context; + return false; +} + +void bad_ble_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadBleApp* bad_ble = context; +} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_error.c b/applications/main/bad_ble/scenes/bad_ble_scene_error.c new file mode 100644 index 000000000..fb8524eb1 --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_error.c @@ -0,0 +1,83 @@ +#include "../bad_ble_app_i.h" +#include "../../../settings/desktop_settings/desktop_settings_app.h" + +typedef enum { + BadBleCustomEventErrorBack, +} BadBleCustomEvent; + +static void + bad_ble_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + BadBleApp* app = context; + + if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { + view_dispatcher_send_custom_event(app->view_dispatcher, BadBleCustomEventErrorBack); + } +} + +void bad_ble_scene_error_on_enter(void* context) { + BadBleApp* app = context; + DesktopSettings* settings = malloc(sizeof(DesktopSettings)); + DESKTOP_SETTINGS_LOAD(settings); + + if(app->error == BadBleAppErrorNoFiles) { + widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); + widget_add_string_multiline_element( + app->widget, + 81, + 4, + AlignCenter, + AlignTop, + FontSecondary, + "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", bad_ble_scene_error_event_callback, app); + } else if(app->error == BadBleAppErrorCloseRpc) { + widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); + if (settings->sfw_mode) { + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Disconnect from\nPC or phone to\nuse this function."); + } + else { + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Pull out from\nPC or phone to\nuse me like this."); + } + } + + view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewError); + free(settings); +} + +bool bad_ble_scene_error_on_event(void* context, SceneManagerEvent event) { + BadBleApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadBleCustomEventErrorBack) { + view_dispatcher_stop(app->view_dispatcher); + consumed = true; + } + } + return consumed; +} + +void bad_ble_scene_error_on_exit(void* context) { + BadBleApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c b/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c new file mode 100644 index 000000000..d60f6a246 --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c @@ -0,0 +1,51 @@ +#include "../bad_ble_app_i.h" +#include "furi_hal_power.h" +#include + +static bool bad_ble_file_select(BadBleApp* bad_ble) { + furi_assert(bad_ble); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_BLE_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_BLE_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_ble->dialogs, bad_ble->file_path, bad_ble->file_path, &browser_options); + + return res; +} + +void bad_ble_scene_file_select_on_enter(void* context) { + BadBleApp* bad_ble = context; + + // furi_hal_ble_disable(); + if(bad_ble->bad_ble_script) { + bad_ble_script_close(bad_ble->bad_ble_script); + bad_ble->bad_ble_script = NULL; + } + + if(bad_ble_file_select(bad_ble)) { + bad_ble->bad_ble_script = bad_ble_script_open(bad_ble->file_path, bad_ble->bt); + bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout); + + scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneWork); + } else { + // furi_hal_ble_enable(); + view_dispatcher_stop(bad_ble->view_dispatcher); + } +} + +bool bad_ble_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadBleApp* bad_ble = context; + return false; +} + +void bad_ble_scene_file_select_on_exit(void* context) { + UNUSED(context); + // BadBleApp* bad_ble = context; +} \ No newline at end of file diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_work.c b/applications/main/bad_ble/scenes/bad_ble_scene_work.c new file mode 100644 index 000000000..a2c009fc3 --- /dev/null +++ b/applications/main/bad_ble/scenes/bad_ble_scene_work.c @@ -0,0 +1,54 @@ +#include "../bad_ble_script.h" +#include "../bad_ble_app_i.h" +#include "../views/bad_ble_view.h" +#include "furi_hal.h" +#include "toolbox/path.h" + +void bad_ble_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadBleApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_ble_scene_work_on_event(void* context, SceneManagerEvent event) { + BadBleApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + scene_manager_next_scene(app->scene_manager, BadBleSceneConfig); + consumed = true; + } else if(event.event == InputKeyOk) { + bad_ble_script_toggle(app->bad_ble_script); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script)); + } + return consumed; +} + +void bad_ble_scene_work_on_enter(void* context) { + BadBleApp* app = context; + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_ble_set_file_name(app->bad_ble_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_ble_set_layout(app->bad_ble_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script)); + + bad_ble_set_button_callback(app->bad_ble_view, bad_ble_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewWork); +} + +void bad_ble_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_ble/switch_ble.py b/applications/main/bad_ble/switch_ble.py new file mode 100644 index 000000000..32ed65492 --- /dev/null +++ b/applications/main/bad_ble/switch_ble.py @@ -0,0 +1,24 @@ +import os +import re + +def analyze_and_replace(directory): + # Recursively search for .c and .h files in the given directory + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith(".c") or file.endswith(".h"): + # Read the contents of the file + with open(os.path.join(root, file), "r") as f: + contents = f.read() + + # Replace all occurrences of "BadUsb" and "bad_usb" with "BadBle" and "bad_ble" + contents = contents.replace("usb", "ble") + contents = contents.replace("USB", "BLE") + contents = contents.replace("Usb", "Ble") + + + # Write the modified contents back to the file + with open(os.path.join(root, file), "w") as f: + f.write(contents) + +# Test the function with a sample directory +analyze_and_replace(".") diff --git a/applications/main/bad_ble/views/bad_ble_view.c b/applications/main/bad_ble/views/bad_ble_view.c new file mode 100644 index 000000000..fa5c099a5 --- /dev/null +++ b/applications/main/bad_ble/views/bad_ble_view.c @@ -0,0 +1,237 @@ +#include "bad_ble_view.h" +#include "../bad_ble_script.h" +#include +#include +#include +#include "../../../settings/desktop_settings/desktop_settings_app.h" + +#define MAX_NAME_LEN 64 + +struct BadBle { + View* view; + BadBleButtonCallback callback; + void* context; +}; + +typedef struct { + char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; + BadBleState state; + uint8_t anim_frame; +} BadBleModel; + +static void bad_ble_draw_callback(Canvas* canvas, void* _model) { + BadBleModel* model = _model; + + FuriString* disp_str; + disp_str = furi_string_alloc_set(model->file_name); + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + DesktopSettings* settings = malloc(sizeof(DesktopSettings)); + DESKTOP_SETTINGS_LOAD(settings); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_reset(disp_str); + furi_string_push_back(disp_str, '('); + for(size_t i = 0; i < strlen(model->layout); i++) + furi_string_push_back(disp_str, model->layout[i]); + furi_string_push_back(disp_str, ')'); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + + furi_string_reset(disp_str); + + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); + + if((model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone) || + (model->state.state == BadBleStateNotConnected)) { + if (settings->sfw_mode) { + elements_button_center(canvas, "Start"); + } + else { + elements_button_center(canvas, "Cum"); + } + } else if((model->state.state == BadBleStateRunning) || (model->state.state == BadBleStateDelay)) { + elements_button_center(canvas, "Stop"); + } else if(model->state.state == BadBleStateWillRun) { + elements_button_center(canvas, "Cancel"); + } + + if((model->state.state == BadBleStateNotConnected) || + (model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone)) { + elements_button_left(canvas, "Config"); + } + + if(model->state.state == BadBleStateNotConnected) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + if (settings->sfw_mode) { + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect me"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to a computer"); + } + else { + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); + } + } else if(model->state.state == BadBleStateWillRun) { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + canvas_set_font(canvas, FontPrimary); + if (settings->sfw_mode) { + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + } + else { + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); + } + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); + } else if(model->state.state == BadBleStateFileError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); + } else if(model->state.state == BadBleStateScriptError) { + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "line %u", model->state.error_line); + canvas_draw_str_aligned( + canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); + } else if(model->state.state == BadBleStateIdle) { + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBleStateRunning) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBleStateDone) { + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); + canvas_set_font(canvas, FontBigNumbers); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + } else if(model->state.state == BadBleStateDelay) { + if(model->anim_frame == 0) { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); + } else { + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); + } + canvas_set_font(canvas, FontBigNumbers); + furi_string_printf( + disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); + canvas_draw_str_aligned( + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); + canvas_set_font(canvas, FontSecondary); + furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); + canvas_draw_str_aligned( + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); + } else { + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); + } + + furi_string_free(disp_str); + free(settings); +} + +static bool bad_ble_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BadBle* bad_ble = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { + consumed = true; + furi_assert(bad_ble->callback); + bad_ble->callback(event->key, bad_ble->context); + } + } + + return consumed; +} + +BadBle* bad_ble_alloc() { + BadBle* bad_ble = malloc(sizeof(BadBle)); + + bad_ble->view = view_alloc(); + view_allocate_model(bad_ble->view, ViewModelTypeLocking, sizeof(BadBleModel)); + view_set_context(bad_ble->view, bad_ble); + view_set_draw_callback(bad_ble->view, bad_ble_draw_callback); + view_set_input_callback(bad_ble->view, bad_ble_input_callback); + + return bad_ble; +} + +void bad_ble_free(BadBle* bad_ble) { + furi_assert(bad_ble); + view_free(bad_ble->view); + free(bad_ble); +} + +View* bad_ble_get_view(BadBle* bad_ble) { + furi_assert(bad_ble); + return bad_ble->view; +} + +void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context) { + furi_assert(bad_ble); + furi_assert(callback); + with_view_model( + bad_ble->view, + BadBleModel * model, + { + UNUSED(model); + bad_ble->callback = callback; + bad_ble->context = context; + }, + true); +} + +void bad_ble_set_file_name(BadBle* bad_ble, const char* name) { + furi_assert(name); + with_view_model( + bad_ble->view, + BadBleModel * model, + { strlcpy(model->file_name, name, MAX_NAME_LEN); }, + true); +} + +void bad_ble_set_layout(BadBle* bad_ble, const char* layout) { + furi_assert(layout); + with_view_model( + bad_ble->view, + BadBleModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); +} + +void bad_ble_set_state(BadBle* bad_ble, BadBleState* st) { + furi_assert(st); + with_view_model( + bad_ble->view, + BadBleModel * model, + { + memcpy(&(model->state), st, sizeof(BadBleState)); + model->anim_frame ^= 1; + }, + true); +} diff --git a/applications/main/bad_ble/views/bad_ble_view.h b/applications/main/bad_ble/views/bad_ble_view.h new file mode 100644 index 000000000..ccbd1d97b --- /dev/null +++ b/applications/main/bad_ble/views/bad_ble_view.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "../bad_ble_script.h" + +typedef struct BadBle BadBle; +typedef void (*BadBleButtonCallback)(InputKey key, void* context); + +BadBle* bad_ble_alloc(); + +void bad_ble_free(BadBle* bad_ble); + +View* bad_ble_get_view(BadBle* bad_ble); + +void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context); + +void bad_ble_set_file_name(BadBle* bad_ble, const char* name); + +void bad_ble_set_layout(BadBle* bad_ble, const char* layout); + +void bad_ble_set_state(BadBle* bad_ble, BadBleState* st); From ce3305193f5d34d425507966f2205606ffb73d2a Mon Sep 17 00:00:00 2001 From: yocvito Date: Thu, 5 Jan 2023 20:32:46 +0100 Subject: [PATCH 003/231] Adds hal modifications --- firmware/targets/f7/api_symbols.csv | 6 ++-- firmware/targets/f7/furi_hal/furi_hal_bt.c | 14 ++++++-- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 35 +++++++++---------- .../targets/furi_hal_include/furi_hal_bt.h | 2 ++ .../furi_hal_include/furi_hal_bt_hid.h | 9 +++++ 5 files changed, 44 insertions(+), 22 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3276f1950..b9ffdcb0d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.3,, +Version,v,12.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -572,7 +572,7 @@ Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" -Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,?,bt_set_profile,_Bool,"Bt*, BtProfile" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* Function,+,buffered_file_stream_close,_Bool,Stream* @@ -1000,6 +1000,7 @@ Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,?,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release_all,_Bool, @@ -1016,6 +1017,7 @@ Function,+,furi_hal_bt_is_alive,_Bool, Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, Function,+,furi_hal_bt_is_testing_supported,_Bool, Function,+,furi_hal_bt_lock_core2,void, +Function,?,furi_hal_bt_modify_profile_adv_name,void,"const char*, FuriHalBtProfile" Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, Function,+,furi_hal_bt_nvm_sram_sem_release,void, Function,+,furi_hal_bt_reinit,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 6dfbe5880..c5ceedba2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -219,8 +219,10 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, // Change MAC address for HID profile config->mac_address[2]++; // Change name Flipper -> Control - const char* clicker_str = "Control"; - memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); + if (strlen(&config->adv_name[1]) > 1) { + const char* clicker_str = "Control"; + memcpy(&config->adv_name[1], clicker_str, strlen(clicker_str)); + } } if(!gap_init(config, event_cb, context)) { gap_thread_stop(); @@ -444,3 +446,11 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { FURI_LOG_E(TAG, "Failed to switch C2 mode: %d", fw_start_res); return false; } + +void furi_hal_bt_modify_profile_adv_name(const char* name, FuriHalBtProfile profile) { + furi_assert(name); + furi_assert(strlen(name) < FURI_HAL_VERSION_DEVICE_NAME_LENGTH); + furi_assert(profile < FuriHalBtProfileNumber); + + strncpy(profile_config[profile].config.adv_name, name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH); +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 5b5a90ed7..501704420 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -138,24 +138,10 @@ void furi_hal_bt_hid_start() { if(!hid_svc_is_started()) { hid_svc_start(); } - // Configure HID Keyboard - // - // this will also be called by Bad-usb now, so we need to prevent memory leak - // I dont know for now, how apps and mains interacts together, so lets add some - // protection in case a crash in one doesn't affect the other - if(kb_report) - memset(kb_report, 0, sizeof(FuriHalBtHidKbReport)); - else - kb_report = malloc(sizeof(FuriHalBtHidKbReport)); - if(mouse_report) - memset(mouse_report, 0, sizeof(FuriHalBtHidMouseReport)); - else - mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); - if(consumer_report) - memset(consumer_report, 0, sizeof(FuriHalBtHidConsumerReport)); - else - consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); + kb_report = malloc(sizeof(FuriHalBtHidKbReport)); + mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); + consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); // Configure Report Map characteristic hid_svc_update_report_map( @@ -195,17 +181,30 @@ void furi_hal_bt_hid_stop() { bool furi_hal_bt_hid_kb_press(uint16_t button) { furi_assert(kb_report); - for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { + uint8_t i; + for(i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { if(kb_report->key[i] == 0) { kb_report->key[i] = button & 0xFF; break; } } + if(i == FURI_HAL_BT_HID_KB_MAX_KEYS) { + return false; + } kb_report->mods |= (button >> 8); return hid_svc_update_input_report( ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); } +bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) { + furi_assert(kb_report); + for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { + if(kb_report->key[i] == 0) + n_empty_slots--; + } + return (n_empty_slots == 0); +} + bool furi_hal_bt_hid_kb_release(uint16_t button) { furi_assert(kb_report); for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 800fc3fe3..9c884b234 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -224,6 +224,8 @@ uint32_t furi_hal_bt_get_transmitted_packets(); */ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); +void furi_hal_bt_modify_profile_adv_name(const char* name, FuriHalBtProfile profile); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h index 4e74bbda7..e7b40f079 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h @@ -86,6 +86,15 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button); */ bool furi_hal_bt_hid_consumer_key_release_all(); +/** + * @brief Check if keyboard buffer has free slots + * + * @param n_emptry_slots number of empty slots in buffer to consider buffer is not full + * + * @return true if there is enough free slots in buffer +*/ +bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots); + #ifdef __cplusplus } #endif From a7c64bf936be1f6c4832a854413c5c06cdec7805 Mon Sep 17 00:00:00 2001 From: johnvizzz Date: Wed, 18 Jan 2023 18:25:11 +0100 Subject: [PATCH 004/231] fix api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 42ea150df..877e20b05 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -603,7 +603,15 @@ Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, B Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* +<<<<<<< HEAD Function,+,canvas_commit,void,Canvas* +======= +<<<<<<< HEAD +Function,-,canvas_commit,void,Canvas* +======= +Function,+,canvas_commit,void,Canvas* +>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215)) +>>>>>>> e04f2b4ce (api_symbols) Function,+,canvas_current_font_height,uint8_t,Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -632,7 +640,15 @@ Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" Function,+,canvas_height,uint8_t,Canvas* Function,-,canvas_init,Canvas*, Function,+,canvas_invert_color,void,Canvas* +<<<<<<< HEAD Function,+,canvas_reset,void,Canvas* +======= +<<<<<<< HEAD +Function,-,canvas_reset,void,Canvas* +======= +Function,+,canvas_reset,void,Canvas* +>>>>>>> b11b9f1b3 (Gui: Direct Draw API (#2215)) +>>>>>>> e04f2b4ce (api_symbols) Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" Function,+,canvas_set_font,void,"Canvas*, Font" From d518f9fa5baa0b8377bb5e38d339a38d6f8d5474 Mon Sep 17 00:00:00 2001 From: yocvito Date: Wed, 25 Jan 2023 23:44:47 +0100 Subject: [PATCH 005/231] Reverse mac address layout in mac changing scene (bad ble) --- .../main/bad_ble/scenes/bad_ble_scene_config_mac.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c index 2b05d7c59..a9ee7e34c 100644 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c @@ -2,6 +2,16 @@ #define TAG "BadBleConfigMac" +static uint8_t* reverse_mac_addr(uint8_t* mac) { + uint8_t tmp; + for(int i = 0; i < 3; i++) { + tmp = mac[i]; + mac[i] = mac[5 - i]; + mac[5 - i] = tmp; + } + return mac; +} + void bad_ble_scene_config_mac_byte_input_callback(void* context) { BadBleApp* bad_ble = context; @@ -19,7 +29,7 @@ void bad_ble_scene_config_mac_on_enter(void* context) { bad_ble_scene_config_mac_byte_input_callback, NULL, bad_ble, - bad_ble->mac, + reverse_mac_addr(bad_ble->mac), GAP_MAC_ADDR_SIZE); view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfigMac); } @@ -30,7 +40,7 @@ bool bad_ble_scene_config_mac_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == BadBleAppCustomEventByteInputDone) { - bt_set_profile_mac_address(bad_ble->bt, bad_ble->mac); + bt_set_profile_mac_address(bad_ble->bt, reverse_mac_addr(bad_ble->mac)); scene_manager_previous_scene(bad_ble->scene_manager); consumed = true; } From 5134f44c09d39344a8747655c0d59864bb574b96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Ha=C5=82adyn=20=28krzys=5Fh=29?= Date: Thu, 26 Jan 2023 09:28:36 +0100 Subject: [PATCH 006/231] nfc: Fix crash when using debug PCAP trace (#2338) --- lib/nfc/helpers/reader_analyzer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nfc/helpers/reader_analyzer.c b/lib/nfc/helpers/reader_analyzer.c index af4869ca9..9bf37a60d 100644 --- a/lib/nfc/helpers/reader_analyzer.c +++ b/lib/nfc/helpers/reader_analyzer.c @@ -159,6 +159,7 @@ void reader_analyzer_stop(ReaderAnalyzer* instance) { } if(instance->pcap) { nfc_debug_pcap_free(instance->pcap); + instance->pcap = NULL; } } From 58fa504a5e8ec35b30d0c7f75363da0a45065604 Mon Sep 17 00:00:00 2001 From: Clara K Date: Thu, 26 Jan 2023 20:31:25 +0100 Subject: [PATCH 007/231] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 4497d6889..60c5f54d6 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -181,4 +181,4 @@ This helps us a lot, thanks for the free license for this project! [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=github&utm_medium=organic&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. ---- -

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

+

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

From a88053964e25555c84d1935bccb27c6de3a1029b Mon Sep 17 00:00:00 2001 From: yocvito Date: Thu, 26 Jan 2023 22:54:15 +0100 Subject: [PATCH 008/231] Bluetooth timeout is now dynamic thanks to rssi analyzing --- applications/main/bad_ble/bad_ble_script.c | 81 +- .../main/bad_ble/scenes/bad_ble_scene_error.c | 34 +- .../main/bad_ble/views/bad_ble_view.c | 24 +- applications/services/bt/bt_service/bt.c | 16 + applications/services/bt/bt_service/bt.h | 8 + firmware/targets/f7/api_symbols.csv | 7 +- firmware/targets/f7/ble_glue/gap.c | 37 + firmware/targets/f7/ble_glue/gap.h | 2 + firmware/targets/f7/furi_hal/furi_hal_bt.c | 31 +- .../targets/furi_hal_include/furi_hal_bt.h | 12 +- flipper.log | 3146 +++++++++++++++++ 11 files changed, 3325 insertions(+), 73 deletions(-) create mode 100644 flipper.log diff --git a/applications/main/bad_ble/bad_ble_script.c b/applications/main/bad_ble/bad_ble_script.c index 5c3e8ce1b..73f4dd6c6 100644 --- a/applications/main/bad_ble/bad_ble_script.c +++ b/applications/main/bad_ble/bad_ble_script.c @@ -38,14 +38,15 @@ typedef enum { LevelRssi59_40, LevelRssi39_0, LevelRssiNum, -} LevelRssiDelays; + LevelRssiError = 0xFF, +} LevelRssiRange; const uint8_t bt_hid_delays[LevelRssiNum] = { - 45, // LevelRssi122_100 - 41, // LevelRssi99_80 - 37, // LevelRssi79_60 - 33, // LevelRssi59_40 - 30, // LevelRssi39_0 + 30, // LevelRssi122_100 + 25, // LevelRssi99_80 + 20, // LevelRssi79_60 + 17, // LevelRssi59_40 + 14, // LevelRssi39_0 }; struct BadBleScript { @@ -158,6 +159,37 @@ static const uint8_t numpad_keys[10] = { HID_KEYPAD_9, }; +uint8_t bt_timeout = 0; + +static LevelRssiRange bt_remote_rssi_range(Bt *bt) { + + BtRssi rssi_data = { 0 }; + + if (!bt_remote_rssi(bt, &rssi_data)) + return LevelRssiError; + + if (rssi_data.rssi <= 39) + return LevelRssi39_0; + else if (rssi_data.rssi <= 59) + return LevelRssi59_40; + else if (rssi_data.rssi <= 79) + return LevelRssi79_60; + else if (rssi_data.rssi <= 99) + return LevelRssi99_80; + else if (rssi_data.rssi <= 122) + return LevelRssi122_100; + + return LevelRssiError; +} + +static inline void update_bt_timeout(Bt *bt) { + + LevelRssiRange r = bt_remote_rssi_range(bt); + if (r < LevelRssiNum) { + bt_timeout = bt_hid_delays[r]; + } +} + /** * @brief Wait until there are enough free slots in the keyboard buffer * @@ -200,8 +232,8 @@ static void ducky_numlock_on() { if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); - furi_delay_ms(25); + + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); } } @@ -212,10 +244,8 @@ static bool ducky_numpad_press(const char num) { uint16_t key = numpad_keys[num - '0']; bt_hid_hold_while_keyboard_buffer_full(1, -1); FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); - furi_hal_bt_hid_kb_press(key); - FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); - furi_delay_ms(25); + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); return true; @@ -267,8 +297,8 @@ static bool ducky_string(BadBleScript* bad_ble, const char* param) { if(keycode != HID_KEYBOARD_NONE) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(keycode); - FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); - furi_delay_ms(25); + + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(keycode); } i++; @@ -377,8 +407,8 @@ static int32_t bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); - FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); - furi_delay_ms(25); + + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); return (0); @@ -398,8 +428,8 @@ static int32_t } FURI_LOG_I(WORKER_TAG, "Special key pressed %x\r\n", key); furi_hal_bt_hid_kb_press(key); - FURI_LOG_I(WORKER_TAG, "BT RSSI: %f\r", (double)furi_hal_bt_get_rssi()); - furi_delay_ms(25); + + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); return (0); } @@ -513,9 +543,13 @@ static void bad_ble_hid_state_callback(BtStatus status, void* context) { BadBleScript* bad_ble = (BadBleScript*)context; bool state = (status == BtStatusConnected); - if(state == true) + if(state == true) { + LevelRssiRange r = bt_remote_rssi_range(bad_ble->bt); + if (r != LevelRssiError) { + bt_timeout = bt_hid_delays[r]; + } furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect); - else + } else furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtDisconnect); } @@ -525,6 +559,8 @@ static int32_t bad_ble_worker(void* context) { BadBleWorkerState worker_state = BadBleStateInit; int32_t delay_val = 0; + bt_timeout = bt_hid_delays[LevelRssi39_0]; + // init ble hid bt_disconnect(bad_ble->bt); @@ -625,6 +661,10 @@ static int32_t bad_ble_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + + update_bt_timeout(bad_ble->bt); + FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + worker_state = BadBleStateRunning; } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadBleStateNotConnected; @@ -685,6 +725,9 @@ static int32_t bad_ble_worker(void* context) { break; } } + + update_bt_timeout(bad_ble->bt); + FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } // release all keys diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_error.c b/applications/main/bad_ble/scenes/bad_ble_scene_error.c index 7460c4042..a212f2087 100644 --- a/applications/main/bad_ble/scenes/bad_ble_scene_error.c +++ b/applications/main/bad_ble/scenes/bad_ble_scene_error.c @@ -30,30 +30,16 @@ void bad_ble_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_ble_scene_error_event_callback, app); } else if(app->error == BadBleAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if (settings->sfw_mode) { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } - else { - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Pull out from\nPC or phone to\nuse me like this."); - } + widget_add_string_multiline_element( + app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); + widget_add_string_multiline_element( + app->widget, + 3, + 30, + AlignLeft, + AlignTop, + FontSecondary, + "Disconnect from\nPC or phone to\nuse this function."); } view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewError); diff --git a/applications/main/bad_ble/views/bad_ble_view.c b/applications/main/bad_ble/views/bad_ble_view.c index fa5c099a5..2690b6702 100644 --- a/applications/main/bad_ble/views/bad_ble_view.c +++ b/applications/main/bad_ble/views/bad_ble_view.c @@ -50,12 +50,7 @@ static void bad_ble_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone) || (model->state.state == BadBleStateNotConnected)) { - if (settings->sfw_mode) { - elements_button_center(canvas, "Start"); - } - else { - elements_button_center(canvas, "Cum"); - } + elements_button_center(canvas, "Start"); } else if((model->state.state == BadBleStateRunning) || (model->state.state == BadBleStateDelay)) { elements_button_center(canvas, "Stop"); } else if(model->state.state == BadBleStateWillRun) { @@ -70,23 +65,12 @@ static void bad_ble_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadBleStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if (settings->sfw_mode) { - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect me"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to a computer"); - } - else { - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); - } + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect me"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to a computer"); } else if(model->state.state == BadBleStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if (settings->sfw_mode) { - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); - } - else { - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); - } + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadBleStateFileError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index a85238fdf..a993b6cae 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -428,6 +428,22 @@ const uint8_t *bt_get_profile_mac_address(Bt *bt) { } } +bool bt_remote_rssi(Bt *bt, BtRssi *rssi) { + furi_assert(bt); + UNUSED(rssi); + + uint8_t rssi_val; + uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val); + + if (since == 0) + return false; + + rssi->rssi = rssi_val; + rssi->since = since; + + return true; +} + int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index 4320e007e..2e6bbf1bb 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -23,6 +23,12 @@ typedef enum { BtProfileHidKeyboard, } BtProfile; +typedef struct { + uint8_t rssi; + uint32_t since; +} BtRssi; + + typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); /** Change BLE Profile @@ -42,6 +48,8 @@ const char *bt_get_profile_adv_name(Bt *bt); void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]); const uint8_t *bt_get_profile_mac_address(Bt *bt); +bool bt_remote_rssi(Bt *bt, BtRssi *rssi); + /** Disconnect from Central * diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7c1a203cc..a5ce0b489 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,12.7,, +Version,v,12.12,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -575,6 +575,7 @@ Function,?,bt_get_profile_adv_name,const char*,Bt* Function,?,bt_get_profile_mac_address,const uint8_t*,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,?,bt_remote_rssi,_Bool,"Bt*, BtRssi*" Function,?,bt_set_profile,_Bool,"Bt*, BtProfile" Function,?,bt_set_profile_adv_name,void,"Bt*, const char*, ..." Function,?,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" @@ -999,6 +1000,7 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi Function,+,furi_hal_bt_clear_white_list,_Bool, Function,+,furi_hal_bt_dump_state,void,FuriString* Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,?,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t* Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,?,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile Function,?,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile @@ -1035,7 +1037,7 @@ Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" -Function,?,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + 8 + ( 8 + 1 ) )]" +Function,?,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" Function,?,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" Function,+,furi_hal_bt_start_advertising,void, Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" @@ -1578,6 +1580,7 @@ Function,-,gamma,double,double Function,-,gamma_r,double,"double, int*" Function,-,gammaf,float,float Function,-,gammaf_r,float,"float, int*" +Function,?,gap_get_remote_conn_rssi,uint32_t,int8_t* Function,-,gap_get_state,GapState, Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" Function,-,gap_start_advertising,void, diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 2bfd2ff3a..8023b8915 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -27,6 +27,8 @@ typedef struct { GapConfig* config; GapConnectionParams connection_params; GapState state; + int8_t conn_rssi; + uint32_t time_rssi_sample; FuriMutex* state_mutex; GapEventCallback on_event_cb; void* context; @@ -52,6 +54,16 @@ static const uint8_t gap_erk[16] = static Gap* gap = NULL; +static inline void fetch_rssi() { + uint8_t ret_rssi = 127; + if (hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) { + gap->conn_rssi = (int8_t) ret_rssi; + gap->time_rssi_sample = furi_get_tick(); + return; + } + FURI_LOG_E(TAG, "Failed to read RSSI"); +} + static void gap_advertise_start(GapState new_state); static int32_t gap_app(void* context); @@ -125,6 +137,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->connection_params.supervisor_timeout = event->Supervision_Timeout; FURI_LOG_I(TAG, "Connection parameters event complete"); gap_verify_connection_parameters(gap); + + // save rssi for current connection + fetch_rssi(); break; } @@ -151,6 +166,7 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->connection_params.slave_latency = event->Conn_Latency; gap->connection_params.supervisor_timeout = event->Supervision_Timeout; + // Stop advertising as connection completed furi_timer_stop(gap->advertise_timer); @@ -159,6 +175,9 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->service.connection_handle = event->Connection_Handle; gap_verify_connection_parameters(gap); + + fetch_rssi(); + // Start pairing by sending security request aci_gap_slave_security_req(event->Connection_Handle); } break; @@ -243,6 +262,8 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { pairing_complete->Status); aci_gap_terminate(gap->service.connection_handle, 5); } else { + fetch_rssi(); + FURI_LOG_I(TAG, "Pairing complete"); GapEvent event = {.type = GapEventTypeConnected}; gap->on_event_cb(event, gap->context); //-V595 @@ -495,6 +516,9 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { gap->service.connection_handle = 0xFFFF; gap->enable_adv = true; + gap->conn_rssi = 127; + gap->time_rssi_sample = 0; + // Thread configuration gap->thread = furi_thread_alloc_ex("BleGapDriver", 1024, gap_app, gap); furi_thread_start(gap->thread); @@ -514,6 +538,19 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { return true; } +uint32_t gap_get_remote_conn_rssi(int8_t *rssi) { + if (gap && gap->state == GapStateConnected) { + fetch_rssi(); + *rssi = gap->conn_rssi; + + FURI_LOG_D(TAG, "RSSI: %d", *rssi); + + if (gap->time_rssi_sample) + return furi_get_tick() - gap->time_rssi_sample; + } + return 0; +} + GapState gap_get_state() { GapState state; if(gap) { diff --git a/firmware/targets/f7/ble_glue/gap.h b/firmware/targets/f7/ble_glue/gap.h index 1e207299f..3b9ca5b28 100644 --- a/firmware/targets/f7/ble_glue/gap.h +++ b/firmware/targets/f7/ble_glue/gap.h @@ -81,6 +81,8 @@ GapState gap_get_state(); void gap_thread_stop(); +uint32_t gap_get_remote_conn_rssi(int8_t *rssi); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 61939e12d..f01b66d14 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -200,14 +200,14 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, break; } GapConfig* config = &profile_config[profile].config; - if (strlen(&(profile_config[profile].config.adv_name[1])) == 0) { + if(strlen(&(profile_config[profile].config.adv_name[1])) == 0) { // Set advertise name strlcpy( profile_config[profile].config.adv_name, furi_hal_version_get_ble_local_device_name_ptr(), FURI_HAL_VERSION_DEVICE_NAME_LENGTH); } - // Configure GAP + // Configure GAP if(profile == FuriHalBtProfileSerial) { // Set mac address memcpy( @@ -425,6 +425,23 @@ float furi_hal_bt_get_rssi() { return val; } +/** fill the RSSI of the remote host of the bt connection and returns the time since + * the beginning of the connection + * +*/ +uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi) { + + int8_t ret_rssi = 0; + uint32_t since = gap_get_remote_conn_rssi(&ret_rssi); + + if (ret_rssi == 127 || since == 0) + return 0; + + *rssi = (uint8_t) abs(ret_rssi); + + return since; +} + uint32_t furi_hal_bt_get_transmitted_packets() { uint32_t packets = 0; aci_hal_le_tx_test_packet_number(&packets); @@ -450,13 +467,14 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { return false; } -void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]) { +void furi_hal_bt_set_profile_adv_name( + FuriHalBtProfile profile, + const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]) { furi_assert(profile < FuriHalBtProfileNumber); furi_assert(name); - memcpy(&(profile_config[profile].config.adv_name[1]), - name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH); - + memcpy( + &(profile_config[profile].config.adv_name[1]), name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1); } const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) { @@ -471,7 +489,6 @@ void furi_hal_bt_set_profile_mac_addr( furi_assert(mac_addr); memcpy(profile_config[profile].config.mac_address, mac_addr, GAP_MAC_ADDR_SIZE); - } const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile) { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 5127972f5..45d2a4261 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -224,14 +224,24 @@ uint32_t furi_hal_bt_get_transmitted_packets(); */ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); -void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]); +/** Modify profile advertisement name and restart bluetooth + * @param[in] profile profile type + * @param[in] name new adv name +*/ +void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]); const char *furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile); +/** Modify profile mac address and restart bluetooth + * @param[in] profile profile type + * @param[in] mac new mac address +*/ void furi_hal_bt_set_profile_mac_addr(FuriHalBtProfile profile, const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); const uint8_t *furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile); +uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi); + #ifdef __cplusplus } #endif diff --git a/flipper.log b/flipper.log new file mode 100644 index 000000000..f267750c0 --- /dev/null +++ b/flipper.log @@ -0,0 +1,3146 @@ + + _.-------.._ -, + .-"```"--..,,_/ /`-, -, \ + .:" /:/ /'\ \ ,_..., `. | | + / ,----/:/ /`\ _\~`_-"` _; + ' / /`"""'\ \ \.~`_-' ,-"'/ + | | | 0 | | .-' ,/` / + | ,..\ \ ,.-"` ,/` / + ; : `/`""\` ,/--==,/-----, + | `-...| -.___-Z:_______J...---; + : ` _-' + _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ +| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| +| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | +|_| |____||___||_| |_| |___||_|_\ \___||____||___| + +Welcome to Flipper Zero Command Line Interface! +Read Manual https://docs.flipperzero.one + +Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) + +>: log +Press CTRL+C to stop... +218220 [D][BrowserWorker] End +218233 [I][BtGap] Stop advertising +218237 [D][BtGap] set_non_discoverable success +218252 [I][SavedStruct] Loading "/int/.desktop.settings" +218390 [I][SavedStruct] Loading "/int/.desktop.settings" +218442 [I][FuriHalBt] Disconnect and stop advertising +218444 [I][FuriHalBt] Stop current profile services +218453 [I][FuriHalBt] Stop BLE related RTOS threads +218479 [I][FuriHalBt] Reset SHCI +218590 [I][SavedStruct] Loading "/int/.desktop.settings" +218594 [I][FuriHalBt] Start BT initialization +218601 [I][Core2] Core2 started +218604 [I][Core2] C2 boot completed, mode: Stack +218608 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +218612 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +218619 [I][Core2] Radio stack started +218623 [I][Core2] Flash activity control switched to SEM7 +218626 [I][BtGap] Advertising name: Keyboard +218629 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +218646 [D][BtBatterySvc] Updating power state characteristic +218651 [I][BtGap] Start advertising +218654 [I][SavedStruct] Loading "/int/.bt.settings" +218671 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +218681 [I][FuriHalBt] Disconnect and stop advertising +218683 [I][BtGap] Stop advertising +218686 [D][BtGap] set_non_discoverable success +218689 [I][FuriHalBt] Stop current profile services +218698 [I][FuriHalBt] Stop BLE related RTOS threads +218723 [I][FuriHalBt] Reset SHCI +218748 [I][SavedStruct] Loading "/int/.desktop.settings" +218790 [I][SavedStruct] Loading "/int/.desktop.settings" +218837 [I][FuriHalBt] Start BT initialization +218842 [I][Core2] Core2 started +218844 [I][Core2] C2 boot completed, mode: Stack +218847 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +218849 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +218854 [I][Core2] Radio stack started +218856 [I][Core2] Flash activity control switched to SEM7 +218859 [I][BtGap] Advertising name: Rumik1 +218861 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +218879 [D][BtBatterySvc] Updating power state characteristic +218889 [I][BtSrv] Bt App started +218891 [I][BtGap] Start advertising +218894 [I][BadBleWorker] Init +218896 [I][SavedStruct] Loading "/int/.desktop.settings" +218923 [I][BadBleWorker] BLE Key timeout : 16 +218990 [I][SavedStruct] Loading "/int/.desktop.settings" +219190 [I][SavedStruct] Loading "/int/.desktop.settings" +219248 [I][SavedStruct] Loading "/int/.desktop.settings" +219285 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +219287 [I][BtGap] Rssi: 295 +219390 [D][BtGap] Slave security initiated +219392 [I][SavedStruct] Loading "/int/.desktop.settings" +219508 [I][BtGap] Pairing complete +219511 [D][BtBatterySvc] Updating battery level characteristic +219515 [I][BadBleWorker] BLE Key timeout : 16 +219590 [I][SavedStruct] Loading "/int/.desktop.settings" +219687 [I][BtGap] Rx MTU size: 414 +219748 [I][SavedStruct] Loading "/int/.desktop.settings" +219790 [I][SavedStruct] Loading "/int/.desktop.settings" +219990 [I][SavedStruct] Loading "/int/.desktop.settings" +220190 [I][SavedStruct] Loading "/int/.desktop.settings" +220248 [I][SavedStruct] Loading "/int/.desktop.settings" +220282 [I][BtGap] Connection parameters event complete +220284 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +220287 [W][BtGap] Unsupported connection interval. Request connection parameters update +220290 [I][BtGap] Rssi: 298 +220335 [D][BtGap] Connection parameters accepted +220390 [I][SavedStruct] Loading "/int/.desktop.settings" +220498 [I][BtGap] Connection parameters event complete +220500 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +220506 [I][BtGap] Rssi: 293 +220590 [I][SavedStruct] Loading "/int/.desktop.settings" +220748 [I][SavedStruct] Loading "/int/.desktop.settings" +220790 [I][SavedStruct] Loading "/int/.desktop.settings" +220990 [I][SavedStruct] Loading "/int/.desktop.settings" +221190 [I][SavedStruct] Loading "/int/.desktop.settings" +221248 [I][SavedStruct] Loading "/int/.desktop.settings" +221390 [I][SavedStruct] Loading "/int/.desktop.settings" +221590 [I][SavedStruct] Loading "/int/.desktop.settings" +221748 [I][SavedStruct] Loading "/int/.desktop.settings" +221790 [I][SavedStruct] Loading "/int/.desktop.settings" +221990 [I][SavedStruct] Loading "/int/.desktop.settings" +222190 [I][SavedStruct] Loading "/int/.desktop.settings" +222248 [I][SavedStruct] Loading "/int/.desktop.settings" +222390 [I][SavedStruct] Loading "/int/.desktop.settings" +222590 [I][SavedStruct] Loading "/int/.desktop.settings" +222748 [I][SavedStruct] Loading "/int/.desktop.settings" +222790 [I][SavedStruct] Loading "/int/.desktop.settings" +222990 [I][SavedStruct] Loading "/int/.desktop.settings" +223190 [I][SavedStruct] Loading "/int/.desktop.settings" +223248 [I][SavedStruct] Loading "/int/.desktop.settings" +223390 [I][SavedStruct] Loading "/int/.desktop.settings" +223590 [I][SavedStruct] Loading "/int/.desktop.settings" +223748 [I][SavedStruct] Loading "/int/.desktop.settings" +223790 [I][SavedStruct] Loading "/int/.desktop.settings" +223990 [I][SavedStruct] Loading "/int/.desktop.settings" +224190 [I][SavedStruct] Loading "/int/.desktop.settings" +224248 [I][SavedStruct] Loading "/int/.desktop.settings" +224390 [I][SavedStruct] Loading "/int/.desktop.settings" +224590 [I][SavedStruct] Loading "/int/.desktop.settings" +224748 [I][SavedStruct] Loading "/int/.desktop.settings" +224790 [I][SavedStruct] Loading "/int/.desktop.settings" +224990 [I][SavedStruct] Loading "/int/.desktop.settings" +225190 [I][SavedStruct] Loading "/int/.desktop.settings" +225248 [I][SavedStruct] Loading "/int/.desktop.settings" +225390 [I][SavedStruct] Loading "/int/.desktop.settings" +225590 [I][SavedStruct] Loading "/int/.desktop.settings" +225748 [I][SavedStruct] Loading "/int/.desktop.settings" +225790 [I][SavedStruct] Loading "/int/.desktop.settings" +225990 [I][SavedStruct] Loading "/int/.desktop.settings" +226190 [I][SavedStruct] Loading "/int/.desktop.settings" +226248 [I][SavedStruct] Loading "/int/.desktop.settings" +226390 [I][SavedStruct] Loading "/int/.desktop.settings" +226596 [I][SavedStruct] Loading "/int/.desktop.settings" +226748 [I][SavedStruct] Loading "/int/.desktop.settings" +226790 [I][SavedStruct] Loading "/int/.desktop.settings" +226990 [I][SavedStruct] Loading "/int/.desktop.settings" +227190 [I][SavedStruct] Loading "/int/.desktop.settings" +227248 [I][SavedStruct] Loading "/int/.desktop.settings" +227390 [I][SavedStruct] Loading "/int/.desktop.settings" +227590 [I][SavedStruct] Loading "/int/.desktop.settings" +227748 [I][SavedStruct] Loading "/int/.desktop.settings" +227790 [I][SavedStruct] Loading "/int/.desktop.settings" +227990 [I][SavedStruct] Loading "/int/.desktop.settings" +228190 [I][SavedStruct] Loading "/int/.desktop.settings" +228248 [I][SavedStruct] Loading "/int/.desktop.settings" +228390 [I][SavedStruct] Loading "/int/.desktop.settings" +228590 [I][SavedStruct] Loading "/int/.desktop.settings" +228748 [I][SavedStruct] Loading "/int/.desktop.settings" +228790 [I][SavedStruct] Loading "/int/.desktop.settings" +234485 [I][FuriHalBt] Disconnect and stop advertising +234489 [I][BtGap] Stop advertising +234493 [D][BtGap] terminate success +234497 [E][BtGap] set_non_discoverable failed 12 +234502 [I][FuriHalBt] Stop current profile services +234507 [I][BadBleWorker] BLE Key timeout : 16 +234526 [I][FuriHalBt] Stop BLE related RTOS threads +234551 [I][FuriHalBt] Reset SHCI +234665 [I][FuriHalBt] Start BT initialization +234670 [I][Core2] Core2 started +234672 [I][Core2] C2 boot completed, mode: Stack +234675 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +234677 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +234681 [I][Core2] Radio stack started +234683 [I][Core2] Flash activity control switched to SEM7 +234685 [I][BtGap] Advertising name: Mmm_ +234687 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +234711 [D][BtBatterySvc] Updating power state characteristic +234721 [I][BtGap] Start advertising +235977 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +235982 [I][BtGap] Rssi: 308 +236066 [D][BtGap] Slave security initiated +236187 [I][BtGap] Pairing complete +236190 [D][BtBatterySvc] Updating battery level characteristic +236195 [I][BadBleWorker] BLE Key timeout : 16 +236366 [I][BtGap] Rx MTU size: 414 +236935 [I][BtGap] Connection parameters event complete +236938 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +236941 [W][BtGap] Unsupported connection interval. Request connection parameters update +236944 [I][BtGap] Rssi: 314 +236975 [D][BtGap] Connection parameters accepted +237139 [I][BtGap] Connection parameters event complete +237141 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +237144 [I][BtGap] Rssi: 326 +240985 [I][FuriHalBt] Disconnect and stop advertising +240989 [I][BtGap] Stop advertising +240993 [D][BtGap] terminate success +240997 [E][BtGap] set_non_discoverable failed 12 +241002 [I][FuriHalBt] Stop current profile services +241007 [I][BadBleWorker] BLE Key timeout : 16 +241028 [I][FuriHalBt] Stop BLE related RTOS threads +241054 [I][FuriHalBt] Reset SHCI +241168 [I][FuriHalBt] Start BT initialization +241173 [I][Core2] Core2 started +241175 [I][Core2] C2 boot completed, mode: Stack +241178 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +241180 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +241184 [I][Core2] Radio stack started +241186 [I][Core2] Flash activity control switched to SEM7 +241188 [I][BtGap] Advertising name: Mmm_ +241190 [I][BtGap] MAC @ : 35:F2:47:26:81:88 +241212 [D][BtBatterySvc] Updating power state characteristic +241222 [I][BtGap] Start advertising +242157 [D][ViewDispatcher] View changed while key press 20010600 -> 20010708. Sending key: Back, type: Release, sequence: 0000002C to previous view port +242161 [I][SavedStruct] Loading "/int/.desktop.settings" +242190 [I][SavedStruct] Loading "/int/.desktop.settings" +242390 [I][SavedStruct] Loading "/int/.desktop.settings" +242590 [I][SavedStruct] Loading "/int/.desktop.settings" +242662 [I][SavedStruct] Loading "/int/.desktop.settings" +242790 [I][SavedStruct] Loading "/int/.desktop.settings" +242836 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +242839 [I][BtGap] Rssi: 305 +242929 [D][BtGap] Slave security initiated +242990 [I][SavedStruct] Loading "/int/.desktop.settings" +243018 [I][BtGap] Rx MTU size: 414 +243162 [I][SavedStruct] Loading "/int/.desktop.settings" +243190 [I][SavedStruct] Loading "/int/.desktop.settings" +243390 [I][SavedStruct] Loading "/int/.desktop.settings" +243524 [D][BtGap] Bond lost event. Start rebonding +243590 [I][SavedStruct] Loading "/int/.desktop.settings" +243662 [I][SavedStruct] Loading "/int/.desktop.settings" +243702 [I][BtGap] Verify numeric comparison: 382889 +243829 [I][SavedStruct] Loading "/int/.desktop.settings" +245253 [I][SavedStruct] Loading "/int/.desktop.settings" +245274 [I][SavedStruct] Loading "/int/.desktop.settings" +245390 [I][SavedStruct] Loading "/int/.desktop.settings" +245590 [I][SavedStruct] Loading "/int/.desktop.settings" +245662 [I][SavedStruct] Loading "/int/.desktop.settings" +245679 [I][BtGap] Pairing complete +245683 [D][BtBatterySvc] Updating battery level characteristic +245687 [I][BadBleWorker] BLE Key timeout : 16 +245790 [I][SavedStruct] Loading "/int/.desktop.settings" +245990 [I][SavedStruct] Loading "/int/.desktop.settings" +246162 [I][SavedStruct] Loading "/int/.desktop.settings" +246190 [I][SavedStruct] Loading "/int/.desktop.settings" +246390 [I][SavedStruct] Loading "/int/.desktop.settings" +246590 [I][SavedStruct] Loading "/int/.desktop.settings" +246660 [I][BtGap] Connection parameters event complete +246663 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +246667 [W][BtGap] Unsupported connection interval. Request connection parameters update +246670 [I][SavedStruct] Loading "/int/.desktop.settings" +246673 [I][BtGap] Rssi: 304 +246699 [D][BtGap] Connection parameters accepted +246790 [I][SavedStruct] Loading "/int/.desktop.settings" +246862 [I][BtGap] Connection parameters event complete +246866 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +246871 [I][BtGap] Rssi: 303 +246990 [I][SavedStruct] Loading "/int/.desktop.settings" +247162 [I][SavedStruct] Loading "/int/.desktop.settings" +247190 [I][SavedStruct] Loading "/int/.desktop.settings" +247390 [I][SavedStruct] Loading "/int/.desktop.settings" +247590 [I][SavedStruct] Loading "/int/.desktop.settings" +247662 [I][SavedStruct] Loading "/int/.desktop.settings" +247790 [I][SavedStruct] Loading "/int/.desktop.settings" +247990 [I][SavedStruct] Loading "/int/.desktop.settings" +248162 [I][SavedStruct] Loading "/int/.desktop.settings" +248190 [I][SavedStruct] Loading "/int/.desktop.settings" +248390 [I][SavedStruct] Loading "/int/.desktop.settings" +248590 [I][SavedStruct] Loading "/int/.desktop.settings" +248747 [D][DolphinState] icounter 1325, butthurt 0 +248750 [I][BadBleWorker] BLE Key timeout : 16 +248754 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +248757 [I][BadBleWorker] BLE Key timeout : 16 +248759 [D][BadBleWorker] line:DELAY 1000 +248763 [I][BadBleWorker] BLE Key timeout : 16 +248790 [I][SavedStruct] Loading "/int/.desktop.settings" +248990 [I][SavedStruct] Loading "/int/.desktop.settings" +249190 [I][SavedStruct] Loading "/int/.desktop.settings" +249248 [I][SavedStruct] Loading "/int/.desktop.settings" +249390 [I][SavedStruct] Loading "/int/.desktop.settings" +249590 [I][SavedStruct] Loading "/int/.desktop.settings" +249748 [I][SavedStruct] Loading "/int/.desktop.settings" +249766 [D][BadBleWorker] line:GUI SPACE +249768 [I][BadBleWorker] Special key pressed 82c + +249773 [I][BadBleWorker] BT RSSI: 0.000000 +249790 [I][SavedStruct] Loading "/int/.desktop.settings" +249794 [I][BadBleWorker] BLE Key timeout : 16 +249800 [D][BadBleWorker] line:DELAY 500 +249804 [I][BadBleWorker] BLE Key timeout : 16 +249990 [I][SavedStruct] Loading "/int/.desktop.settings" +250190 [I][SavedStruct] Loading "/int/.desktop.settings" +250248 [I][SavedStruct] Loading "/int/.desktop.settings" +250308 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +250312 [I][BadBleWorker] BT RSSI: 0.000000 +250332 [I][BadBleWorker] BT RSSI: 0.000000 +250351 [I][BadBleWorker] BT RSSI: 0.000000 +250371 [I][BadBleWorker] BT RSSI: 0.000000 +250390 [I][SavedStruct] Loading "/int/.desktop.settings" +250392 [I][BadBleWorker] BT RSSI: 0.000000 +250414 [I][BadBleWorker] BT RSSI: 0.000000 +250434 [I][BadBleWorker] BT RSSI: 0.000000 +250454 [I][BadBleWorker] BT RSSI: 0.000000 +250474 [I][BadBleWorker] BT RSSI: 0.000000 +250494 [I][BadBleWorker] BT RSSI: 0.000000 +250514 [I][BadBleWorker] BT RSSI: 0.000000 +250534 [I][BadBleWorker] BT RSSI: 0.000000 +250554 [I][BadBleWorker] BT RSSI: 0.000000 +250574 [I][BadBleWorker] BT RSSI: 0.000000 +250590 [I][SavedStruct] Loading "/int/.desktop.settings" +250596 [I][BadBleWorker] BT RSSI: 0.000000 +250618 [I][BadBleWorker] BT RSSI: 0.000000 +250638 [I][BadBleWorker] BT RSSI: 0.000000 +250658 [I][BadBleWorker] BT RSSI: 0.000000 +250678 [I][BadBleWorker] BT RSSI: 0.000000 +250698 [I][BadBleWorker] BT RSSI: 0.000000 +250718 [I][BadBleWorker] BT RSSI: 0.000000 +250737 [I][BadBleWorker] BT RSSI: 0.000000 +250748 [I][SavedStruct] Loading "/int/.desktop.settings" +250760 [I][BadBleWorker] BT RSSI: 0.000000 +250781 [I][BadBleWorker] BT RSSI: 0.000000 +250790 [I][SavedStruct] Loading "/int/.desktop.settings" +250803 [I][BadBleWorker] BT RSSI: 0.000000 +250824 [I][BadBleWorker] BT RSSI: 0.000000 +250844 [I][BadBleWorker] BT RSSI: 0.000000 +250864 [I][BadBleWorker] BT RSSI: 0.000000 +250884 [I][BadBleWorker] BT RSSI: 0.000000 +250904 [I][BadBleWorker] BT RSSI: 0.000000 +250926 [I][BadBleWorker] BT RSSI: 0.000000 +250947 [I][BadBleWorker] BT RSSI: 0.000000 +250967 [I][BadBleWorker] BT RSSI: 0.000000 +250987 [I][BadBleWorker] BT RSSI: 0.000000 +250990 [I][SavedStruct] Loading "/int/.desktop.settings" +251008 [I][BadBleWorker] BT RSSI: 0.000000 +251028 [I][BadBleWorker] BT RSSI: 0.000000 +251048 [I][BadBleWorker] BT RSSI: 0.000000 +251068 [I][BadBleWorker] BT RSSI: 0.000000 +251088 [I][BadBleWorker] BT RSSI: 0.000000 +251108 [I][BadBleWorker] BT RSSI: 0.000000 +251128 [I][BadBleWorker] BT RSSI: 0.000000 +251147 [I][BadBleWorker] BLE Key timeout : 16 +251150 [D][BadBleWorker] line:DELAY 200 +251152 [I][BadBleWorker] BLE Key timeout : 16 +251190 [I][SavedStruct] Loading "/int/.desktop.settings" +251248 [I][SavedStruct] Loading "/int/.desktop.settings" +251355 [D][BadBleWorker] line:ENTER +251357 [I][BadBleWorker] Special key pressed 28 + +251360 [I][BadBleWorker] BT RSSI: 0.000000 +251379 [I][BadBleWorker] BLE Key timeout : 16 +251382 [D][BadBleWorker] line:GUI SPACE +251384 [I][BadBleWorker] Special key pressed 82c + +251387 [I][BadBleWorker] BT RSSI: 0.000000 +251391 [I][SavedStruct] Loading "/int/.desktop.settings" +251408 [I][BadBleWorker] BLE Key timeout : 16 +251411 [D][BadBleWorker] line:DELAY 500 +251413 [I][BadBleWorker] BLE Key timeout : 16 +251590 [I][SavedStruct] Loading "/int/.desktop.settings" +251748 [I][SavedStruct] Loading "/int/.desktop.settings" +251790 [I][SavedStruct] Loading "/int/.desktop.settings" +251915 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +251919 [I][BadBleWorker] BT RSSI: 0.000000 +251941 [I][BadBleWorker] BT RSSI: 0.000000 +251962 [I][BadBleWorker] BT RSSI: 0.000000 +251982 [I][BadBleWorker] BT RSSI: 0.000000 +251990 [I][SavedStruct] Loading "/int/.desktop.settings" +252004 [I][BadBleWorker] BT RSSI: 0.000000 +252026 [I][BadBleWorker] BT RSSI: 0.000000 +252046 [I][BadBleWorker] BT RSSI: 0.000000 +252066 [I][BadBleWorker] BT RSSI: 0.000000 +252086 [I][BadBleWorker] BT RSSI: 0.000000 +252106 [I][BadBleWorker] BT RSSI: 0.000000 +252126 [I][BadBleWorker] BT RSSI: 0.000000 +252146 [I][BadBleWorker] BT RSSI: 0.000000 +252166 [I][BadBleWorker] BT RSSI: 0.000000 +252186 [I][BadBleWorker] BT RSSI: 0.000000 +252190 [I][SavedStruct] Loading "/int/.desktop.settings" +252207 [I][BadBleWorker] BT RSSI: 0.000000 +252227 [I][BadBleWorker] BT RSSI: 0.000000 +252248 [I][BadBleWorker] BT RSSI: 0.000000 +252251 [I][SavedStruct] Loading "/int/.desktop.settings" +252269 [I][BadBleWorker] BT RSSI: 0.000000 +252289 [I][BadBleWorker] BT RSSI: 0.000000 +252309 [I][BadBleWorker] BT RSSI: 0.000000 +252329 [I][BadBleWorker] BT RSSI: 0.000000 +252348 [I][BadBleWorker] BT RSSI: 0.000000 +252368 [I][BadBleWorker] BT RSSI: 0.000000 +252388 [I][BadBleWorker] BT RSSI: 0.000000 +252391 [I][SavedStruct] Loading "/int/.desktop.settings" +252409 [I][BadBleWorker] BT RSSI: 0.000000 +252429 [I][BadBleWorker] BT RSSI: 0.000000 +252449 [I][BadBleWorker] BT RSSI: 0.000000 +252469 [I][BadBleWorker] BT RSSI: 0.000000 +252489 [I][BadBleWorker] BT RSSI: 0.000000 +252509 [I][BadBleWorker] BT RSSI: 0.000000 +252529 [I][BadBleWorker] BT RSSI: 0.000000 +252549 [I][BadBleWorker] BT RSSI: 0.000000 +252569 [I][BadBleWorker] BT RSSI: 0.000000 +252590 [I][SavedStruct] Loading "/int/.desktop.settings" +252592 [I][BadBleWorker] BT RSSI: 0.000000 +252614 [I][BadBleWorker] BT RSSI: 0.000000 +252634 [I][BadBleWorker] BT RSSI: 0.000000 +252654 [I][BadBleWorker] BT RSSI: 0.000000 +252674 [I][BadBleWorker] BT RSSI: 0.000000 +252694 [I][BadBleWorker] BT RSSI: 0.000000 +252714 [I][BadBleWorker] BT RSSI: 0.000000 +252733 [I][BadBleWorker] BT RSSI: 0.000000 +252748 [I][SavedStruct] Loading "/int/.desktop.settings" +252754 [I][BadBleWorker] BLE Key timeout : 16 +252758 [D][BadBleWorker] line:DELAY 200 +252763 [I][BadBleWorker] BLE Key timeout : 16 +252790 [I][SavedStruct] Loading "/int/.desktop.settings" +252967 [D][BadBleWorker] line:ENTER +252969 [I][BadBleWorker] Special key pressed 28 + +252972 [I][BadBleWorker] BT RSSI: 0.000000 +252990 [I][SavedStruct] Loading "/int/.desktop.settings" +252993 [I][BadBleWorker] BLE Key timeout : 16 +253000 [D][BadBleWorker] line:GUI SPACE +253002 [I][BadBleWorker] Special key pressed 82c + +253007 [I][BadBleWorker] BT RSSI: 0.000000 +253028 [I][BadBleWorker] BLE Key timeout : 16 +253031 [D][BadBleWorker] line:DELAY 500 +253033 [I][BadBleWorker] BLE Key timeout : 16 +253190 [I][SavedStruct] Loading "/int/.desktop.settings" +253248 [I][SavedStruct] Loading "/int/.desktop.settings" +253390 [I][SavedStruct] Loading "/int/.desktop.settings" +253536 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +253540 [I][BadBleWorker] BT RSSI: 0.000000 +253560 [I][BadBleWorker] BT RSSI: 0.000000 +253580 [I][BadBleWorker] BT RSSI: 0.000000 +253590 [I][SavedStruct] Loading "/int/.desktop.settings" +253602 [I][BadBleWorker] BT RSSI: 0.000000 +253624 [I][BadBleWorker] BT RSSI: 0.000000 +253644 [I][BadBleWorker] BT RSSI: 0.000000 +253664 [I][BadBleWorker] BT RSSI: 0.000000 +253684 [I][BadBleWorker] BT RSSI: 0.000000 +253704 [I][BadBleWorker] BT RSSI: 0.000000 +253724 [I][BadBleWorker] BT RSSI: 0.000000 +253743 [I][BadBleWorker] BT RSSI: 0.000000 +253748 [I][SavedStruct] Loading "/int/.desktop.settings" +253765 [I][BadBleWorker] BT RSSI: 0.000000 +253785 [I][BadBleWorker] BT RSSI: 0.000000 +253790 [I][SavedStruct] Loading "/int/.desktop.settings" +253807 [I][BadBleWorker] BT RSSI: 0.000000 +253827 [I][BadBleWorker] BT RSSI: 0.000000 +253847 [I][BadBleWorker] BT RSSI: 0.000000 +253867 [I][BadBleWorker] BT RSSI: 0.000000 +253887 [I][BadBleWorker] BT RSSI: 0.000000 +253907 [I][BadBleWorker] BT RSSI: 0.000000 +253927 [I][BadBleWorker] BT RSSI: 0.000000 +253947 [I][BadBleWorker] BT RSSI: 0.000000 +253969 [I][BadBleWorker] BT RSSI: 0.000000 +253990 [I][SavedStruct] Loading "/int/.desktop.settings" +253993 [I][BadBleWorker] BT RSSI: 0.000000 +254015 [I][BadBleWorker] BT RSSI: 0.000000 +254035 [I][BadBleWorker] BT RSSI: 0.000000 +254055 [I][BadBleWorker] BT RSSI: 0.000000 +254075 [I][BadBleWorker] BT RSSI: 0.000000 +254095 [I][BadBleWorker] BT RSSI: 0.000000 +254115 [I][BadBleWorker] BT RSSI: 0.000000 +254135 [I][BadBleWorker] BT RSSI: 0.000000 +254155 [I][BadBleWorker] BT RSSI: 0.000000 +254174 [I][BadBleWorker] BT RSSI: 0.000000 +254190 [I][SavedStruct] Loading "/int/.desktop.settings" +254196 [I][BadBleWorker] BT RSSI: 0.000000 +254217 [I][BadBleWorker] BT RSSI: 0.000000 +254237 [I][BadBleWorker] BT RSSI: 0.000000 +254248 [I][SavedStruct] Loading "/int/.desktop.settings" +254259 [I][BadBleWorker] BT RSSI: 0.000000 +254281 [I][BadBleWorker] BT RSSI: 0.000000 +254300 [I][BadBleWorker] BT RSSI: 0.000000 +254320 [I][BadBleWorker] BT RSSI: 0.000000 +254340 [I][BadBleWorker] BT RSSI: 0.000000 +254360 [I][BadBleWorker] BT RSSI: 0.000000 +254379 [I][BadBleWorker] BLE Key timeout : 16 +254383 [D][BadBleWorker] line:DELAY 200 +254386 [I][BadBleWorker] BLE Key timeout : 16 +254390 [I][SavedStruct] Loading "/int/.desktop.settings" +254588 [D][BadBleWorker] line:ENTER +254590 [I][BadBleWorker] Special key pressed 28 + +254593 [I][SavedStruct] Loading "/int/.desktop.settings" +254596 [I][BadBleWorker] BT RSSI: 0.000000 +254617 [I][BadBleWorker] BLE Key timeout : 16 +254621 [D][BadBleWorker] line:GUI SPACE +254623 [I][BadBleWorker] Special key pressed 82c + +254627 [I][BadBleWorker] BT RSSI: 0.000000 +254646 [I][BadBleWorker] BLE Key timeout : 16 +254650 [D][BadBleWorker] line:DELAY 500 +254653 [I][BadBleWorker] BLE Key timeout : 16 +254748 [I][SavedStruct] Loading "/int/.desktop.settings" +254790 [I][SavedStruct] Loading "/int/.desktop.settings" +254990 [I][SavedStruct] Loading "/int/.desktop.settings" +255155 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +255159 [I][BadBleWorker] BT RSSI: 0.000000 +255179 [I][BadBleWorker] BT RSSI: 0.000000 +255190 [I][SavedStruct] Loading "/int/.desktop.settings" +255201 [I][BadBleWorker] BT RSSI: 0.000000 +255223 [I][BadBleWorker] BT RSSI: 0.000000 +255243 [I][BadBleWorker] BT RSSI: 0.000000 +255248 [I][SavedStruct] Loading "/int/.desktop.settings" +255265 [I][BadBleWorker] BT RSSI: 0.000000 +255286 [I][BadBleWorker] BT RSSI: 0.000000 +255306 [I][BadBleWorker] BT RSSI: 0.000000 +255326 [I][BadBleWorker] BT RSSI: 0.000000 +255346 [I][BadBleWorker] BT RSSI: 0.000000 +255366 [I][BadBleWorker] BT RSSI: 0.000000 +255386 [I][BadBleWorker] BT RSSI: 0.000000 +255390 [I][SavedStruct] Loading "/int/.desktop.settings" +255407 [I][BadBleWorker] BT RSSI: 0.000000 +255427 [I][BadBleWorker] BT RSSI: 0.000000 +255446 [I][BadBleWorker] BT RSSI: 0.000000 +255466 [I][BadBleWorker] BT RSSI: 0.000000 +255485 [I][BadBleWorker] BT RSSI: 0.000000 +255505 [I][BadBleWorker] BT RSSI: 0.000000 +255525 [I][BadBleWorker] BT RSSI: 0.000000 +255545 [I][BadBleWorker] BT RSSI: 0.000000 +255565 [I][BadBleWorker] BT RSSI: 0.000000 +255585 [I][BadBleWorker] BT RSSI: 0.000000 +255590 [I][SavedStruct] Loading "/int/.desktop.settings" +255607 [I][BadBleWorker] BT RSSI: 0.000000 +255628 [I][BadBleWorker] BT RSSI: 0.000000 +255648 [I][BadBleWorker] BT RSSI: 0.000000 +255668 [I][BadBleWorker] BT RSSI: 0.000000 +255688 [I][BadBleWorker] BT RSSI: 0.000000 +255708 [I][BadBleWorker] BT RSSI: 0.000000 +255728 [I][BadBleWorker] BT RSSI: 0.000000 +255748 [I][SavedStruct] Loading "/int/.desktop.settings" +255750 [I][BadBleWorker] BT RSSI: 0.000000 +255772 [I][BadBleWorker] BT RSSI: 0.000000 +255790 [I][SavedStruct] Loading "/int/.desktop.settings" +255794 [I][BadBleWorker] BT RSSI: 0.000000 +255816 [I][BadBleWorker] BT RSSI: 0.000000 +255836 [I][BadBleWorker] BT RSSI: 0.000000 +255856 [I][BadBleWorker] BT RSSI: 0.000000 +255876 [I][BadBleWorker] BT RSSI: 0.000000 +255896 [I][BadBleWorker] BT RSSI: 0.000000 +255915 [I][BadBleWorker] BT RSSI: 0.000000 +255935 [I][BadBleWorker] BT RSSI: 0.000000 +255955 [I][BadBleWorker] BT RSSI: 0.000000 +255975 [I][BadBleWorker] BT RSSI: 0.000000 +255996 [I][BadBleWorker] BLE Key timeout : 16 +256000 [D][BadBleWorker] line:DELAY 200 +256003 [I][BadBleWorker] BLE Key timeout : 16 +256006 [I][SavedStruct] Loading "/int/.desktop.settings" +256190 [I][SavedStruct] Loading "/int/.desktop.settings" +256207 [D][BadBleWorker] line:ENTER +256209 [I][BadBleWorker] Special key pressed 28 + +256213 [I][BadBleWorker] BT RSSI: 0.000000 +256232 [I][BadBleWorker] BLE Key timeout : 16 +256236 [I][BadBleWorker] BLE Key timeout : 16 +256248 [I][SavedStruct] Loading "/int/.desktop.settings" +256390 [I][SavedStruct] Loading "/int/.desktop.settings" +256590 [I][SavedStruct] Loading "/int/.desktop.settings" +256748 [I][SavedStruct] Loading "/int/.desktop.settings" +256790 [I][SavedStruct] Loading "/int/.desktop.settings" +256990 [I][SavedStruct] Loading "/int/.desktop.settings" +257190 [I][SavedStruct] Loading "/int/.desktop.settings" +257248 [I][SavedStruct] Loading "/int/.desktop.settings" +257390 [I][SavedStruct] Loading "/int/.desktop.settings" +257436 [I][BtGap] Stop advertising +257439 [D][BtGap] terminate success +257442 [E][BtGap] set_non_discoverable failed 12 +257446 [I][SavedStruct] Loading "/int/.desktop.settings" +257590 [I][SavedStruct] Loading "/int/.desktop.settings" +257646 [I][SavedStruct] Loading "/int/.bt.settings" +257659 [I][SavedStruct] Loading "/int/.bt.keys" +257669 [I][FuriHalBt] Disconnect and stop advertising +257671 [I][FuriHalBt] Stop current profile services +257683 [I][FuriHalBt] Stop BLE related RTOS threads +257707 [I][FuriHalBt] Reset SHCI +257790 [I][SavedStruct] Loading "/int/.desktop.settings" +257821 [I][FuriHalBt] Start BT initialization +257826 [I][Core2] Core2 started +257828 [I][Core2] C2 boot completed, mode: Stack +257831 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +257833 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +257837 [I][Core2] Radio stack started +257839 [I][Core2] Flash activity control switched to SEM7 +257841 [I][BtGap] Advertising name: Keyboard +257843 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +257860 [D][BtBatterySvc] Updating power state characteristic +257866 [I][BtSrv] Bt App started +257868 [I][BtGap] Start advertising +257871 [I][BadBleWorker] End +257873 [I][SavedStruct] Loading "/int/.desktop.settings" +257893 [D][BrowserWorker] Start +257909 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +257911 [D][BrowserWorker] Load offset: 0 cnt: 50 +257983 [D][GuiSrv] ViewPort changed while key press 2000D718 -> 20010BD0. Discarding key: Back, type: Short, sequence: 00000031 +257987 [D][GuiSrv] ViewPort changed while key press 2000D718 -> 20010BD0. Sending key: Back, type: Release, sequence: 00000031 to previous view port +258434 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 +258438 [D][BrowserWorker] Load offset: 0 cnt: 50 +278749 [I][Dolphin] Flush stats +278751 [I][SavedStruct] Saving "/int/.dolphin.state" +278762 [D][StorageInt] Device erase: page 2, translated page: cf +278771 [D][StorageInt] Device sync: skipping +278774 [I][DolphinState] State saved +317982 [D][BtGap] set_non_discoverable success +377984 [D][BtGap] set_non_discoverable success + + _.-------.._ -, + .-"```"--..,,_/ /`-, -, \ + .:" /:/ /'\ \ ,_..., `. | | + / ,----/:/ /`\ _\~`_-"` _; + ' / /`"""'\ \ \.~`_-' ,-"'/ + | | | 0 | | .-' ,/` / + | ,..\ \ ,.-"` ,/` / + ; : `/`""\` ,/--==,/-----, + | `-...| -.___-Z:_______J...---; + : ` _-' + _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ +| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| +| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | +|_| |____||___||_| |_| |___||_|_\ \___||____||___| + +Welcome to Flipper Zero Command Line Interface! +Read Manual https://docs.flipperzero.one + +Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) + +>: log +Press CTRL+C to stop... +48839 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 +48842 [D][BrowserWorker] Load offset: 0 cnt: 50 +51685 [D][BrowserWorker] End +51698 [I][BtGap] Stop advertising +51702 [D][BtGap] set_non_discoverable success +51716 [I][SavedStruct] Loading "/int/.desktop.settings" +51843 [I][SavedStruct] Loading "/int/.desktop.settings" +51908 [I][FuriHalBt] Disconnect and stop advertising +51910 [I][FuriHalBt] Stop current profile services +51919 [I][FuriHalBt] Stop BLE related RTOS threads +51943 [I][FuriHalBt] Reset SHCI +52043 [I][SavedStruct] Loading "/int/.desktop.settings" +52057 [I][FuriHalBt] Start BT initialization +52063 [I][Core2] Core2 started +52065 [I][Core2] C2 boot completed, mode: Stack +52068 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +52070 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +52074 [I][Core2] Radio stack started +52077 [I][Core2] Flash activity control switched to SEM7 +52079 [I][BtGap] Advertising name: Keyboard +52082 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +52098 [D][BtBatterySvc] Updating power state characteristic +52104 [I][BtGap] Start advertising +52106 [I][SavedStruct] Loading "/int/.bt.settings" +52124 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +52134 [I][FuriHalBt] Disconnect and stop advertising +52136 [I][BtGap] Stop advertising +52139 [D][BtGap] set_non_discoverable success +52142 [I][FuriHalBt] Stop current profile services +52152 [I][FuriHalBt] Stop BLE related RTOS threads +52177 [I][FuriHalBt] Reset SHCI +52212 [I][SavedStruct] Loading "/int/.desktop.settings" +52243 [I][SavedStruct] Loading "/int/.desktop.settings" +52291 [I][FuriHalBt] Start BT initialization +52296 [I][Core2] Core2 started +52298 [I][Core2] C2 boot completed, mode: Stack +52301 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +52303 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +52308 [I][Core2] Radio stack started +52310 [I][Core2] Flash activity control switched to SEM7 +52314 [I][BtGap] Advertising name: Rumik1 +52316 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +52333 [D][BtBatterySvc] Updating power state characteristic +52342 [I][BtSrv] Bt App started +52343 [I][BtGap] Start advertising +52347 [I][BadBleWorker] Init +52351 [I][SavedStruct] Loading "/int/.desktop.settings" +52378 [I][BadBleWorker] BLE Key timeout : 16 +52443 [I][SavedStruct] Loading "/int/.desktop.settings" +52643 [I][SavedStruct] Loading "/int/.desktop.settings" +52712 [I][SavedStruct] Loading "/int/.desktop.settings" +52843 [I][SavedStruct] Loading "/int/.desktop.settings" +53043 [I][SavedStruct] Loading "/int/.desktop.settings" +53243 [I][SavedStruct] Loading "/int/.desktop.settings" +53861 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +53865 [I][BtGap] Rssi: 302 +53954 [D][BtGap] Slave security initiated +54073 [I][BtGap] Pairing complete +54076 [D][BtBatterySvc] Updating battery level characteristic +54079 [I][BadBleWorker] BLE Key timeout : 16 +54252 [I][BtGap] Rx MTU size: 414 +54822 [I][BtGap] Connection parameters event complete +54824 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +54826 [W][BtGap] Unsupported connection interval. Request connection parameters update +54829 [I][BtGap] Rssi: 290 +54859 [D][BtGap] Connection parameters accepted +55022 [I][BtGap] Connection parameters event complete +55025 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +55027 [I][BtGap] Rssi: 293 +59176 [I][FuriHalBt] Disconnect and stop advertising +59180 [I][BtGap] Stop advertising +59184 [D][BtGap] terminate success +59188 [E][BtGap] set_non_discoverable failed 12 +59193 [I][FuriHalBt] Stop current profile services +59198 [I][BadBleWorker] BLE Key timeout : 16 +59216 [I][FuriHalBt] Stop BLE related RTOS threads +59240 [I][FuriHalBt] Reset SHCI +59354 [I][FuriHalBt] Start BT initialization +59360 [I][Core2] Core2 started +59363 [I][Core2] C2 boot completed, mode: Stack +59366 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +59368 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +59372 [I][Core2] Radio stack started +59375 [I][Core2] Flash activity control switched to SEM7 +59377 [I][BtGap] Advertising name: Kkk +59380 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +59397 [D][BtBatterySvc] Updating power state characteristic +59407 [I][BtGap] Start advertising +60280 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +60282 [I][BtGap] Rssi: 302 +60374 [D][BtGap] Slave security initiated +60495 [I][BtGap] Pairing complete +60498 [D][BtBatterySvc] Updating battery level characteristic +60500 [I][BadBleWorker] BLE Key timeout : 16 +60673 [I][BtGap] Rx MTU size: 414 +61244 [I][BtGap] Connection parameters event complete +61248 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +61252 [W][BtGap] Unsupported connection interval. Request connection parameters update +61258 [I][BtGap] Rssi: 297 +61284 [D][BtGap] Connection parameters accepted +61448 [I][BtGap] Connection parameters event complete +61452 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +61456 [I][BtGap] Rssi: 298 +66066 [I][BtGap] Disconnect from client. Reason: 13 +66069 [I][BadBleWorker] BLE Key timeout : 16 +69485 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +69489 [I][BtGap] Rssi: 297 +69600 [D][BtGap] Slave security initiated +69720 [I][BtGap] Pairing complete +69723 [D][BtBatterySvc] Updating battery level characteristic +69727 [I][BadBleWorker] BLE Key timeout : 16 +69900 [I][BtGap] Rx MTU size: 414 +71400 [I][BtGap] Connection parameters event complete +71402 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +71405 [W][BtGap] Unsupported connection interval. Request connection parameters update +71408 [I][BtGap] Rssi: 296 +71438 [D][BtGap] Connection parameters accepted +71603 [I][BtGap] Connection parameters event complete +71605 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +71609 [I][BtGap] Rssi: 299 +73790 [I][BtGap] Disconnect from client. Reason: 13 +73792 [I][BadBleWorker] BLE Key timeout : 16 +80587 [I][FuriHalBt] Disconnect and stop advertising +80591 [I][BtGap] Stop advertising +80595 [D][BtGap] set_non_discoverable success +80600 [I][FuriHalBt] Stop current profile services +80621 [I][FuriHalBt] Stop BLE related RTOS threads +80647 [I][FuriHalBt] Reset SHCI +80762 [I][FuriHalBt] Start BT initialization +80767 [I][Core2] Core2 started +80769 [I][Core2] C2 boot completed, mode: Stack +80772 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +80774 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +80778 [I][Core2] Radio stack started +80781 [I][Core2] Flash activity control switched to SEM7 +80783 [I][BtGap] Advertising name: Kkk +80786 [I][BtGap] MAC @ : 35:F2:47:26:11:11 +80803 [D][BtBatterySvc] Updating power state characteristic +80813 [I][BtGap] Start advertising +81988 [D][ViewDispatcher] View changed while key press 200101B0 -> 200102B8. Sending key: Back, type: Release, sequence: 00000028 to previous view port +81992 [I][SavedStruct] Loading "/int/.desktop.settings" +82043 [I][SavedStruct] Loading "/int/.desktop.settings" +82243 [I][SavedStruct] Loading "/int/.desktop.settings" +82443 [I][SavedStruct] Loading "/int/.desktop.settings" +82493 [I][SavedStruct] Loading "/int/.desktop.settings" +82643 [I][SavedStruct] Loading "/int/.desktop.settings" +82843 [I][SavedStruct] Loading "/int/.desktop.settings" +82993 [I][SavedStruct] Loading "/int/.desktop.settings" +83043 [I][SavedStruct] Loading "/int/.desktop.settings" +83243 [I][SavedStruct] Loading "/int/.desktop.settings" +83443 [I][SavedStruct] Loading "/int/.desktop.settings" +83493 [I][SavedStruct] Loading "/int/.desktop.settings" +83643 [I][SavedStruct] Loading "/int/.desktop.settings" +83843 [I][SavedStruct] Loading "/int/.desktop.settings" +83993 [I][SavedStruct] Loading "/int/.desktop.settings" +84043 [I][SavedStruct] Loading "/int/.desktop.settings" +84243 [I][SavedStruct] Loading "/int/.desktop.settings" +84443 [I][SavedStruct] Loading "/int/.desktop.settings" +84493 [I][SavedStruct] Loading "/int/.desktop.settings" +84643 [I][SavedStruct] Loading "/int/.desktop.settings" +84843 [I][SavedStruct] Loading "/int/.desktop.settings" +84993 [I][SavedStruct] Loading "/int/.desktop.settings" +85043 [I][SavedStruct] Loading "/int/.desktop.settings" +85243 [I][SavedStruct] Loading "/int/.desktop.settings" +85443 [I][SavedStruct] Loading "/int/.desktop.settings" +85493 [I][SavedStruct] Loading "/int/.desktop.settings" +85643 [I][SavedStruct] Loading "/int/.desktop.settings" +85843 [I][SavedStruct] Loading "/int/.desktop.settings" +85993 [I][SavedStruct] Loading "/int/.desktop.settings" +86043 [I][SavedStruct] Loading "/int/.desktop.settings" +86243 [I][SavedStruct] Loading "/int/.desktop.settings" +86443 [I][SavedStruct] Loading "/int/.desktop.settings" +86493 [I][SavedStruct] Loading "/int/.desktop.settings" +86643 [I][SavedStruct] Loading "/int/.desktop.settings" +86843 [I][SavedStruct] Loading "/int/.desktop.settings" +86993 [I][SavedStruct] Loading "/int/.desktop.settings" +87043 [I][SavedStruct] Loading "/int/.desktop.settings" +87243 [I][SavedStruct] Loading "/int/.desktop.settings" +87443 [I][SavedStruct] Loading "/int/.desktop.settings" +87493 [I][SavedStruct] Loading "/int/.desktop.settings" +87643 [I][SavedStruct] Loading "/int/.desktop.settings" +87843 [I][SavedStruct] Loading "/int/.desktop.settings" +87993 [I][SavedStruct] Loading "/int/.desktop.settings" +88043 [I][SavedStruct] Loading "/int/.desktop.settings" +88243 [I][SavedStruct] Loading "/int/.desktop.settings" +88443 [I][SavedStruct] Loading "/int/.desktop.settings" +88493 [I][SavedStruct] Loading "/int/.desktop.settings" +88643 [I][SavedStruct] Loading "/int/.desktop.settings" +88843 [I][SavedStruct] Loading "/int/.desktop.settings" +88993 [I][SavedStruct] Loading "/int/.desktop.settings" +89043 [I][SavedStruct] Loading "/int/.desktop.settings" +89243 [I][SavedStruct] Loading "/int/.desktop.settings" +89443 [I][SavedStruct] Loading "/int/.desktop.settings" +89493 [I][SavedStruct] Loading "/int/.desktop.settings" +89643 [I][SavedStruct] Loading "/int/.desktop.settings" +89843 [I][SavedStruct] Loading "/int/.desktop.settings" +89993 [I][SavedStruct] Loading "/int/.desktop.settings" +90043 [I][SavedStruct] Loading "/int/.desktop.settings" +90243 [I][SavedStruct] Loading "/int/.desktop.settings" +90443 [I][SavedStruct] Loading "/int/.desktop.settings" +90493 [I][SavedStruct] Loading "/int/.desktop.settings" +90643 [I][SavedStruct] Loading "/int/.desktop.settings" +90843 [I][SavedStruct] Loading "/int/.desktop.settings" +90993 [I][SavedStruct] Loading "/int/.desktop.settings" +91043 [I][SavedStruct] Loading "/int/.desktop.settings" +91243 [I][SavedStruct] Loading "/int/.desktop.settings" +91443 [I][SavedStruct] Loading "/int/.desktop.settings" +91493 [I][SavedStruct] Loading "/int/.desktop.settings" +91643 [I][SavedStruct] Loading "/int/.desktop.settings" +91843 [I][SavedStruct] Loading "/int/.desktop.settings" +91993 [I][SavedStruct] Loading "/int/.desktop.settings" +92043 [I][SavedStruct] Loading "/int/.desktop.settings" +92243 [I][SavedStruct] Loading "/int/.desktop.settings" +92443 [I][SavedStruct] Loading "/int/.desktop.settings" +92493 [I][SavedStruct] Loading "/int/.desktop.settings" +92643 [I][SavedStruct] Loading "/int/.desktop.settings" +92843 [I][SavedStruct] Loading "/int/.desktop.settings" +92993 [I][SavedStruct] Loading "/int/.desktop.settings" +93043 [I][SavedStruct] Loading "/int/.desktop.settings" +93243 [I][SavedStruct] Loading "/int/.desktop.settings" +93443 [I][SavedStruct] Loading "/int/.desktop.settings" +93493 [I][SavedStruct] Loading "/int/.desktop.settings" +93643 [I][SavedStruct] Loading "/int/.desktop.settings" +93843 [I][SavedStruct] Loading "/int/.desktop.settings" +93993 [I][SavedStruct] Loading "/int/.desktop.settings" +94043 [I][SavedStruct] Loading "/int/.desktop.settings" +94243 [I][SavedStruct] Loading "/int/.desktop.settings" +94443 [I][SavedStruct] Loading "/int/.desktop.settings" +94493 [I][SavedStruct] Loading "/int/.desktop.settings" +94643 [I][SavedStruct] Loading "/int/.desktop.settings" +94843 [I][SavedStruct] Loading "/int/.desktop.settings" +94993 [I][SavedStruct] Loading "/int/.desktop.settings" +95043 [I][SavedStruct] Loading "/int/.desktop.settings" +95243 [I][SavedStruct] Loading "/int/.desktop.settings" +95443 [I][SavedStruct] Loading "/int/.desktop.settings" +95493 [I][SavedStruct] Loading "/int/.desktop.settings" +95643 [I][SavedStruct] Loading "/int/.desktop.settings" +95843 [I][SavedStruct] Loading "/int/.desktop.settings" +95993 [I][SavedStruct] Loading "/int/.desktop.settings" +96043 [I][SavedStruct] Loading "/int/.desktop.settings" +96243 [I][SavedStruct] Loading "/int/.desktop.settings" +96443 [I][SavedStruct] Loading "/int/.desktop.settings" +96493 [I][SavedStruct] Loading "/int/.desktop.settings" +96643 [I][SavedStruct] Loading "/int/.desktop.settings" +96846 [I][SavedStruct] Loading "/int/.desktop.settings" +96913 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +96916 [I][BtGap] Rssi: 324 +96993 [I][SavedStruct] Loading "/int/.desktop.settings" +97006 [D][BtGap] Slave security initiated +97043 [I][SavedStruct] Loading "/int/.desktop.settings" +97094 [I][BtGap] Rx MTU size: 414 +97243 [I][SavedStruct] Loading "/int/.desktop.settings" +97443 [I][SavedStruct] Loading "/int/.desktop.settings" +97493 [I][SavedStruct] Loading "/int/.desktop.settings" +97601 [D][BtGap] Bond lost event. Start rebonding +97643 [I][SavedStruct] Loading "/int/.desktop.settings" +97780 [I][BtGap] Verify numeric comparison: 898056 +100222 [I][SavedStruct] Loading "/int/.desktop.settings" +100243 [I][SavedStruct] Loading "/int/.desktop.settings" +100264 [I][SavedStruct] Loading "/int/.desktop.settings" +100443 [I][SavedStruct] Loading "/int/.desktop.settings" +100493 [I][SavedStruct] Loading "/int/.desktop.settings" +100643 [I][SavedStruct] Loading "/int/.desktop.settings" +100657 [I][BtGap] Pairing complete +100661 [D][BtBatterySvc] Updating battery level characteristic +100666 [I][BadBleWorker] BLE Key timeout : 16 +100843 [I][SavedStruct] Loading "/int/.desktop.settings" +100993 [I][SavedStruct] Loading "/int/.desktop.settings" +101043 [I][SavedStruct] Loading "/int/.desktop.settings" +101243 [I][SavedStruct] Loading "/int/.desktop.settings" +101443 [I][SavedStruct] Loading "/int/.desktop.settings" +101493 [I][SavedStruct] Loading "/int/.desktop.settings" +101643 [I][SavedStruct] Loading "/int/.desktop.settings" +101668 [I][BtGap] Connection parameters event complete +101670 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +101673 [W][BtGap] Unsupported connection interval. Request connection parameters update +101677 [I][BtGap] Rssi: 345 +101707 [D][BtGap] Connection parameters accepted +101843 [I][SavedStruct] Loading "/int/.desktop.settings" +101870 [I][BtGap] Connection parameters event complete +101873 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +101875 [I][BtGap] Rssi: 337 +101993 [I][SavedStruct] Loading "/int/.desktop.settings" +102043 [I][SavedStruct] Loading "/int/.desktop.settings" +102243 [I][SavedStruct] Loading "/int/.desktop.settings" +102443 [I][SavedStruct] Loading "/int/.desktop.settings" +102493 [I][SavedStruct] Loading "/int/.desktop.settings" +102643 [I][SavedStruct] Loading "/int/.desktop.settings" +102843 [I][SavedStruct] Loading "/int/.desktop.settings" +102993 [I][SavedStruct] Loading "/int/.desktop.settings" +103043 [I][SavedStruct] Loading "/int/.desktop.settings" +103243 [I][SavedStruct] Loading "/int/.desktop.settings" +103443 [I][SavedStruct] Loading "/int/.desktop.settings" +103493 [I][SavedStruct] Loading "/int/.desktop.settings" +103643 [I][SavedStruct] Loading "/int/.desktop.settings" +103843 [I][SavedStruct] Loading "/int/.desktop.settings" +103993 [I][SavedStruct] Loading "/int/.desktop.settings" +104043 [I][SavedStruct] Loading "/int/.desktop.settings" +104243 [I][SavedStruct] Loading "/int/.desktop.settings" +104443 [I][SavedStruct] Loading "/int/.desktop.settings" +104493 [I][SavedStruct] Loading "/int/.desktop.settings" +104643 [I][SavedStruct] Loading "/int/.desktop.settings" +104843 [I][SavedStruct] Loading "/int/.desktop.settings" +104993 [I][SavedStruct] Loading "/int/.desktop.settings" +105043 [I][SavedStruct] Loading "/int/.desktop.settings" +105243 [I][SavedStruct] Loading "/int/.desktop.settings" +105443 [I][SavedStruct] Loading "/int/.desktop.settings" +105493 [I][SavedStruct] Loading "/int/.desktop.settings" +105643 [I][SavedStruct] Loading "/int/.desktop.settings" +105843 [I][SavedStruct] Loading "/int/.desktop.settings" +105993 [I][SavedStruct] Loading "/int/.desktop.settings" +106043 [I][SavedStruct] Loading "/int/.desktop.settings" +106243 [I][SavedStruct] Loading "/int/.desktop.settings" +106443 [I][SavedStruct] Loading "/int/.desktop.settings" +106493 [I][SavedStruct] Loading "/int/.desktop.settings" +106643 [I][SavedStruct] Loading "/int/.desktop.settings" +106843 [I][SavedStruct] Loading "/int/.desktop.settings" +106993 [I][SavedStruct] Loading "/int/.desktop.settings" +107043 [I][SavedStruct] Loading "/int/.desktop.settings" +107243 [I][SavedStruct] Loading "/int/.desktop.settings" +107443 [I][SavedStruct] Loading "/int/.desktop.settings" +107493 [I][SavedStruct] Loading "/int/.desktop.settings" +107643 [I][SavedStruct] Loading "/int/.desktop.settings" +107843 [I][SavedStruct] Loading "/int/.desktop.settings" +107995 [I][SavedStruct] Loading "/int/.desktop.settings" +108043 [I][SavedStruct] Loading "/int/.desktop.settings" +108243 [I][SavedStruct] Loading "/int/.desktop.settings" +108443 [I][SavedStruct] Loading "/int/.desktop.settings" +108493 [I][SavedStruct] Loading "/int/.desktop.settings" +108643 [I][SavedStruct] Loading "/int/.desktop.settings" +108843 [I][SavedStruct] Loading "/int/.desktop.settings" +108993 [I][SavedStruct] Loading "/int/.desktop.settings" +109043 [I][SavedStruct] Loading "/int/.desktop.settings" +109243 [I][SavedStruct] Loading "/int/.desktop.settings" +109443 [I][SavedStruct] Loading "/int/.desktop.settings" +109536 [D][DolphinState] icounter 1325, butthurt 0 +109539 [I][BadBleWorker] BLE Key timeout : 16 +109543 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +109546 [I][BadBleWorker] BLE Key timeout : 16 +109549 [D][BadBleWorker] line:DELAY 1000 +109552 [I][BadBleWorker] BLE Key timeout : 16 +109643 [I][SavedStruct] Loading "/int/.desktop.settings" +109843 [I][SavedStruct] Loading "/int/.desktop.settings" +110037 [I][SavedStruct] Loading "/int/.desktop.settings" +110055 [I][SavedStruct] Loading "/int/.desktop.settings" +110243 [I][SavedStruct] Loading "/int/.desktop.settings" +110443 [I][SavedStruct] Loading "/int/.desktop.settings" +110537 [I][SavedStruct] Loading "/int/.desktop.settings" +110555 [D][BadBleWorker] line:GUI SPACE +110557 [I][BadBleWorker] Special key pressed 82c + +110578 [I][BadBleWorker] BLE Key timeout : 16 +110582 [D][BadBleWorker] line:DELAY 500 +110585 [I][BadBleWorker] BLE Key timeout : 16 +110643 [I][SavedStruct] Loading "/int/.desktop.settings" +110843 [I][SavedStruct] Loading "/int/.desktop.settings" +111039 [I][SavedStruct] Loading "/int/.desktop.settings" +111066 [I][SavedStruct] Loading "/int/.desktop.settings" +111088 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +111243 [I][SavedStruct] Loading "/int/.desktop.settings" +111443 [I][SavedStruct] Loading "/int/.desktop.settings" +111537 [I][SavedStruct] Loading "/int/.desktop.settings" +111643 [I][SavedStruct] Loading "/int/.desktop.settings" +111792 [E][BtHid] Failed updating report characteristic: 100 +111811 [E][BtHid] Failed updating report characteristic: 100 +111814 [E][BtHid] Failed updating report characteristic: 100 +111833 [E][BtHid] Failed updating report characteristic: 100 +111836 [I][BadBleWorker] BLE Key timeout : 16 +111838 [D][BadBleWorker] line:DELAY 200 +111840 [I][BadBleWorker] BLE Key timeout : 16 +111843 [I][SavedStruct] Loading "/int/.desktop.settings" +112037 [I][SavedStruct] Loading "/int/.desktop.settings" +112044 [D][BadBleWorker] line:ENTER +112046 [I][BadBleWorker] Special key pressed 28 + +112069 [I][BadBleWorker] BLE Key timeout : 16 +112072 [D][BadBleWorker] line:GUI SPACE +112074 [I][BadBleWorker] Special key pressed 82c + +112077 [I][SavedStruct] Loading "/int/.desktop.settings" +112095 [I][BadBleWorker] BLE Key timeout : 16 +112099 [D][BadBleWorker] line:DELAY 500 +112102 [I][BadBleWorker] BLE Key timeout : 16 +112243 [I][SavedStruct] Loading "/int/.desktop.settings" +112443 [I][SavedStruct] Loading "/int/.desktop.settings" +112537 [I][SavedStruct] Loading "/int/.desktop.settings" +112605 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +112643 [I][SavedStruct] Loading "/int/.desktop.settings" +112843 [I][SavedStruct] Loading "/int/.desktop.settings" +113037 [I][SavedStruct] Loading "/int/.desktop.settings" +113055 [I][SavedStruct] Loading "/int/.desktop.settings" +113243 [I][SavedStruct] Loading "/int/.desktop.settings" +113347 [I][BadBleWorker] BLE Key timeout : 16 +113351 [D][BadBleWorker] line:DELAY 200 +113354 [I][BadBleWorker] BLE Key timeout : 16 +113443 [I][SavedStruct] Loading "/int/.desktop.settings" +113537 [I][SavedStruct] Loading "/int/.desktop.settings" +113557 [D][BadBleWorker] line:ENTER +113559 [I][BadBleWorker] Special key pressed 28 + +113579 [I][BadBleWorker] BLE Key timeout : 16 +113583 [D][BadBleWorker] line:GUI SPACE +113585 [I][BadBleWorker] Special key pressed 82c + +113605 [I][BadBleWorker] BLE Key timeout : 16 +113608 [D][BadBleWorker] line:DELAY 500 +113610 [I][BadBleWorker] BLE Key timeout : 16 +113643 [I][SavedStruct] Loading "/int/.desktop.settings" +113843 [I][SavedStruct] Loading "/int/.desktop.settings" +114037 [I][SavedStruct] Loading "/int/.desktop.settings" +114055 [I][SavedStruct] Loading "/int/.desktop.settings" +114113 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +114243 [I][SavedStruct] Loading "/int/.desktop.settings" +114443 [I][SavedStruct] Loading "/int/.desktop.settings" +114537 [I][SavedStruct] Loading "/int/.desktop.settings" +114643 [I][SavedStruct] Loading "/int/.desktop.settings" +114843 [I][SavedStruct] Loading "/int/.desktop.settings" +114853 [I][BadBleWorker] BLE Key timeout : 16 +114861 [D][BadBleWorker] line:DELAY 200 +114865 [I][BadBleWorker] BLE Key timeout : 16 +115037 [I][SavedStruct] Loading "/int/.desktop.settings" +115055 [I][SavedStruct] Loading "/int/.desktop.settings" +115069 [D][BadBleWorker] line:ENTER +115071 [I][BadBleWorker] Special key pressed 28 + +115093 [I][BadBleWorker] BLE Key timeout : 16 +115097 [D][BadBleWorker] line:GUI SPACE +115100 [I][BadBleWorker] Special key pressed 82c + +115122 [I][BadBleWorker] BLE Key timeout : 16 +115126 [D][BadBleWorker] line:DELAY 500 +115129 [I][BadBleWorker] BLE Key timeout : 16 +115243 [I][SavedStruct] Loading "/int/.desktop.settings" +115443 [I][SavedStruct] Loading "/int/.desktop.settings" +115537 [I][SavedStruct] Loading "/int/.desktop.settings" +115632 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +115643 [I][SavedStruct] Loading "/int/.desktop.settings" +115843 [I][SavedStruct] Loading "/int/.desktop.settings" +116037 [I][SavedStruct] Loading "/int/.desktop.settings" +116055 [I][SavedStruct] Loading "/int/.desktop.settings" +116243 [I][SavedStruct] Loading "/int/.desktop.settings" +116369 [I][BadBleWorker] BLE Key timeout : 16 +116372 [D][BadBleWorker] line:DELAY 200 +116374 [I][BadBleWorker] BLE Key timeout : 16 +116443 [I][SavedStruct] Loading "/int/.desktop.settings" +116537 [I][SavedStruct] Loading "/int/.desktop.settings" +116577 [D][BadBleWorker] line:ENTER +116579 [I][BadBleWorker] Special key pressed 28 + +116599 [I][BadBleWorker] BLE Key timeout : 16 +116603 [I][BadBleWorker] BLE Key timeout : 16 +116643 [I][SavedStruct] Loading "/int/.desktop.settings" +116843 [I][SavedStruct] Loading "/int/.desktop.settings" +117037 [I][SavedStruct] Loading "/int/.desktop.settings" +117056 [I][SavedStruct] Loading "/int/.desktop.settings" +117243 [I][SavedStruct] Loading "/int/.desktop.settings" +117443 [I][SavedStruct] Loading "/int/.desktop.settings" +117537 [I][SavedStruct] Loading "/int/.desktop.settings" +117643 [I][SavedStruct] Loading "/int/.desktop.settings" +117843 [I][SavedStruct] Loading "/int/.desktop.settings" +118037 [I][SavedStruct] Loading "/int/.desktop.settings" +118056 [I][SavedStruct] Loading "/int/.desktop.settings" +118243 [I][SavedStruct] Loading "/int/.desktop.settings" +118443 [I][SavedStruct] Loading "/int/.desktop.settings" +118537 [I][SavedStruct] Loading "/int/.desktop.settings" +118643 [I][SavedStruct] Loading "/int/.desktop.settings" +118843 [I][SavedStruct] Loading "/int/.desktop.settings" +119037 [I][SavedStruct] Loading "/int/.desktop.settings" +119056 [I][SavedStruct] Loading "/int/.desktop.settings" +119243 [I][SavedStruct] Loading "/int/.desktop.settings" +119443 [I][SavedStruct] Loading "/int/.desktop.settings" +119537 [I][SavedStruct] Loading "/int/.desktop.settings" +119643 [I][SavedStruct] Loading "/int/.desktop.settings" +119843 [I][SavedStruct] Loading "/int/.desktop.settings" +120037 [I][SavedStruct] Loading "/int/.desktop.settings" +120056 [I][SavedStruct] Loading "/int/.desktop.settings" +120243 [I][SavedStruct] Loading "/int/.desktop.settings" +120443 [I][SavedStruct] Loading "/int/.desktop.settings" +120537 [I][SavedStruct] Loading "/int/.desktop.settings" +120643 [I][SavedStruct] Loading "/int/.desktop.settings" +120843 [I][SavedStruct] Loading "/int/.desktop.settings" +121037 [I][SavedStruct] Loading "/int/.desktop.settings" +121056 [I][SavedStruct] Loading "/int/.desktop.settings" +121243 [I][SavedStruct] Loading "/int/.desktop.settings" +121443 [I][SavedStruct] Loading "/int/.desktop.settings" +121537 [I][SavedStruct] Loading "/int/.desktop.settings" +121643 [I][SavedStruct] Loading "/int/.desktop.settings" +121843 [I][SavedStruct] Loading "/int/.desktop.settings" +122037 [I][SavedStruct] Loading "/int/.desktop.settings" +122056 [I][SavedStruct] Loading "/int/.desktop.settings" +122243 [I][SavedStruct] Loading "/int/.desktop.settings" +122443 [I][SavedStruct] Loading "/int/.desktop.settings" +122537 [I][SavedStruct] Loading "/int/.desktop.settings" +122643 [I][SavedStruct] Loading "/int/.desktop.settings" +122843 [I][SavedStruct] Loading "/int/.desktop.settings" +123037 [I][SavedStruct] Loading "/int/.desktop.settings" +123056 [I][SavedStruct] Loading "/int/.desktop.settings" +123243 [I][SavedStruct] Loading "/int/.desktop.settings" +123443 [I][SavedStruct] Loading "/int/.desktop.settings" +123537 [I][SavedStruct] Loading "/int/.desktop.settings" +123643 [I][SavedStruct] Loading "/int/.desktop.settings" +123843 [I][SavedStruct] Loading "/int/.desktop.settings" +124037 [I][SavedStruct] Loading "/int/.desktop.settings" +124056 [I][SavedStruct] Loading "/int/.desktop.settings" +124243 [I][SavedStruct] Loading "/int/.desktop.settings" +124335 [I][BtGap] Stop advertising +124338 [D][BtGap] terminate success +124341 [E][BtGap] set_non_discoverable failed 12 +124345 [I][SavedStruct] Loading "/int/.desktop.settings" +124443 [I][SavedStruct] Loading "/int/.desktop.settings" +124483 [I][BtGap] Disconnect from client. Reason: 16 +124545 [I][SavedStruct] Loading "/int/.bt.settings" +124558 [I][SavedStruct] Loading "/int/.bt.keys" +124567 [I][FuriHalBt] Disconnect and stop advertising +124571 [I][FuriHalBt] Stop current profile services +124582 [I][FuriHalBt] Stop BLE related RTOS threads +124606 [I][FuriHalBt] Reset SHCI +124643 [I][SavedStruct] Loading "/int/.desktop.settings" +124720 [I][FuriHalBt] Start BT initialization +124725 [I][Core2] Core2 started +124727 [I][Core2] C2 boot completed, mode: Stack +124730 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +124732 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +124736 [I][Core2] Radio stack started +124738 [I][Core2] Flash activity control switched to SEM7 +124740 [I][BtGap] Advertising name: Keyboard +124742 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +124753 [E][BtGap] Message queue get error: -4 +124762 [D][BtBatterySvc] Updating power state characteristic +124768 [I][BtSrv] Bt App started +124770 [I][BtGap] Start advertising +124772 [I][BadBleWorker] End +124774 [I][SavedStruct] Loading "/int/.desktop.settings" +124785 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +124790 [I][BtGap] Rssi: 328 +124801 [D][BrowserWorker] Start +124803 [I][SavedStruct] Loading "/int/.desktop.settings" +124834 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +124836 [D][BrowserWorker] Load offset: 0 cnt: 50 +124880 [D][BtGap] Slave security initiated +124999 [I][BtGap] Pairing complete +125003 [I][BtSrv] Open RPC connection +125006 [D][RpcSrv] Session started +125008 [D][BtBatterySvc] Updating battery level characteristic +125025 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 +125028 [D][BrowserWorker] Load offset: 0 cnt: 50 +125146 [I][BtGap] Rx MTU size: 414 +125268 [D][BrowserWorker] End +125270 [I][SavedStruct] Loading "/int/.desktop.settings" +125315 [I][fap_loader_app] FAP app returned: 0 +125347 [I][LoaderSrv] Application stopped. Free heap: 118720 +125376 [I][AnimationStorage] Custom Manifest selected +126074 [I][AnimationManager] Select 'SPIRAL' animation +126078 [I][AnimationManager] Load animation 'SPIRAL' +126102 [I][SavedStruct] Loading "/int/.desktop.settings" +127063 [D][BtSerialSvc] Received 57 bytes +127066 [D][BtSerialSvc] Available buff size: 967 +127069 [D][RpcStorage] Read +127123 [D][RpcStorage] Stat +127183 [D][RpcStorage] Info +127243 [D][BtSerialSvc] Received 12 bytes +127245 [D][BtSerialSvc] Available buff size: 1012 +127247 [D][RpcStorage] Info +127301 [D][BtSerialSvc] Received 23 bytes +127303 [D][BtSerialSvc] Available buff size: 1001 +127306 [D][RpcSystem] SetDatetime +127359 [D][BtSerialSvc] Received 29 bytes +127361 [D][BtSerialSvc] Available buff size: 995 +127364 [D][RpcStorage] Stat +127446 [D][BtSerialSvc] Received 11 bytes +127448 [D][BtSerialSvc] Available buff size: 1013 +127451 [D][RpcStorage] List +127745 [D][BtSerialSvc] Received 18 bytes +127747 [D][BtSerialSvc] Available buff size: 1006 +127749 [D][RpcStorage] List +127983 [D][BtSerialSvc] Received 18 bytes +127985 [D][BtSerialSvc] Available buff size: 1006 +127988 [D][RpcStorage] List +128041 [D][BtSerialSvc] Received 15 bytes +128043 [D][BtSerialSvc] Available buff size: 1009 +128046 [D][RpcStorage] List +128219 [D][BtSerialSvc] Received 15 bytes +128222 [D][BtSerialSvc] Available buff size: 1009 +128225 [D][RpcStorage] List +128399 [D][BtSerialSvc] Received 20 bytes +128403 [D][BtSerialSvc] Available buff size: 1004 +128408 [D][RpcStorage] List +128576 [D][BtSerialSvc] Received 19 bytes +128578 [D][BtSerialSvc] Available buff size: 1005 +128580 [D][RpcStorage] List +128633 [D][BtSerialSvc] Received 27 bytes +128635 [D][BtSerialSvc] Available buff size: 997 +128639 [D][RpcStorage] Md5sum +129503 [D][BtSerialSvc] Received 37 bytes +129505 [D][BtSerialSvc] Available buff size: 987 +129512 [D][RpcStorage] Md5sum +129591 [D][BtSerialSvc] Received 38 bytes +129593 [D][BtSerialSvc] Available buff size: 986 +129600 [D][RpcStorage] Md5sum +129678 [D][BtSerialSvc] Received 37 bytes +129680 [D][BtSerialSvc] Available buff size: 987 +129683 [D][RpcStorage] Md5sum +129736 [D][BtSerialSvc] Received 38 bytes +129738 [D][BtSerialSvc] Available buff size: 986 +129741 [D][RpcStorage] Md5sum +129794 [D][BtSerialSvc] Received 36 bytes +129796 [D][BtSerialSvc] Available buff size: 988 +129799 [D][RpcStorage] Md5sum +130032 [D][BtSerialSvc] Received 36 bytes +130034 [D][BtSerialSvc] Available buff size: 988 +130037 [D][RpcStorage] Md5sum +130120 [D][BtSerialSvc] Received 36 bytes +130122 [D][BtSerialSvc] Available buff size: 988 +130125 [D][RpcStorage] Md5sum + +>: + _.-------.._ -, + .-"```"--..,,_/ /`-, -, \ + .:" /:/ /'\ \ ,_..., `. | | + / ,----/:/ /`\ _\~`_-"` _; + ' / /`"""'\ \ \.~`_-' ,-"'/ + | | | 0 | | .-' ,/` / + | ,..\ \ ,.-"` ,/` / + ; : `/`""\` ,/--==,/-----, + | `-...| -.___-Z:_______J...---; + : ` _-' + _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ +| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| +| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | +|_| |____||___||_| |_| |___||_|_\ \___||____||___| + +Welcome to Flipper Zero Command Line Interface! +Read Manual https://docs.flipperzero.one + +Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) + +>: log +Press CTRL+C to stop... +25524 [D][BrowserWorker] End +25537 [I][BtGap] Stop advertising +25541 [D][BtGap] set_non_discoverable success +25555 [I][SavedStruct] Loading "/int/.desktop.settings" +25706 [I][SavedStruct] Loading "/int/.desktop.settings" +25747 [I][FuriHalBt] Disconnect and stop advertising +25749 [I][FuriHalBt] Stop current profile services +25758 [I][FuriHalBt] Stop BLE related RTOS threads +25782 [I][FuriHalBt] Reset SHCI +25896 [I][FuriHalBt] Start BT initialization +25901 [I][Core2] Core2 started +25903 [I][Core2] C2 boot completed, mode: Stack +25906 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +25908 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +25912 [I][SavedStruct] Loading "/int/.desktop.settings" +25915 [I][Core2] Radio stack started +25921 [I][Core2] Flash activity control switched to SEM7 +25925 [I][BtGap] Advertising name: Keyboard +25928 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +25950 [D][BtBatterySvc] Updating power state characteristic +25956 [I][BtGap] Start advertising +25958 [I][SavedStruct] Loading "/int/.bt.settings" +25975 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +25984 [I][FuriHalBt] Disconnect and stop advertising +25986 [I][BtGap] Stop advertising +25989 [D][BtGap] set_non_discoverable success +25992 [I][FuriHalBt] Stop current profile services +26001 [I][FuriHalBt] Stop BLE related RTOS threads +26026 [I][FuriHalBt] Reset SHCI +26051 [I][SavedStruct] Loading "/int/.desktop.settings" +26106 [I][SavedStruct] Loading "/int/.desktop.settings" +26140 [I][FuriHalBt] Start BT initialization +26145 [I][Core2] Core2 started +26147 [I][Core2] C2 boot completed, mode: Stack +26150 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +26152 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +26156 [I][Core2] Radio stack started +26159 [I][Core2] Flash activity control switched to SEM7 +26161 [I][BtGap] Advertising name: Rumik1 +26164 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +26181 [D][BtBatterySvc] Updating power state characteristic +26190 [I][BtSrv] Bt App started +26192 [I][BtGap] Start advertising +26195 [I][BadBleWorker] Init +26197 [I][SavedStruct] Loading "/int/.desktop.settings" +26224 [I][BadBleWorker] BLE Key timeout : 16 +26306 [I][SavedStruct] Loading "/int/.desktop.settings" +26506 [I][SavedStruct] Loading "/int/.desktop.settings" +26551 [I][SavedStruct] Loading "/int/.desktop.settings" +26706 [I][SavedStruct] Loading "/int/.desktop.settings" +26909 [I][SavedStruct] Loading "/int/.desktop.settings" +27051 [I][SavedStruct] Loading "/int/.desktop.settings" +27106 [I][SavedStruct] Loading "/int/.desktop.settings" +27306 [I][SavedStruct] Loading "/int/.desktop.settings" +27506 [I][SavedStruct] Loading "/int/.desktop.settings" +27551 [I][SavedStruct] Loading "/int/.desktop.settings" +27706 [I][SavedStruct] Loading "/int/.desktop.settings" +27906 [I][SavedStruct] Loading "/int/.desktop.settings" +28051 [I][SavedStruct] Loading "/int/.desktop.settings" +28106 [I][SavedStruct] Loading "/int/.desktop.settings" +28306 [I][SavedStruct] Loading "/int/.desktop.settings" +28506 [I][SavedStruct] Loading "/int/.desktop.settings" +28551 [I][SavedStruct] Loading "/int/.desktop.settings" +28706 [I][SavedStruct] Loading "/int/.desktop.settings" +28906 [I][SavedStruct] Loading "/int/.desktop.settings" +29051 [I][SavedStruct] Loading "/int/.desktop.settings" +29106 [I][SavedStruct] Loading "/int/.desktop.settings" +29306 [I][SavedStruct] Loading "/int/.desktop.settings" +29506 [I][SavedStruct] Loading "/int/.desktop.settings" +29551 [I][SavedStruct] Loading "/int/.desktop.settings" +29706 [I][SavedStruct] Loading "/int/.desktop.settings" +29906 [I][SavedStruct] Loading "/int/.desktop.settings" +30051 [I][SavedStruct] Loading "/int/.desktop.settings" +30106 [I][SavedStruct] Loading "/int/.desktop.settings" +30306 [I][SavedStruct] Loading "/int/.desktop.settings" +30506 [I][SavedStruct] Loading "/int/.desktop.settings" +30551 [I][SavedStruct] Loading "/int/.desktop.settings" +30706 [I][SavedStruct] Loading "/int/.desktop.settings" +30906 [I][SavedStruct] Loading "/int/.desktop.settings" +31051 [I][SavedStruct] Loading "/int/.desktop.settings" +31106 [I][SavedStruct] Loading "/int/.desktop.settings" +31306 [I][SavedStruct] Loading "/int/.desktop.settings" +31506 [I][SavedStruct] Loading "/int/.desktop.settings" +31551 [I][SavedStruct] Loading "/int/.desktop.settings" +31706 [I][SavedStruct] Loading "/int/.desktop.settings" +31906 [I][SavedStruct] Loading "/int/.desktop.settings" +32051 [I][SavedStruct] Loading "/int/.desktop.settings" +32106 [I][SavedStruct] Loading "/int/.desktop.settings" +32306 [I][SavedStruct] Loading "/int/.desktop.settings" +32506 [I][SavedStruct] Loading "/int/.desktop.settings" +32551 [I][SavedStruct] Loading "/int/.desktop.settings" +32706 [I][SavedStruct] Loading "/int/.desktop.settings" +32906 [I][SavedStruct] Loading "/int/.desktop.settings" +33051 [I][SavedStruct] Loading "/int/.desktop.settings" +33106 [I][SavedStruct] Loading "/int/.desktop.settings" +33306 [I][SavedStruct] Loading "/int/.desktop.settings" +33506 [I][SavedStruct] Loading "/int/.desktop.settings" +33551 [I][SavedStruct] Loading "/int/.desktop.settings" +33706 [I][SavedStruct] Loading "/int/.desktop.settings" +33906 [I][SavedStruct] Loading "/int/.desktop.settings" +34051 [I][SavedStruct] Loading "/int/.desktop.settings" +34106 [I][SavedStruct] Loading "/int/.desktop.settings" +34306 [I][SavedStruct] Loading "/int/.desktop.settings" +34506 [I][SavedStruct] Loading "/int/.desktop.settings" +34551 [I][SavedStruct] Loading "/int/.desktop.settings" +34706 [I][SavedStruct] Loading "/int/.desktop.settings" +34906 [I][SavedStruct] Loading "/int/.desktop.settings" +35051 [I][SavedStruct] Loading "/int/.desktop.settings" +35106 [I][SavedStruct] Loading "/int/.desktop.settings" +35306 [I][SavedStruct] Loading "/int/.desktop.settings" +35506 [I][SavedStruct] Loading "/int/.desktop.settings" +35551 [I][SavedStruct] Loading "/int/.desktop.settings" +35706 [I][SavedStruct] Loading "/int/.desktop.settings" +35906 [I][SavedStruct] Loading "/int/.desktop.settings" +36051 [I][SavedStruct] Loading "/int/.desktop.settings" +36106 [I][SavedStruct] Loading "/int/.desktop.settings" +36306 [I][SavedStruct] Loading "/int/.desktop.settings" +36506 [I][SavedStruct] Loading "/int/.desktop.settings" +36551 [I][SavedStruct] Loading "/int/.desktop.settings" +36706 [I][SavedStruct] Loading "/int/.desktop.settings" +36906 [I][SavedStruct] Loading "/int/.desktop.settings" +37051 [I][SavedStruct] Loading "/int/.desktop.settings" +37106 [I][SavedStruct] Loading "/int/.desktop.settings" +37306 [I][SavedStruct] Loading "/int/.desktop.settings" +37506 [I][SavedStruct] Loading "/int/.desktop.settings" +37551 [I][SavedStruct] Loading "/int/.desktop.settings" +37706 [I][SavedStruct] Loading "/int/.desktop.settings" +37906 [I][SavedStruct] Loading "/int/.desktop.settings" +38053 [I][SavedStruct] Loading "/int/.desktop.settings" +38106 [I][SavedStruct] Loading "/int/.desktop.settings" +38306 [I][SavedStruct] Loading "/int/.desktop.settings" +38506 [I][SavedStruct] Loading "/int/.desktop.settings" +38551 [I][SavedStruct] Loading "/int/.desktop.settings" +38706 [I][SavedStruct] Loading "/int/.desktop.settings" +38906 [I][SavedStruct] Loading "/int/.desktop.settings" +39051 [I][SavedStruct] Loading "/int/.desktop.settings" +39106 [I][SavedStruct] Loading "/int/.desktop.settings" +39256 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +39258 [I][BtGap] Rssi: 334 +39306 [I][SavedStruct] Loading "/int/.desktop.settings" +39344 [D][BtGap] Slave security initiated +39435 [I][BtGap] Rx MTU size: 414 +39506 [I][SavedStruct] Loading "/int/.desktop.settings" +39551 [I][SavedStruct] Loading "/int/.desktop.settings" +39706 [I][SavedStruct] Loading "/int/.desktop.settings" +39906 [I][SavedStruct] Loading "/int/.desktop.settings" +39941 [D][BtGap] Bond lost event. Start rebonding +40051 [I][SavedStruct] Loading "/int/.desktop.settings" +40106 [I][SavedStruct] Loading "/int/.desktop.settings" +40119 [I][BtGap] Verify numeric comparison: 448118 +41993 [I][SavedStruct] Loading "/int/.desktop.settings" +42014 [I][SavedStruct] Loading "/int/.desktop.settings" +42051 [I][SavedStruct] Loading "/int/.desktop.settings" +42106 [I][SavedStruct] Loading "/int/.desktop.settings" +42306 [I][SavedStruct] Loading "/int/.desktop.settings" +42453 [I][BtKeyStorage] Base address: 200301E0. Start update address: 20030938. Size changed: 4 +42456 [I][SavedStruct] Saving "/ext/apps/Tools/.bt_hid.keys" +42458 [I][BtGap] Pairing complete +42506 [I][BtKeyStorage] Base address: 200301E0. Start update address: 20030938. Size changed: 88 +42508 [I][SavedStruct] Saving "/ext/apps/Tools/.bt_hid.keys" +42540 [D][BtGap] RSSI: -81 +42542 [D][BtBatterySvc] Updating battery level characteristic +42544 [I][SavedStruct] Loading "/int/.desktop.settings" +42547 [D][BtGap] RSSI: -81 +42551 [I][BadBleWorker] BLE Key timeout : 33 +42569 [I][SavedStruct] Loading "/int/.desktop.settings" +42706 [I][SavedStruct] Loading "/int/.desktop.settings" +42906 [I][SavedStruct] Loading "/int/.desktop.settings" +43068 [I][SavedStruct] Loading "/int/.desktop.settings" +43106 [I][SavedStruct] Loading "/int/.desktop.settings" +43306 [I][SavedStruct] Loading "/int/.desktop.settings" +43506 [I][SavedStruct] Loading "/int/.desktop.settings" +43514 [I][BtGap] Connection parameters event complete +43518 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +43522 [W][BtGap] Unsupported connection interval. Request connection parameters update +43528 [I][BtGap] Rssi: 358 +43568 [I][SavedStruct] Loading "/int/.desktop.settings" +43570 [D][BtGap] Connection parameters accepted +43706 [I][SavedStruct] Loading "/int/.desktop.settings" +43731 [I][BtGap] Connection parameters event complete +43735 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +43737 [I][BtGap] Rssi: 337 +43906 [I][SavedStruct] Loading "/int/.desktop.settings" +44068 [I][SavedStruct] Loading "/int/.desktop.settings" +44106 [I][SavedStruct] Loading "/int/.desktop.settings" +44306 [I][SavedStruct] Loading "/int/.desktop.settings" +44506 [I][SavedStruct] Loading "/int/.desktop.settings" +44568 [I][SavedStruct] Loading "/int/.desktop.settings" +44706 [I][SavedStruct] Loading "/int/.desktop.settings" +44906 [I][SavedStruct] Loading "/int/.desktop.settings" +45068 [I][SavedStruct] Loading "/int/.desktop.settings" +45106 [I][SavedStruct] Loading "/int/.desktop.settings" +45306 [I][SavedStruct] Loading "/int/.desktop.settings" +45337 [D][DolphinState] icounter 1325, butthurt 0 +45340 [D][BtGap] RSSI: -92 +45341 [I][BadBleWorker] BLE Key timeout : 16 +45346 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +45348 [D][BtGap] RSSI: -92 +45350 [I][BadBleWorker] BLE Key timeout : 16 +45352 [D][BadBleWorker] line:DELAY 1000 +45354 [D][BtGap] RSSI: -92 +45355 [I][BadBleWorker] BLE Key timeout : 16 +45506 [I][SavedStruct] Loading "/int/.desktop.settings" +45706 [I][SavedStruct] Loading "/int/.desktop.settings" +45838 [I][SavedStruct] Loading "/int/.desktop.settings" +45906 [I][SavedStruct] Loading "/int/.desktop.settings" +46106 [I][SavedStruct] Loading "/int/.desktop.settings" +46306 [I][SavedStruct] Loading "/int/.desktop.settings" +46338 [I][SavedStruct] Loading "/int/.desktop.settings" +46357 [D][BadBleWorker] line:GUI SPACE +46359 [I][BadBleWorker] Special key pressed 82c + +46380 [D][BtGap] RSSI: -96 +46382 [I][BadBleWorker] BLE Key timeout : 16 +46385 [D][BadBleWorker] line:DELAY 500 +46388 [D][BtGap] RSSI: -96 +46390 [I][BadBleWorker] BLE Key timeout : 16 +46506 [I][SavedStruct] Loading "/int/.desktop.settings" +46706 [I][SavedStruct] Loading "/int/.desktop.settings" +46838 [I][SavedStruct] Loading "/int/.desktop.settings" +46892 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +46906 [I][SavedStruct] Loading "/int/.desktop.settings" +47106 [I][SavedStruct] Loading "/int/.desktop.settings" +47306 [I][SavedStruct] Loading "/int/.desktop.settings" +47308 [E][BtHid] Failed updating report characteristic: 100 +47313 [E][BtHid] Failed updating report characteristic: 100 +47334 [E][BtHid] Failed updating report characteristic: 100 +47338 [I][SavedStruct] Loading "/int/.desktop.settings" +47340 [E][BtHid] Failed updating report characteristic: 100 +47469 [E][BtHid] Failed updating report characteristic: 100 +47472 [E][BtHid] Failed updating report characteristic: 100 +47491 [E][BtHid] Failed updating report characteristic: 100 +47494 [E][BtHid] Failed updating report characteristic: 100 +47506 [I][SavedStruct] Loading "/int/.desktop.settings" +47513 [E][BtHid] Failed updating report characteristic: 100 +47516 [E][BtHid] Failed updating report characteristic: 100 +47537 [E][BtHid] Failed updating report characteristic: 100 +47540 [E][BtHid] Failed updating report characteristic: 100 +47559 [E][BtHid] Failed updating report characteristic: 100 +47562 [E][BtHid] Failed updating report characteristic: 100 +47581 [E][BtHid] Failed updating report characteristic: 100 +47584 [E][BtHid] Failed updating report characteristic: 100 +47603 [E][BtHid] Failed updating report characteristic: 100 +47606 [E][BtHid] Failed updating report characteristic: 100 +47625 [E][BtHid] Failed updating report characteristic: 100 +47628 [E][BtHid] Failed updating report characteristic: 100 +47682 [D][BtGap] RSSI: -97 +47684 [I][BadBleWorker] BLE Key timeout : 16 +47686 [D][BadBleWorker] line:DELAY 200 +47688 [D][BtGap] RSSI: -97 +47690 [I][BadBleWorker] BLE Key timeout : 16 +47706 [I][SavedStruct] Loading "/int/.desktop.settings" +47838 [I][SavedStruct] Loading "/int/.desktop.settings" +47892 [D][BadBleWorker] line:ENTER +47894 [I][BadBleWorker] Special key pressed 28 + +47906 [I][SavedStruct] Loading "/int/.desktop.settings" +47916 [D][BtGap] RSSI: -91 +47918 [I][BadBleWorker] BLE Key timeout : 16 +47922 [D][BadBleWorker] line:GUI SPACE +47924 [I][BadBleWorker] Special key pressed 82c + +47948 [D][BtGap] RSSI: -91 +47950 [I][BadBleWorker] BLE Key timeout : 16 +47953 [D][BadBleWorker] line:DELAY 500 +47956 [D][BtGap] RSSI: -91 +47958 [I][BadBleWorker] BLE Key timeout : 16 +48106 [I][SavedStruct] Loading "/int/.desktop.settings" +48306 [I][SavedStruct] Loading "/int/.desktop.settings" +48338 [I][SavedStruct] Loading "/int/.desktop.settings" +48460 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +48506 [I][SavedStruct] Loading "/int/.desktop.settings" +48706 [I][SavedStruct] Loading "/int/.desktop.settings" +48787 [E][BtHid] Failed updating report characteristic: 100 +48790 [E][BtHid] Failed updating report characteristic: 100 +48809 [E][BtHid] Failed updating report characteristic: 100 +48812 [E][BtHid] Failed updating report characteristic: 100 +48831 [E][BtHid] Failed updating report characteristic: 100 +48834 [E][BtHid] Failed updating report characteristic: 100 +48838 [I][SavedStruct] Loading "/int/.desktop.settings" +48854 [E][BtHid] Failed updating report characteristic: 100 +48859 [E][BtHid] Failed updating report characteristic: 100 +48878 [E][BtHid] Failed updating report characteristic: 100 +48881 [E][BtHid] Failed updating report characteristic: 100 +48900 [E][BtHid] Failed updating report characteristic: 100 +48903 [E][BtHid] Failed updating report characteristic: 100 +48907 [I][SavedStruct] Loading "/int/.desktop.settings" +48923 [E][BtHid] Failed updating report characteristic: 100 +48928 [E][BtHid] Failed updating report characteristic: 100 +48947 [E][BtHid] Failed updating report characteristic: 100 +48950 [E][BtHid] Failed updating report characteristic: 100 +48969 [E][BtHid] Failed updating report characteristic: 100 +48972 [E][BtHid] Failed updating report characteristic: 100 +48991 [E][BtHid] Failed updating report characteristic: 100 +48994 [E][BtHid] Failed updating report characteristic: 100 +49103 [E][BtHid] Failed updating report characteristic: 100 +49106 [I][SavedStruct] Loading "/int/.desktop.settings" +49123 [E][BtHid] Failed updating report characteristic: 100 +49127 [E][BtHid] Failed updating report characteristic: 100 +49146 [E][BtHid] Failed updating report characteristic: 100 +49149 [E][BtHid] Failed updating report characteristic: 100 +49168 [E][BtHid] Failed updating report characteristic: 100 +49171 [E][BtHid] Failed updating report characteristic: 100 +49190 [E][BtHid] Failed updating report characteristic: 100 +49193 [E][BtHid] Failed updating report characteristic: 100 +49213 [E][BtHid] Failed updating report characteristic: 100 +49218 [E][BtHid] Failed updating report characteristic: 100 +49238 [E][BtHid] Failed updating report characteristic: 100 +49241 [E][BtHid] Failed updating report characteristic: 100 +49260 [E][BtHid] Failed updating report characteristic: 100 +49280 [D][BtGap] RSSI: -90 +49282 [I][BadBleWorker] BLE Key timeout : 16 +49285 [D][BadBleWorker] line:DELAY 200 +49288 [D][BtGap] RSSI: -90 +49290 [I][BadBleWorker] BLE Key timeout : 16 +49306 [I][SavedStruct] Loading "/int/.desktop.settings" +49338 [I][SavedStruct] Loading "/int/.desktop.settings" +49492 [D][BadBleWorker] line:ENTER +49494 [I][BadBleWorker] Special key pressed 28 + +49506 [I][SavedStruct] Loading "/int/.desktop.settings" +49516 [D][BtGap] RSSI: -96 +49518 [I][BadBleWorker] BLE Key timeout : 16 +49527 [D][BadBleWorker] line:GUI SPACE +49529 [I][BadBleWorker] Special key pressed 82c + +49550 [D][BtGap] RSSI: -97 +49552 [I][BadBleWorker] BLE Key timeout : 16 +49555 [D][BadBleWorker] line:DELAY 500 +49557 [D][BtGap] RSSI: -97 +49558 [I][BadBleWorker] BLE Key timeout : 16 +49706 [I][SavedStruct] Loading "/int/.desktop.settings" +49838 [I][SavedStruct] Loading "/int/.desktop.settings" +49906 [I][SavedStruct] Loading "/int/.desktop.settings" +50060 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +50106 [I][SavedStruct] Loading "/int/.desktop.settings" +50306 [I][SavedStruct] Loading "/int/.desktop.settings" +50338 [I][SavedStruct] Loading "/int/.desktop.settings" +50506 [I][SavedStruct] Loading "/int/.desktop.settings" +50706 [I][SavedStruct] Loading "/int/.desktop.settings" +50796 [D][BtGap] RSSI: -91 +50798 [I][BadBleWorker] BLE Key timeout : 16 +50801 [D][BadBleWorker] line:DELAY 200 +50804 [D][BtGap] RSSI: -91 +50806 [I][BadBleWorker] BLE Key timeout : 16 +50838 [I][SavedStruct] Loading "/int/.desktop.settings" +50906 [I][SavedStruct] Loading "/int/.desktop.settings" +51008 [D][BadBleWorker] line:ENTER +51010 [I][BadBleWorker] Special key pressed 28 + +51029 [D][BtGap] RSSI: -95 +51031 [I][BadBleWorker] BLE Key timeout : 16 +51034 [D][BadBleWorker] line:GUI SPACE +51036 [I][BadBleWorker] Special key pressed 82c + +51056 [D][BtGap] RSSI: -85 +51058 [I][BadBleWorker] BLE Key timeout : 33 +51061 [D][BadBleWorker] line:DELAY 500 +51064 [D][BtGap] RSSI: -85 +51066 [I][BadBleWorker] BLE Key timeout : 33 +51106 [I][SavedStruct] Loading "/int/.desktop.settings" +51306 [I][SavedStruct] Loading "/int/.desktop.settings" +51338 [I][SavedStruct] Loading "/int/.desktop.settings" +51506 [I][SavedStruct] Loading "/int/.desktop.settings" +51568 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +51706 [I][SavedStruct] Loading "/int/.desktop.settings" +51838 [I][SavedStruct] Loading "/int/.desktop.settings" +51906 [I][SavedStruct] Loading "/int/.desktop.settings" +52106 [I][SavedStruct] Loading "/int/.desktop.settings" +52306 [I][SavedStruct] Loading "/int/.desktop.settings" +52338 [I][SavedStruct] Loading "/int/.desktop.settings" +52506 [I][SavedStruct] Loading "/int/.desktop.settings" +52706 [I][SavedStruct] Loading "/int/.desktop.settings" +52838 [I][SavedStruct] Loading "/int/.desktop.settings" +52906 [I][SavedStruct] Loading "/int/.desktop.settings" +53007 [D][BtGap] RSSI: -79 +53009 [I][BadBleWorker] BLE Key timeout : 33 +53012 [D][BadBleWorker] line:DELAY 200 +53014 [D][BtGap] RSSI: -79 +53016 [I][BadBleWorker] BLE Key timeout : 33 +53106 [I][SavedStruct] Loading "/int/.desktop.settings" +53218 [D][BadBleWorker] line:ENTER +53220 [I][BadBleWorker] Special key pressed 28 + +53257 [D][BtGap] RSSI: -80 +53259 [I][BadBleWorker] BLE Key timeout : 33 +53263 [D][BtGap] RSSI: -80 +53265 [I][BadBleWorker] BLE Key timeout : 33 +53306 [I][SavedStruct] Loading "/int/.desktop.settings" +53338 [I][SavedStruct] Loading "/int/.desktop.settings" +53506 [I][SavedStruct] Loading "/int/.desktop.settings" +53706 [I][SavedStruct] Loading "/int/.desktop.settings" +53838 [I][SavedStruct] Loading "/int/.desktop.settings" +53906 [I][SavedStruct] Loading "/int/.desktop.settings" +54106 [I][SavedStruct] Loading "/int/.desktop.settings" +54306 [I][SavedStruct] Loading "/int/.desktop.settings" +54338 [I][SavedStruct] Loading "/int/.desktop.settings" +54506 [I][SavedStruct] Loading "/int/.desktop.settings" +54706 [I][SavedStruct] Loading "/int/.desktop.settings" +54838 [I][SavedStruct] Loading "/int/.desktop.settings" +54906 [I][SavedStruct] Loading "/int/.desktop.settings" +55106 [I][SavedStruct] Loading "/int/.desktop.settings" +55306 [I][SavedStruct] Loading "/int/.desktop.settings" +55338 [I][SavedStruct] Loading "/int/.desktop.settings" +55506 [I][SavedStruct] Loading "/int/.desktop.settings" +55706 [I][SavedStruct] Loading "/int/.desktop.settings" +55838 [I][SavedStruct] Loading "/int/.desktop.settings" +55906 [I][SavedStruct] Loading "/int/.desktop.settings" +56106 [I][SavedStruct] Loading "/int/.desktop.settings" +56316 [I][SavedStruct] Loading "/int/.desktop.settings" +56338 [I][SavedStruct] Loading "/int/.desktop.settings" +56506 [I][SavedStruct] Loading "/int/.desktop.settings" +56706 [I][SavedStruct] Loading "/int/.desktop.settings" +56838 [I][SavedStruct] Loading "/int/.desktop.settings" +56906 [I][SavedStruct] Loading "/int/.desktop.settings" +57106 [I][SavedStruct] Loading "/int/.desktop.settings" +57306 [I][SavedStruct] Loading "/int/.desktop.settings" +57338 [I][SavedStruct] Loading "/int/.desktop.settings" +57506 [I][SavedStruct] Loading "/int/.desktop.settings" +57706 [I][SavedStruct] Loading "/int/.desktop.settings" +57838 [I][SavedStruct] Loading "/int/.desktop.settings" +57906 [I][SavedStruct] Loading "/int/.desktop.settings" +58083 [I][BtGap] Stop advertising +58086 [D][BtGap] terminate success +58089 [E][BtGap] set_non_discoverable failed 12 +58093 [I][SavedStruct] Loading "/int/.desktop.settings" +58112 [I][SavedStruct] Loading "/int/.desktop.settings" +58131 [I][BtGap] Disconnect from client. Reason: 16 +58293 [I][SavedStruct] Loading "/int/.bt.settings" +58307 [I][SavedStruct] Loading "/int/.bt.keys" +58317 [I][FuriHalBt] Disconnect and stop advertising +58320 [I][FuriHalBt] Stop current profile services +58324 [I][SavedStruct] Loading "/int/.desktop.settings" +58346 [I][FuriHalBt] Stop BLE related RTOS threads +58374 [I][FuriHalBt] Reset SHCI +58488 [I][FuriHalBt] Start BT initialization +58493 [I][Core2] Core2 started +58495 [I][Core2] C2 boot completed, mode: Stack +58498 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +58500 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +58504 [I][Core2] Radio stack started +58507 [I][Core2] Flash activity control switched to SEM7 +58511 [I][BtGap] Advertising name: Keyboard +58514 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +58518 [I][SavedStruct] Loading "/int/.desktop.settings" +58541 [D][BtBatterySvc] Updating power state characteristic +58547 [I][BtSrv] Bt App started +58548 [I][BtGap] Start advertising +58550 [I][BadBleWorker] End +58552 [I][SavedStruct] Loading "/int/.desktop.settings" +58573 [I][SavedStruct] Loading "/int/.desktop.settings" +58575 [D][BrowserWorker] Start +58608 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +58610 [D][BrowserWorker] Load offset: 0 cnt: 50 +58724 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 +58727 [D][BrowserWorker] Load offset: 0 cnt: 50 +59009 [D][BrowserWorker] End +59011 [I][SavedStruct] Loading "/int/.desktop.settings" +59055 [I][fap_loader_app] FAP app returned: 0 +59091 [I][LoaderSrv] Application stopped. Free heap: 127088 +59120 [I][AnimationStorage] Custom Manifest selected +59411 [I][AnimationManager] Select 'HANDS' animation +59415 [I][AnimationManager] Load animation 'HANDS' +59443 [I][SavedStruct] Loading "/int/.desktop.settings" +75339 [I][Dolphin] Flush stats +75341 [I][SavedStruct] Saving "/int/.dolphin.state" +75351 [D][StorageInt] Device erase: page 9, translated page: d6 +75439 [D][StorageInt] Device sync: skipping +75441 [I][DolphinState] State saved +77873 [I][AnimationStorage] Custom Manifest selected +78353 [I][AnimationManager] Select 'DEDSEC_AD' animation +108394 [I][AnimationStorage] Custom Manifest selected +108758 [I][AnimationManager] Select 'DEDSEC_ASCII' animation +118697 [D][BtGap] set_non_discoverable success +138798 [I][AnimationStorage] Custom Manifest selected +139612 [I][AnimationManager] Select 'MARCUS' animation +169649 [I][AnimationStorage] Custom Manifest selected +170049 [I][AnimationManager] Select 'HANDS' animation +178699 [D][BtGap] set_non_discoverable success +200094 [I][AnimationStorage] Custom Manifest selected +200963 [I][AnimationManager] Select 'SKULL' animation +231003 [I][AnimationStorage] Custom Manifest selected +231753 [I][AnimationManager] Select 'GUNS_CAR' animation +238701 [D][BtGap] set_non_discoverable success +261795 [I][AnimationStorage] Custom Manifest selected +262627 [I][AnimationManager] Select 'DEDSEC_LOGO' animation +292669 [I][AnimationStorage] Custom Manifest selected +293615 [I][AnimationManager] Select 'LOGO_WD2' animation +298703 [D][BtGap] set_non_discoverable success +323656 [I][AnimationStorage] Custom Manifest selected +324243 [I][AnimationManager] Select 'DEDSEC_OLD' animation +354285 [I][AnimationStorage] Custom Manifest selected +355074 [I][AnimationManager] Select 'SPIRAL' animation +358705 [D][BtGap] set_non_discoverable success +385114 [I][AnimationStorage] Custom Manifest selected +385501 [I][AnimationManager] Select 'HANDS' animation +415546 [I][AnimationStorage] Custom Manifest selected +416332 [I][AnimationManager] Select 'SPIRAL' animation +418707 [D][BtGap] set_non_discoverable success +446371 [I][AnimationStorage] Custom Manifest selected +446861 [I][AnimationManager] Select 'DEDSEC_AD' animation +476903 [I][AnimationStorage] Custom Manifest selected +477735 [I][AnimationManager] Select 'DEDSEC_LOGO' animation +478709 [D][BtGap] set_non_discoverable success +507777 [I][AnimationStorage] Custom Manifest selected +508256 [I][AnimationManager] Select 'DEDSEC_AD' animation +538299 [I][AnimationStorage] Custom Manifest selected +539085 [I][AnimationManager] Select 'SPIRAL' animation +539117 [D][BtGap] set_non_discoverable success +569126 [I][AnimationStorage] Custom Manifest selected +569876 [I][AnimationManager] Select 'GUNS_CAR' animation +599120 [D][BtGap] set_non_discoverable success +599918 [I][AnimationStorage] Custom Manifest selected +600830 [I][AnimationManager] Select 'REAPER_ALT' animation +630871 [I][AnimationStorage] Custom Manifest selected +631392 [I][AnimationManager] Select 'DEDSEC_TALK' animation +659122 [D][BtGap] set_non_discoverable success +661435 [I][AnimationStorage] Custom Manifest selected +662238 [I][AnimationManager] Select 'MARCUS' animation +690919 [I][LoaderSrv] Starting: Applications +690925 [I][AnimationManager] Unload animation 'MARCUS' +690948 [D][BrowserWorker] Start +690965 [D][BrowserWorker] Enter folder: /ext/apps items: 6 idx: -1 +690967 [D][BrowserWorker] Load offset: 0 cnt: 50 +717407 [D][BrowserWorker] Enter folder: /ext/apps/Tools items: 23 idx: -1 +717410 [D][BrowserWorker] Load offset: 0 cnt: 50 +718007 [D][BrowserWorker] Exit to: /ext/apps items: 6 idx: 5 +718011 [D][BrowserWorker] Load offset: 0 cnt: 50 +719124 [D][BtGap] set_non_discoverable success +719398 [D][BrowserWorker] Enter folder: /ext/apps/Main items: 8 idx: -1 +719400 [D][BrowserWorker] Load offset: 0 cnt: 50 +720667 [D][BrowserWorker] End +720679 [I][fap_loader_app] FAP Loader is loading /ext/apps/Main/bad_ble.fap +720722 [I][fap_loader_app] FAP Loader is mapping +721451 [I][elf] Total size of loaded sections: 10460 +721453 [I][fap_loader_app] Loaded in 774ms +721455 [I][fap_loader_app] FAP Loader is starting app +721483 [D][BrowserWorker] Start +721498 [D][BrowserWorker] Enter folder: /any/BadUsb items: 10 idx: -1 +721502 [D][BrowserWorker] Load offset: 0 cnt: 50 +724277 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 +724280 [D][BrowserWorker] Load offset: 0 cnt: 50 +737153 [D][BrowserWorker] End +737166 [I][BtGap] Stop advertising +737170 [D][BtGap] set_non_discoverable success +737185 [I][SavedStruct] Loading "/int/.desktop.settings" +737308 [I][SavedStruct] Loading "/int/.desktop.settings" +737327 [I][SavedStruct] Loading "/int/.desktop.settings" +737376 [I][FuriHalBt] Disconnect and stop advertising +737380 [I][FuriHalBt] Stop current profile services +737395 [I][FuriHalBt] Stop BLE related RTOS threads +737419 [I][FuriHalBt] Reset SHCI +737527 [I][SavedStruct] Loading "/int/.desktop.settings" +737533 [I][FuriHalBt] Start BT initialization +737540 [I][Core2] Core2 started +737542 [I][Core2] C2 boot completed, mode: Stack +737546 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +737550 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +737557 [I][Core2] Radio stack started +737559 [I][Core2] Flash activity control switched to SEM7 +737562 [I][BtGap] Advertising name: Keyboard +737565 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +737582 [D][BtBatterySvc] Updating power state characteristic +737587 [I][BtGap] Start advertising +737590 [I][SavedStruct] Loading "/int/.bt.settings" +737607 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +737616 [I][FuriHalBt] Disconnect and stop advertising +737618 [I][BtGap] Stop advertising +737621 [D][BtGap] set_non_discoverable success +737623 [I][FuriHalBt] Stop current profile services +737633 [I][FuriHalBt] Stop BLE related RTOS threads +737641 [I][SavedStruct] Loading "/int/.desktop.settings" +737658 [I][FuriHalBt] Reset SHCI +737683 [I][SavedStruct] Loading "/int/.desktop.settings" +737727 [I][SavedStruct] Loading "/int/.desktop.settings" +737772 [I][FuriHalBt] Start BT initialization +737777 [I][Core2] Core2 started +737779 [I][Core2] C2 boot completed, mode: Stack +737782 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +737784 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +737788 [I][Core2] Radio stack started +737790 [I][Core2] Flash activity control switched to SEM7 +737792 [I][BtGap] Advertising name: Rumik1 +737794 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +737810 [D][BtBatterySvc] Updating power state characteristic +737820 [I][BtSrv] Bt App started +737822 [I][BtGap] Start advertising +737824 [I][BadBleWorker] Init +737828 [I][SavedStruct] Loading "/int/.desktop.settings" +737834 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +737839 [I][BtGap] Rssi: 296 +737863 [D][BtGap] RSSI: -43 +737865 [I][BadBleWorker] BLE Key timeout : 41 +737921 [D][BtGap] Slave security initiated +737927 [I][SavedStruct] Loading "/int/.desktop.settings" +737974 [I][SavedStruct] Loading "/int/.desktop.settings" +738039 [I][BtGap] Pairing complete +738042 [D][BtGap] RSSI: -47 +738044 [D][BtBatterySvc] Updating battery level characteristic +738048 [D][BtGap] RSSI: -47 +738050 [I][BadBleWorker] BLE Key timeout : 41 +738127 [I][SavedStruct] Loading "/int/.desktop.settings" +738183 [I][SavedStruct] Loading "/int/.desktop.settings" +738216 [I][BtGap] Rx MTU size: 414 +738307 [I][SavedStruct] Loading "/int/.desktop.settings" +738327 [I][SavedStruct] Loading "/int/.desktop.settings" +738527 [I][SavedStruct] Loading "/int/.desktop.settings" +738640 [I][SavedStruct] Loading "/int/.desktop.settings" +738683 [I][SavedStruct] Loading "/int/.desktop.settings" +738727 [I][SavedStruct] Loading "/int/.desktop.settings" +738810 [I][BtGap] Connection parameters event complete +738813 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +738815 [W][BtGap] Unsupported connection interval. Request connection parameters update +738819 [I][BtGap] Rssi: 311 +738849 [D][BtGap] Connection parameters accepted +738927 [I][SavedStruct] Loading "/int/.desktop.settings" +738973 [I][SavedStruct] Loading "/int/.desktop.settings" +739010 [I][BtGap] Connection parameters event complete +739012 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +739015 [I][BtGap] Rssi: 328 +739127 [I][SavedStruct] Loading "/int/.desktop.settings" +739183 [I][SavedStruct] Loading "/int/.desktop.settings" +739306 [I][SavedStruct] Loading "/int/.desktop.settings" +739327 [I][SavedStruct] Loading "/int/.desktop.settings" +739527 [I][SavedStruct] Loading "/int/.desktop.settings" +739639 [I][SavedStruct] Loading "/int/.desktop.settings" +739683 [I][SavedStruct] Loading "/int/.desktop.settings" +739727 [I][SavedStruct] Loading "/int/.desktop.settings" +739927 [I][SavedStruct] Loading "/int/.desktop.settings" +739972 [I][SavedStruct] Loading "/int/.desktop.settings" +740127 [I][SavedStruct] Loading "/int/.desktop.settings" +740183 [I][SavedStruct] Loading "/int/.desktop.settings" +740305 [I][SavedStruct] Loading "/int/.desktop.settings" +740327 [I][SavedStruct] Loading "/int/.desktop.settings" +740527 [I][SavedStruct] Loading "/int/.desktop.settings" +740638 [I][SavedStruct] Loading "/int/.desktop.settings" +740683 [I][SavedStruct] Loading "/int/.desktop.settings" +740727 [I][SavedStruct] Loading "/int/.desktop.settings" +740927 [I][SavedStruct] Loading "/int/.desktop.settings" +740971 [I][SavedStruct] Loading "/int/.desktop.settings" +741127 [I][SavedStruct] Loading "/int/.desktop.settings" +741183 [I][SavedStruct] Loading "/int/.desktop.settings" +741304 [I][SavedStruct] Loading "/int/.desktop.settings" +741327 [I][SavedStruct] Loading "/int/.desktop.settings" +741527 [I][SavedStruct] Loading "/int/.desktop.settings" +741637 [I][SavedStruct] Loading "/int/.desktop.settings" +741683 [I][SavedStruct] Loading "/int/.desktop.settings" +741727 [I][SavedStruct] Loading "/int/.desktop.settings" +741927 [I][SavedStruct] Loading "/int/.desktop.settings" +741970 [I][SavedStruct] Loading "/int/.desktop.settings" +742127 [I][SavedStruct] Loading "/int/.desktop.settings" +742183 [I][SavedStruct] Loading "/int/.desktop.settings" +742303 [I][SavedStruct] Loading "/int/.desktop.settings" +742327 [I][SavedStruct] Loading "/int/.desktop.settings" +742527 [I][SavedStruct] Loading "/int/.desktop.settings" +742636 [I][SavedStruct] Loading "/int/.desktop.settings" +742727 [I][SavedStruct] Loading "/int/.desktop.settings" +742927 [I][SavedStruct] Loading "/int/.desktop.settings" +742969 [I][SavedStruct] Loading "/int/.desktop.settings" +743127 [I][SavedStruct] Loading "/int/.desktop.settings" +743302 [I][SavedStruct] Loading "/int/.desktop.settings" +743327 [I][SavedStruct] Loading "/int/.desktop.settings" +743382 [I][SavedStruct] Loading "/int/.desktop.settings" +743527 [I][SavedStruct] Loading "/int/.desktop.settings" +743635 [I][SavedStruct] Loading "/int/.desktop.settings" +743727 [I][SavedStruct] Loading "/int/.desktop.settings" +743882 [I][SavedStruct] Loading "/int/.desktop.settings" +743927 [I][SavedStruct] Loading "/int/.desktop.settings" +743968 [I][SavedStruct] Loading "/int/.desktop.settings" +744127 [I][SavedStruct] Loading "/int/.desktop.settings" +744301 [I][SavedStruct] Loading "/int/.desktop.settings" +744327 [I][SavedStruct] Loading "/int/.desktop.settings" +744382 [I][SavedStruct] Loading "/int/.desktop.settings" +744527 [I][SavedStruct] Loading "/int/.desktop.settings" +744634 [I][SavedStruct] Loading "/int/.desktop.settings" +744727 [I][SavedStruct] Loading "/int/.desktop.settings" +744882 [I][SavedStruct] Loading "/int/.desktop.settings" +744927 [I][SavedStruct] Loading "/int/.desktop.settings" +744967 [I][SavedStruct] Loading "/int/.desktop.settings" +745127 [I][SavedStruct] Loading "/int/.desktop.settings" +745300 [I][SavedStruct] Loading "/int/.desktop.settings" +745327 [I][SavedStruct] Loading "/int/.desktop.settings" +745382 [I][SavedStruct] Loading "/int/.desktop.settings" +745527 [I][SavedStruct] Loading "/int/.desktop.settings" +745633 [I][SavedStruct] Loading "/int/.desktop.settings" +745727 [I][SavedStruct] Loading "/int/.desktop.settings" +745882 [I][SavedStruct] Loading "/int/.desktop.settings" +745927 [I][SavedStruct] Loading "/int/.desktop.settings" +745966 [I][SavedStruct] Loading "/int/.desktop.settings" +746127 [I][SavedStruct] Loading "/int/.desktop.settings" +746299 [I][SavedStruct] Loading "/int/.desktop.settings" +746327 [I][SavedStruct] Loading "/int/.desktop.settings" +746382 [I][SavedStruct] Loading "/int/.desktop.settings" +746527 [I][SavedStruct] Loading "/int/.desktop.settings" +746632 [I][SavedStruct] Loading "/int/.desktop.settings" +746727 [I][SavedStruct] Loading "/int/.desktop.settings" +746882 [I][SavedStruct] Loading "/int/.desktop.settings" +746927 [I][SavedStruct] Loading "/int/.desktop.settings" +746965 [I][SavedStruct] Loading "/int/.desktop.settings" +747127 [I][SavedStruct] Loading "/int/.desktop.settings" +747298 [I][SavedStruct] Loading "/int/.desktop.settings" +747327 [I][SavedStruct] Loading "/int/.desktop.settings" +747453 [D][DolphinState] icounter 1325, butthurt 0 +747456 [D][BtGap] RSSI: -65 +747458 [I][BadBleWorker] BLE Key timeout : 37 +747462 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +747465 [D][BtGap] RSSI: -65 +747467 [I][BadBleWorker] BLE Key timeout : 37 +747470 [D][BadBleWorker] line:DELAY 1000 +747473 [D][BtGap] RSSI: -65 +747475 [I][BadBleWorker] BLE Key timeout : 37 +747527 [I][SavedStruct] Loading "/int/.desktop.settings" +747631 [I][SavedStruct] Loading "/int/.desktop.settings" +747727 [I][SavedStruct] Loading "/int/.desktop.settings" +747927 [I][SavedStruct] Loading "/int/.desktop.settings" +747954 [I][SavedStruct] Loading "/int/.desktop.settings" +747972 [I][SavedStruct] Loading "/int/.desktop.settings" +748127 [I][SavedStruct] Loading "/int/.desktop.settings" +748297 [I][SavedStruct] Loading "/int/.desktop.settings" +748327 [I][SavedStruct] Loading "/int/.desktop.settings" +748454 [I][SavedStruct] Loading "/int/.desktop.settings" +748477 [D][BadBleWorker] line:GUI SPACE +748479 [I][BadBleWorker] Special key pressed 82c + +748520 [D][BtGap] RSSI: -77 +748523 [I][BadBleWorker] BLE Key timeout : 33 +748531 [D][BadBleWorker] line:DELAY 500 +748535 [D][BtGap] RSSI: -77 +748537 [I][BadBleWorker] BLE Key timeout : 33 +748543 [I][SavedStruct] Loading "/int/.desktop.settings" +748630 [I][SavedStruct] Loading "/int/.desktop.settings" +748727 [I][SavedStruct] Loading "/int/.desktop.settings" +748927 [I][SavedStruct] Loading "/int/.desktop.settings" +748954 [I][SavedStruct] Loading "/int/.desktop.settings" +748972 [I][SavedStruct] Loading "/int/.desktop.settings" +749041 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +749127 [I][SavedStruct] Loading "/int/.desktop.settings" +749296 [I][SavedStruct] Loading "/int/.desktop.settings" +749327 [I][SavedStruct] Loading "/int/.desktop.settings" +749454 [I][SavedStruct] Loading "/int/.desktop.settings" +749527 [I][SavedStruct] Loading "/int/.desktop.settings" +749629 [I][SavedStruct] Loading "/int/.desktop.settings" +749727 [I][SavedStruct] Loading "/int/.desktop.settings" +749927 [I][SavedStruct] Loading "/int/.desktop.settings" +749954 [I][SavedStruct] Loading "/int/.desktop.settings" +749972 [I][SavedStruct] Loading "/int/.desktop.settings" +750127 [I][SavedStruct] Loading "/int/.desktop.settings" +750295 [I][SavedStruct] Loading "/int/.desktop.settings" +750327 [I][SavedStruct] Loading "/int/.desktop.settings" +750454 [I][SavedStruct] Loading "/int/.desktop.settings" +750479 [D][BtGap] RSSI: -76 +750481 [I][BadBleWorker] BLE Key timeout : 33 +750484 [D][BadBleWorker] line:DELAY 200 +750487 [D][BtGap] RSSI: -76 +750489 [I][BadBleWorker] BLE Key timeout : 33 +750527 [I][SavedStruct] Loading "/int/.desktop.settings" +750628 [I][SavedStruct] Loading "/int/.desktop.settings" +750692 [D][BadBleWorker] line:ENTER +750694 [I][BadBleWorker] Special key pressed 28 + +750727 [I][SavedStruct] Loading "/int/.desktop.settings" +750733 [D][BtGap] RSSI: -63 +750735 [I][BadBleWorker] BLE Key timeout : 37 +750738 [D][BadBleWorker] line:GUI SPACE +750740 [I][BadBleWorker] Special key pressed 82c + +750782 [D][BtGap] RSSI: -62 +750784 [I][BadBleWorker] BLE Key timeout : 37 +750788 [D][BadBleWorker] line:DELAY 500 +750791 [D][BtGap] RSSI: -62 +750793 [I][BadBleWorker] BLE Key timeout : 37 +750927 [I][SavedStruct] Loading "/int/.desktop.settings" +750954 [I][SavedStruct] Loading "/int/.desktop.settings" +750972 [I][SavedStruct] Loading "/int/.desktop.settings" +751127 [I][SavedStruct] Loading "/int/.desktop.settings" +751294 [I][SavedStruct] Loading "/int/.desktop.settings" +751304 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +751327 [I][SavedStruct] Loading "/int/.desktop.settings" +751454 [I][SavedStruct] Loading "/int/.desktop.settings" +751527 [I][SavedStruct] Loading "/int/.desktop.settings" +751627 [I][SavedStruct] Loading "/int/.desktop.settings" +751727 [I][SavedStruct] Loading "/int/.desktop.settings" +751927 [I][SavedStruct] Loading "/int/.desktop.settings" +751954 [I][SavedStruct] Loading "/int/.desktop.settings" +751972 [I][SavedStruct] Loading "/int/.desktop.settings" +752127 [I][SavedStruct] Loading "/int/.desktop.settings" +752293 [I][SavedStruct] Loading "/int/.desktop.settings" +752327 [I][SavedStruct] Loading "/int/.desktop.settings" +752454 [I][SavedStruct] Loading "/int/.desktop.settings" +752527 [I][SavedStruct] Loading "/int/.desktop.settings" +752626 [I][SavedStruct] Loading "/int/.desktop.settings" +752727 [I][SavedStruct] Loading "/int/.desktop.settings" +752908 [D][BtGap] RSSI: -54 +752910 [I][BadBleWorker] BLE Key timeout : 37 +752914 [D][BadBleWorker] line:DELAY 200 +752917 [D][BtGap] RSSI: -54 +752919 [I][BadBleWorker] BLE Key timeout : 37 +752927 [I][SavedStruct] Loading "/int/.desktop.settings" +752954 [I][SavedStruct] Loading "/int/.desktop.settings" +752972 [I][SavedStruct] Loading "/int/.desktop.settings" +753122 [D][BadBleWorker] line:ENTER +753124 [I][BadBleWorker] Special key pressed 28 + +753127 [I][SavedStruct] Loading "/int/.desktop.settings" +753166 [D][BtGap] RSSI: -54 +753168 [I][BadBleWorker] BLE Key timeout : 37 +753172 [D][BadBleWorker] line:GUI SPACE +753174 [I][BadBleWorker] Special key pressed 82c + +753215 [D][BtGap] RSSI: -55 +753217 [I][BadBleWorker] BLE Key timeout : 37 +753220 [D][BadBleWorker] line:DELAY 500 +753224 [D][BtGap] RSSI: -55 +753226 [I][BadBleWorker] BLE Key timeout : 37 +753292 [I][SavedStruct] Loading "/int/.desktop.settings" +753327 [I][SavedStruct] Loading "/int/.desktop.settings" +753527 [I][SavedStruct] Loading "/int/.desktop.settings" +753625 [I][SavedStruct] Loading "/int/.desktop.settings" +753727 [I][SavedStruct] Loading "/int/.desktop.settings" +753927 [I][SavedStruct] Loading "/int/.desktop.settings" +753958 [I][SavedStruct] Loading "/int/.desktop.settings" +753998 [I][SavedStruct] Loading "/int/.desktop.settings" +754127 [I][SavedStruct] Loading "/int/.desktop.settings" +754146 [I][BtGap] Stop advertising +754149 [D][BtGap] terminate success +754152 [E][BtGap] set_non_discoverable failed 12 +754156 [I][SavedStruct] Loading "/int/.desktop.settings" +754275 [I][BtGap] Disconnect from client. Reason: 16 +754291 [I][SavedStruct] Loading "/int/.desktop.settings" +754327 [I][SavedStruct] Loading "/int/.desktop.settings" +754356 [I][SavedStruct] Loading "/int/.bt.settings" +754370 [I][SavedStruct] Loading "/int/.bt.keys" +754380 [I][FuriHalBt] Disconnect and stop advertising +754382 [I][FuriHalBt] Stop current profile services +754393 [I][FuriHalBt] Stop BLE related RTOS threads +754417 [I][FuriHalBt] Reset SHCI +754527 [I][SavedStruct] Loading "/int/.desktop.settings" +754531 [I][FuriHalBt] Start BT initialization +754538 [I][Core2] Core2 started +754541 [I][Core2] C2 boot completed, mode: Stack +754544 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +754547 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +754554 [I][Core2] Radio stack started +754559 [I][Core2] Flash activity control switched to SEM7 +754563 [I][BtGap] Advertising name: Keyboard +754567 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +754585 [D][BtBatterySvc] Updating power state characteristic +754591 [I][BtSrv] Bt App started +754592 [I][BtGap] Start advertising +754596 [I][BadBleWorker] End +754599 [I][SavedStruct] Loading "/int/.desktop.settings" +754630 [I][SavedStruct] Loading "/int/.desktop.settings" +754634 [D][BrowserWorker] Start +754661 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +754664 [D][BrowserWorker] Load offset: 0 cnt: 50 + + _.-------.._ -, + .-"```"--..,,_/ /`-, -, \ + .:" /:/ /'\ \ ,_..., `. | | + / ,----/:/ /`\ _\~`_-"` _; + ' / /`"""'\ \ \.~`_-' ,-"'/ + | | | 0 | | .-' ,/` / + | ,..\ \ ,.-"` ,/` / + ; : `/`""\` ,/--==,/-----, + | `-...| -.___-Z:_______J...---; + : ` _-' + _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ +| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| +| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | +|_| |____||___||_| |_| |___||_|_\ \___||____||___| + +Welcome to Flipper Zero Command Line Interface! +Read Manual https://docs.flipperzero.one + +Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) + +>: log +Press CTRL+C to stop... +988327 [I][SavedStruct] Loading "/int/.desktop.settings" +988390 [I][SavedStruct] Loading "/int/.desktop.settings" +988527 [I][SavedStruct] Loading "/int/.desktop.settings" +988555 [I][SavedStruct] Loading "/int/.desktop.settings" +988723 [I][SavedStruct] Loading "/int/.desktop.settings" +988742 [I][SavedStruct] Loading "/int/.desktop.settings" +988927 [I][SavedStruct] Loading "/int/.desktop.settings" +989055 [I][SavedStruct] Loading "/int/.desktop.settings" +989074 [I][SavedStruct] Loading "/int/.desktop.settings" +989127 [I][SavedStruct] Loading "/int/.desktop.settings" +989327 [I][SavedStruct] Loading "/int/.desktop.settings" +989389 [I][SavedStruct] Loading "/int/.desktop.settings" +989527 [I][SavedStruct] Loading "/int/.desktop.settings" +989555 [I][SavedStruct] Loading "/int/.desktop.settings" +989722 [I][SavedStruct] Loading "/int/.desktop.settings" +989741 [I][SavedStruct] Loading "/int/.desktop.settings" +989927 [I][SavedStruct] Loading "/int/.desktop.settings" +990055 [I][SavedStruct] Loading "/int/.desktop.settings" +990074 [I][SavedStruct] Loading "/int/.desktop.settings" +990127 [I][SavedStruct] Loading "/int/.desktop.settings" +990327 [I][SavedStruct] Loading "/int/.desktop.settings" +990388 [I][SavedStruct] Loading "/int/.desktop.settings" +990527 [I][SavedStruct] Loading "/int/.desktop.settings" +990558 [I][BtGap] Stop advertising +990561 [D][BtGap] terminate success +990564 [E][BtGap] set_non_discoverable failed 12 +990568 [I][SavedStruct] Loading "/int/.desktop.settings" +990720 [I][BtGap] Disconnect from client. Reason: 16 +990723 [I][SavedStruct] Loading "/int/.desktop.settings" +990743 [I][SavedStruct] Loading "/int/.desktop.settings" +990768 [I][SavedStruct] Loading "/int/.bt.settings" +990782 [I][SavedStruct] Loading "/int/.bt.keys" +990792 [I][FuriHalBt] Disconnect and stop advertising +990794 [I][FuriHalBt] Stop current profile services +990805 [I][FuriHalBt] Stop BLE related RTOS threads +990829 [I][FuriHalBt] Reset SHCI +990927 [I][SavedStruct] Loading "/int/.desktop.settings" +990943 [I][FuriHalBt] Start BT initialization +990949 [I][Core2] Core2 started +990951 [I][Core2] C2 boot completed, mode: Stack +990954 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +990956 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +990960 [I][Core2] Radio stack started +990962 [I][Core2] Flash activity control switched to SEM7 +990964 [I][BtGap] Advertising name: Keyboard +990966 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +990983 [D][BtBatterySvc] Updating power state characteristic +990989 [I][BtSrv] Bt App started +990991 [I][BtGap] Start advertising +990993 [I][BadBleWorker] End +990995 [I][SavedStruct] Loading "/int/.desktop.settings" +991016 [I][SavedStruct] Loading "/int/.desktop.settings" +991020 [D][BrowserWorker] Start +991046 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +991048 [D][BrowserWorker] Load offset: 0 cnt: 50 +995286 [D][BrowserWorker] End +995288 [I][SavedStruct] Loading "/int/.desktop.settings" +995311 [I][BtGap] Stop advertising +995315 [D][BtGap] set_non_discoverable success +995321 [I][SavedStruct] Loading "/int/.desktop.settings" +995342 [I][SavedStruct] Loading "/int/.desktop.settings" +995360 [I][SavedStruct] Loading "/int/.desktop.settings" +995383 [I][SavedStruct] Loading "/int/.desktop.settings" +995520 [I][FuriHalBt] Disconnect and stop advertising +995522 [I][FuriHalBt] Stop current profile services +995527 [I][SavedStruct] Loading "/int/.desktop.settings" +995539 [I][FuriHalBt] Stop BLE related RTOS threads +995565 [I][FuriHalBt] Reset SHCI +995679 [I][FuriHalBt] Start BT initialization +995684 [I][Core2] Core2 started +995686 [I][Core2] C2 boot completed, mode: Stack +995689 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +995691 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +995695 [I][Core2] Radio stack started +995697 [I][Core2] Flash activity control switched to SEM7 +995699 [I][BtGap] Advertising name: Keyboard +995701 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +995712 [E][BtGap] Message queue get error: -4 +995716 [I][SavedStruct] Loading "/int/.desktop.settings" +995728 [D][BtBatterySvc] Updating power state characteristic +995738 [I][BtGap] Start advertising +995741 [I][SavedStruct] Loading "/int/.bt.settings" +995745 [I][SavedStruct] Loading "/int/.desktop.settings" +995773 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +995786 [I][FuriHalBt] Disconnect and stop advertising +995789 [I][BtGap] Stop advertising +995791 [D][BtGap] set_non_discoverable success +995794 [I][FuriHalBt] Stop current profile services +995805 [I][FuriHalBt] Stop BLE related RTOS threads +995830 [I][FuriHalBt] Reset SHCI +995927 [I][SavedStruct] Loading "/int/.desktop.settings" +995944 [I][FuriHalBt] Start BT initialization +995949 [I][Core2] Core2 started +995951 [I][Core2] C2 boot completed, mode: Stack +995954 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +995956 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +995960 [I][Core2] Radio stack started +995962 [I][Core2] Flash activity control switched to SEM7 +995964 [I][BtGap] Advertising name: Rumik1 +995966 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +995984 [D][BtBatterySvc] Updating power state characteristic +995993 [I][BtSrv] Bt App started +995994 [I][BtGap] Start advertising +995998 [I][BadBleWorker] Init +996000 [I][SavedStruct] Loading "/int/.desktop.settings" +996023 [I][BadBleWorker] BLE Key timeout : 16 +996027 [I][BtGap] Stop advertising +996030 [D][BtGap] set_non_discoverable success +996034 [I][SavedStruct] Loading "/int/.desktop.settings" +996052 [I][SavedStruct] Loading "/int/.desktop.settings" +996127 [I][SavedStruct] Loading "/int/.desktop.settings" +996234 [I][SavedStruct] Loading "/int/.bt.settings" +996248 [I][SavedStruct] Loading "/int/.bt.keys" +996258 [I][FuriHalBt] Disconnect and stop advertising +996260 [I][FuriHalBt] Stop current profile services +996271 [I][FuriHalBt] Stop BLE related RTOS threads +996296 [I][FuriHalBt] Reset SHCI +996327 [I][SavedStruct] Loading "/int/.desktop.settings" +996382 [I][SavedStruct] Loading "/int/.desktop.settings" +996410 [I][FuriHalBt] Start BT initialization +996415 [I][Core2] Core2 started +996417 [I][Core2] C2 boot completed, mode: Stack +996420 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +996422 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +996426 [I][Core2] Radio stack started +996428 [I][Core2] Flash activity control switched to SEM7 +996430 [I][BtGap] Advertising name: Keyboard +996432 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +996449 [D][BtBatterySvc] Updating power state characteristic +996456 [I][BtSrv] Bt App started +996458 [I][BtGap] Start advertising +996461 [I][BadBleWorker] End +996464 [I][SavedStruct] Loading "/int/.desktop.settings" +996482 [I][SavedStruct] Loading "/int/.desktop.settings" +996485 [D][BrowserWorker] Start +996510 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 6 +996512 [D][BrowserWorker] Load offset: 0 cnt: 50 +996800 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 +996803 [D][BrowserWorker] Load offset: 0 cnt: 50 +999551 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 +999553 [D][BrowserWorker] Load offset: 0 cnt: 50 +1001657 [D][BrowserWorker] End +1001660 [I][SavedStruct] Loading "/int/.desktop.settings" +1001680 [I][BtGap] Stop advertising +1001685 [D][BtGap] set_non_discoverable success +1001692 [I][SavedStruct] Loading "/int/.desktop.settings" +1001710 [I][SavedStruct] Loading "/int/.desktop.settings" +1001726 [I][SavedStruct] Loading "/int/.desktop.settings" +1001744 [I][SavedStruct] Loading "/int/.desktop.settings" +1001890 [I][FuriHalBt] Disconnect and stop advertising +1001892 [I][FuriHalBt] Stop current profile services +1001902 [I][FuriHalBt] Stop BLE related RTOS threads +1001926 [I][FuriHalBt] Reset SHCI +1001929 [I][SavedStruct] Loading "/int/.desktop.settings" +1002040 [I][FuriHalBt] Start BT initialization +1002044 [I][SavedStruct] Loading "/int/.desktop.settings" +1002046 [I][Core2] Core2 started +1002049 [I][Core2] C2 boot completed, mode: Stack +1002053 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +1002057 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +1002063 [I][Core2] Radio stack started +1002066 [I][Core2] Flash activity control switched to SEM7 +1002070 [I][BtGap] Advertising name: Keyboard +1002074 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +1002095 [D][BtBatterySvc] Updating power state characteristic +1002102 [I][BtGap] Start advertising +1002104 [I][SavedStruct] Loading "/int/.bt.settings" +1002121 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +1002130 [I][FuriHalBt] Disconnect and stop advertising +1002132 [I][BtGap] Stop advertising +1002136 [D][BtGap] set_non_discoverable success +1002139 [I][SavedStruct] Loading "/int/.desktop.settings" +1002141 [I][FuriHalBt] Stop current profile services +1002158 [I][FuriHalBt] Stop BLE related RTOS threads +1002183 [I][FuriHalBt] Reset SHCI +1002209 [I][SavedStruct] Loading "/int/.desktop.settings" +1002297 [I][FuriHalBt] Start BT initialization +1002302 [I][Core2] Core2 started +1002305 [I][Core2] C2 boot completed, mode: Stack +1002308 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +1002310 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +1002314 [I][Core2] Radio stack started +1002316 [I][Core2] Flash activity control switched to SEM7 +1002318 [I][BtGap] Advertising name: Rumik1 +1002321 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +1002327 [I][SavedStruct] Loading "/int/.desktop.settings" +1002348 [D][BtBatterySvc] Updating power state characteristic +1002357 [I][BtSrv] Bt App started +1002359 [I][BtGap] Start advertising +1002362 [I][BadBleWorker] Init +1002364 [I][SavedStruct] Loading "/int/.desktop.settings" +1002393 [I][BadBleWorker] BLE Key timeout : 16 +1002396 [I][SavedStruct] Loading "/int/.desktop.settings" +1002527 [I][SavedStruct] Loading "/int/.desktop.settings" +1002709 [I][SavedStruct] Loading "/int/.desktop.settings" +1002729 [I][SavedStruct] Loading "/int/.desktop.settings" +1002750 [I][SavedStruct] Loading "/int/.desktop.settings" +1002927 [I][SavedStruct] Loading "/int/.desktop.settings" +1002929 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +1002934 [I][BtGap] Rssi: 315 +1003036 [D][BtGap] Slave security initiated +1003042 [I][SavedStruct] Loading "/int/.desktop.settings" +1003127 [I][SavedStruct] Loading "/int/.desktop.settings" +1003155 [I][BtGap] Pairing complete +1003158 [D][BtGap] RSSI: -55 +1003160 [D][BtBatterySvc] Updating battery level characteristic +1003163 [D][BtGap] RSSI: -55 +1003165 [I][BadBleWorker] BLE Key timeout : 37 +1003209 [I][SavedStruct] Loading "/int/.desktop.settings" +1003327 [I][SavedStruct] Loading "/int/.desktop.settings" +1003331 [I][BtGap] Rx MTU size: 414 +1003375 [I][SavedStruct] Loading "/int/.desktop.settings" +1003527 [I][SavedStruct] Loading "/int/.desktop.settings" +1003708 [I][SavedStruct] Loading "/int/.desktop.settings" +1003727 [I][SavedStruct] Loading "/int/.desktop.settings" +1003746 [I][SavedStruct] Loading "/int/.desktop.settings" +1003894 [I][BtGap] Connection parameters event complete +1003896 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +1003899 [W][BtGap] Unsupported connection interval. Request connection parameters update +1003903 [I][BtGap] Rssi: 314 +1003927 [I][SavedStruct] Loading "/int/.desktop.settings" +1003933 [D][BtGap] Connection parameters accepted +1004041 [I][SavedStruct] Loading "/int/.desktop.settings" +1004095 [I][BtGap] Connection parameters event complete +1004097 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +1004100 [I][BtGap] Rssi: 317 +1004127 [I][SavedStruct] Loading "/int/.desktop.settings" +1004225 [I][SavedStruct] Loading "/int/.desktop.settings" +1004327 [I][SavedStruct] Loading "/int/.desktop.settings" +1004374 [I][SavedStruct] Loading "/int/.desktop.settings" +1004527 [I][SavedStruct] Loading "/int/.desktop.settings" +1004707 [I][SavedStruct] Loading "/int/.desktop.settings" +1004726 [I][SavedStruct] Loading "/int/.desktop.settings" +1004745 [I][SavedStruct] Loading "/int/.desktop.settings" +1004927 [I][SavedStruct] Loading "/int/.desktop.settings" +1005040 [I][SavedStruct] Loading "/int/.desktop.settings" +1005127 [I][SavedStruct] Loading "/int/.desktop.settings" +1005225 [I][SavedStruct] Loading "/int/.desktop.settings" +1005327 [I][SavedStruct] Loading "/int/.desktop.settings" +1005373 [I][SavedStruct] Loading "/int/.desktop.settings" +1005527 [I][SavedStruct] Loading "/int/.desktop.settings" +1005706 [I][SavedStruct] Loading "/int/.desktop.settings" +1005727 [I][SavedStruct] Loading "/int/.desktop.settings" +1005800 [D][DolphinState] icounter 1325, butthurt 0 +1005803 [D][BtGap] RSSI: -57 +1005805 [I][BadBleWorker] BLE Key timeout : 37 +1005809 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +1005812 [D][BtGap] RSSI: -57 +1005814 [I][BadBleWorker] BLE Key timeout : 37 +1005817 [D][BadBleWorker] line:DELAY 1000 +1005820 [D][BtGap] RSSI: -57 +1005822 [I][BadBleWorker] BLE Key timeout : 37 +1005927 [I][SavedStruct] Loading "/int/.desktop.settings" +1006039 [I][SavedStruct] Loading "/int/.desktop.settings" +1006127 [I][SavedStruct] Loading "/int/.desktop.settings" +1006301 [I][SavedStruct] Loading "/int/.desktop.settings" +1006327 [I][SavedStruct] Loading "/int/.desktop.settings" +1006372 [I][SavedStruct] Loading "/int/.desktop.settings" +1006527 [I][SavedStruct] Loading "/int/.desktop.settings" +1006705 [I][SavedStruct] Loading "/int/.desktop.settings" +1006727 [I][SavedStruct] Loading "/int/.desktop.settings" +1006801 [I][SavedStruct] Loading "/int/.desktop.settings" +1006824 [D][BadBleWorker] line:GUI SPACE +1006826 [I][BadBleWorker] Special key pressed 82c + +1006867 [D][BtGap] RSSI: -62 +1006869 [I][BadBleWorker] BLE Key timeout : 37 +1006873 [D][BadBleWorker] line:DELAY 500 +1006875 [D][BtGap] RSSI: -62 +1006877 [I][BadBleWorker] BLE Key timeout : 37 +1006927 [I][SavedStruct] Loading "/int/.desktop.settings" +1007039 [I][SavedStruct] Loading "/int/.desktop.settings" +1007127 [I][SavedStruct] Loading "/int/.desktop.settings" +1007301 [I][SavedStruct] Loading "/int/.desktop.settings" +1007327 [I][SavedStruct] Loading "/int/.desktop.settings" +1007371 [I][SavedStruct] Loading "/int/.desktop.settings" +1007388 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +1007527 [I][SavedStruct] Loading "/int/.desktop.settings" +1007704 [I][SavedStruct] Loading "/int/.desktop.settings" +1007727 [I][SavedStruct] Loading "/int/.desktop.settings" +1007801 [I][SavedStruct] Loading "/int/.desktop.settings" +1007927 [I][SavedStruct] Loading "/int/.desktop.settings" +1008037 [I][SavedStruct] Loading "/int/.desktop.settings" +1008127 [I][SavedStruct] Loading "/int/.desktop.settings" +1008301 [I][SavedStruct] Loading "/int/.desktop.settings" +1008327 [I][SavedStruct] Loading "/int/.desktop.settings" +1008370 [I][SavedStruct] Loading "/int/.desktop.settings" +1008527 [I][SavedStruct] Loading "/int/.desktop.settings" +1008703 [I][SavedStruct] Loading "/int/.desktop.settings" +1008727 [I][SavedStruct] Loading "/int/.desktop.settings" +1008801 [I][SavedStruct] Loading "/int/.desktop.settings" +1008927 [I][SavedStruct] Loading "/int/.desktop.settings" +1008996 [D][BtGap] RSSI: -79 +1008997 [I][BadBleWorker] BLE Key timeout : 33 +1008999 [D][BadBleWorker] line:DELAY 200 +1009001 [D][BtGap] RSSI: -74 +1009004 [I][BadBleWorker] BLE Key timeout : 33 +1009036 [I][SavedStruct] Loading "/int/.desktop.settings" +1009127 [I][SavedStruct] Loading "/int/.desktop.settings" +1009207 [D][BadBleWorker] line:ENTER +1009209 [I][BadBleWorker] Special key pressed 28 + +1009246 [D][BtGap] RSSI: -79 +1009248 [I][BadBleWorker] BLE Key timeout : 33 +1009252 [D][BadBleWorker] line:GUI SPACE +1009254 [I][BadBleWorker] Special key pressed 82c + +1009291 [D][BtGap] RSSI: -75 +1009293 [I][BadBleWorker] BLE Key timeout : 33 +1009297 [D][BadBleWorker] line:DELAY 500 +1009299 [D][BtGap] RSSI: -75 +1009302 [I][BadBleWorker] BLE Key timeout : 33 +1009305 [I][SavedStruct] Loading "/int/.desktop.settings" +1009327 [I][SavedStruct] Loading "/int/.desktop.settings" +1009369 [I][SavedStruct] Loading "/int/.desktop.settings" +1009527 [I][SavedStruct] Loading "/int/.desktop.settings" +1009702 [I][SavedStruct] Loading "/int/.desktop.settings" +1009727 [I][SavedStruct] Loading "/int/.desktop.settings" +1009801 [I][SavedStruct] Loading "/int/.desktop.settings" +1009813 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +1009927 [I][SavedStruct] Loading "/int/.desktop.settings" +1010035 [I][SavedStruct] Loading "/int/.desktop.settings" +1010127 [I][SavedStruct] Loading "/int/.desktop.settings" +1010301 [I][SavedStruct] Loading "/int/.desktop.settings" +1010327 [I][SavedStruct] Loading "/int/.desktop.settings" +1010368 [I][SavedStruct] Loading "/int/.desktop.settings" +1010527 [I][SavedStruct] Loading "/int/.desktop.settings" +1010701 [I][SavedStruct] Loading "/int/.desktop.settings" +1010727 [I][SavedStruct] Loading "/int/.desktop.settings" +1010801 [I][SavedStruct] Loading "/int/.desktop.settings" +1010927 [I][SavedStruct] Loading "/int/.desktop.settings" +1011034 [I][SavedStruct] Loading "/int/.desktop.settings" +1011127 [I][SavedStruct] Loading "/int/.desktop.settings" +1011254 [D][BtGap] RSSI: -85 +1011256 [I][BadBleWorker] BLE Key timeout : 33 +1011260 [D][BadBleWorker] line:DELAY 200 +1011262 [D][BtGap] RSSI: -85 +1011264 [I][BadBleWorker] BLE Key timeout : 33 +1011301 [I][SavedStruct] Loading "/int/.desktop.settings" +1011327 [I][SavedStruct] Loading "/int/.desktop.settings" +1011367 [I][SavedStruct] Loading "/int/.desktop.settings" +1011466 [D][BadBleWorker] line:ENTER +1011468 [I][BadBleWorker] Special key pressed 28 + +1011505 [D][BtGap] RSSI: -85 +1011507 [I][BadBleWorker] BLE Key timeout : 33 +1011511 [D][BadBleWorker] line:GUI SPACE +1011513 [I][BadBleWorker] Special key pressed 82c + +1011527 [I][SavedStruct] Loading "/int/.desktop.settings" +1011549 [D][BtGap] RSSI: -85 +1011551 [I][BadBleWorker] BLE Key timeout : 33 +1011555 [D][BadBleWorker] line:DELAY 500 +1011558 [D][BtGap] RSSI: -85 +1011560 [I][BadBleWorker] BLE Key timeout : 33 +1011700 [I][SavedStruct] Loading "/int/.desktop.settings" +1011727 [I][SavedStruct] Loading "/int/.desktop.settings" +1011801 [I][SavedStruct] Loading "/int/.desktop.settings" +1011927 [I][SavedStruct] Loading "/int/.desktop.settings" +1012033 [I][SavedStruct] Loading "/int/.desktop.settings" +1012062 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +1012127 [I][SavedStruct] Loading "/int/.desktop.settings" +1012301 [I][SavedStruct] Loading "/int/.desktop.settings" +1012327 [I][SavedStruct] Loading "/int/.desktop.settings" +1012366 [I][SavedStruct] Loading "/int/.desktop.settings" +1012527 [I][SavedStruct] Loading "/int/.desktop.settings" +1012699 [I][SavedStruct] Loading "/int/.desktop.settings" +1012727 [I][SavedStruct] Loading "/int/.desktop.settings" +1012801 [I][SavedStruct] Loading "/int/.desktop.settings" +1012927 [I][SavedStruct] Loading "/int/.desktop.settings" +1013032 [I][SavedStruct] Loading "/int/.desktop.settings" +1013127 [I][SavedStruct] Loading "/int/.desktop.settings" +1013301 [I][SavedStruct] Loading "/int/.desktop.settings" +1013327 [I][SavedStruct] Loading "/int/.desktop.settings" +1013365 [I][SavedStruct] Loading "/int/.desktop.settings" +1013505 [D][BtGap] RSSI: -91 +1013507 [I][BadBleWorker] BLE Key timeout : 16 +1013511 [D][BadBleWorker] line:DELAY 200 +1013513 [D][BtGap] RSSI: -91 +1013515 [I][BadBleWorker] BLE Key timeout : 16 +1013527 [I][SavedStruct] Loading "/int/.desktop.settings" +1013698 [I][SavedStruct] Loading "/int/.desktop.settings" +1013717 [D][BadBleWorker] line:ENTER +1013719 [I][BadBleWorker] Special key pressed 28 + +1013727 [I][SavedStruct] Loading "/int/.desktop.settings" +1013741 [D][BtGap] RSSI: -99 +1013743 [I][BadBleWorker] BLE Key timeout : 16 +1013749 [D][BadBleWorker] line:GUI SPACE +1013751 [I][BadBleWorker] Special key pressed 82c + +1013770 [D][BtGap] RSSI: -99 +1013772 [I][BadBleWorker] BLE Key timeout : 16 +1013776 [D][BadBleWorker] line:DELAY 500 +1013778 [D][BtGap] RSSI: -99 +1013780 [I][BadBleWorker] BLE Key timeout : 16 +1013801 [I][SavedStruct] Loading "/int/.desktop.settings" +1013927 [I][SavedStruct] Loading "/int/.desktop.settings" +1014031 [I][SavedStruct] Loading "/int/.desktop.settings" +1014134 [I][SavedStruct] Loading "/int/.desktop.settings" +1014282 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +1014301 [I][SavedStruct] Loading "/int/.desktop.settings" +1014327 [I][SavedStruct] Loading "/int/.desktop.settings" +1014364 [I][SavedStruct] Loading "/int/.desktop.settings" +1014527 [I][SavedStruct] Loading "/int/.desktop.settings" +1014593 [E][BtHid] Failed updating report characteristic: 100 +1014596 [E][BtHid] Failed updating report characteristic: 100 +1014616 [E][BtHid] Failed updating report characteristic: 100 +1014619 [E][BtHid] Failed updating report characteristic: 100 +1014640 [E][BtHid] Failed updating report characteristic: 100 +1014643 [E][BtHid] Failed updating report characteristic: 100 +1014664 [E][BtHid] Failed updating report characteristic: 100 +1014667 [E][BtHid] Failed updating report characteristic: 100 +1014688 [E][BtHid] Failed updating report characteristic: 100 +1014691 [E][BtHid] Failed updating report characteristic: 100 +1014697 [I][SavedStruct] Loading "/int/.desktop.settings" +1014712 [E][BtHid] Failed updating report characteristic: 100 +1014717 [E][BtHid] Failed updating report characteristic: 100 +1014727 [I][SavedStruct] Loading "/int/.desktop.settings" +1014739 [E][BtHid] Failed updating report characteristic: 100 +1014744 [E][BtHid] Failed updating report characteristic: 100 +1014765 [E][BtHid] Failed updating report characteristic: 100 +1014768 [E][BtHid] Failed updating report characteristic: 100 +1014789 [E][BtHid] Failed updating report characteristic: 100 +1014792 [E][BtHid] Failed updating report characteristic: 100 +1014801 [I][SavedStruct] Loading "/int/.desktop.settings" +1014814 [E][BtHid] Failed updating report characteristic: 100 +1014819 [E][BtHid] Failed updating report characteristic: 100 +1014840 [E][BtHid] Failed updating report characteristic: 100 +1014843 [E][BtHid] Failed updating report characteristic: 100 +1014862 [E][BtHid] Failed updating report characteristic: 100 +1014865 [E][BtHid] Failed updating report characteristic: 100 +1014885 [E][BtHid] Failed updating report characteristic: 100 +1014888 [E][BtHid] Failed updating report characteristic: 100 +1014907 [E][BtHid] Failed updating report characteristic: 100 +1014910 [E][BtHid] Failed updating report characteristic: 100 +1014927 [I][SavedStruct] Loading "/int/.desktop.settings" +1014931 [E][BtHid] Failed updating report characteristic: 100 +1014936 [E][BtHid] Failed updating report characteristic: 100 +1014957 [E][BtHid] Failed updating report characteristic: 100 +1014960 [E][BtHid] Failed updating report characteristic: 100 +1014981 [E][BtHid] Failed updating report characteristic: 100 +1014984 [E][BtHid] Failed updating report characteristic: 100 +1015004 [E][BtHid] Failed updating report characteristic: 100 +1015007 [E][BtHid] Failed updating report characteristic: 100 +1015027 [E][BtHid] Failed updating report characteristic: 100 +1015031 [E][BtHid] Failed updating report characteristic: 100 +1015033 [I][SavedStruct] Loading "/int/.desktop.settings" +1015051 [E][BtHid] Failed updating report characteristic: 100 +1015055 [E][BtHid] Failed updating report characteristic: 100 +1015075 [E][BtHid] Failed updating report characteristic: 100 +1015078 [E][BtHid] Failed updating report characteristic: 100 +1015099 [E][BtHid] Failed updating report characteristic: 100 +1015102 [E][BtHid] Failed updating report characteristic: 100 +1015121 [E][BtHid] Failed updating report characteristic: 100 +1015124 [E][BtHid] Failed updating report characteristic: 100 +1015129 [I][SavedStruct] Loading "/int/.desktop.settings" +1015146 [E][BtHid] Failed updating report characteristic: 100 +1015153 [E][BtHid] Failed updating report characteristic: 100 +1015174 [E][BtHid] Failed updating report characteristic: 100 +1015177 [D][BtGap] RSSI: -99 +1015179 [I][BadBleWorker] BLE Key timeout : 16 +1015181 [D][BadBleWorker] line:DELAY 200 +1015183 [D][BtGap] RSSI: -99 +1015184 [I][BadBleWorker] BLE Key timeout : 16 +1015301 [I][SavedStruct] Loading "/int/.desktop.settings" +1015327 [I][SavedStruct] Loading "/int/.desktop.settings" +1015363 [I][SavedStruct] Loading "/int/.desktop.settings" +1015386 [D][BadBleWorker] line:ENTER +1015388 [I][BadBleWorker] Special key pressed 28 + +1015391 [E][BtHid] Failed updating report characteristic: 100 +1015410 [D][BtGap] RSSI: -89 +1015412 [I][BadBleWorker] BLE Key timeout : 16 +1015416 [D][BtGap] RSSI: -89 +1015418 [I][BadBleWorker] BLE Key timeout : 16 +1015527 [I][SavedStruct] Loading "/int/.desktop.settings" +1015696 [I][SavedStruct] Loading "/int/.desktop.settings" +1015727 [I][SavedStruct] Loading "/int/.desktop.settings" +1015801 [I][SavedStruct] Loading "/int/.desktop.settings" +1015927 [I][SavedStruct] Loading "/int/.desktop.settings" +1016029 [I][SavedStruct] Loading "/int/.desktop.settings" +1016127 [I][SavedStruct] Loading "/int/.desktop.settings" +1016301 [I][SavedStruct] Loading "/int/.desktop.settings" +1016327 [I][SavedStruct] Loading "/int/.desktop.settings" +1016362 [I][SavedStruct] Loading "/int/.desktop.settings" +1016527 [I][SavedStruct] Loading "/int/.desktop.settings" +1016695 [I][SavedStruct] Loading "/int/.desktop.settings" +1016727 [I][SavedStruct] Loading "/int/.desktop.settings" +1016801 [I][SavedStruct] Loading "/int/.desktop.settings" +1016927 [I][SavedStruct] Loading "/int/.desktop.settings" +1017028 [I][SavedStruct] Loading "/int/.desktop.settings" +1017127 [I][SavedStruct] Loading "/int/.desktop.settings" +1017301 [I][SavedStruct] Loading "/int/.desktop.settings" +1017327 [I][SavedStruct] Loading "/int/.desktop.settings" +1017361 [I][SavedStruct] Loading "/int/.desktop.settings" +1017527 [I][SavedStruct] Loading "/int/.desktop.settings" +1017694 [I][SavedStruct] Loading "/int/.desktop.settings" +1017727 [I][SavedStruct] Loading "/int/.desktop.settings" +1017801 [I][SavedStruct] Loading "/int/.desktop.settings" +1017927 [I][SavedStruct] Loading "/int/.desktop.settings" +1018027 [I][SavedStruct] Loading "/int/.desktop.settings" +1018127 [I][SavedStruct] Loading "/int/.desktop.settings" +1018301 [I][SavedStruct] Loading "/int/.desktop.settings" +1018327 [I][SavedStruct] Loading "/int/.desktop.settings" +1018360 [I][SavedStruct] Loading "/int/.desktop.settings" +1018527 [I][SavedStruct] Loading "/int/.desktop.settings" +1018693 [I][SavedStruct] Loading "/int/.desktop.settings" +1018727 [I][SavedStruct] Loading "/int/.desktop.settings" +1018801 [I][SavedStruct] Loading "/int/.desktop.settings" +1018927 [I][SavedStruct] Loading "/int/.desktop.settings" +1019026 [I][SavedStruct] Loading "/int/.desktop.settings" +1019127 [I][SavedStruct] Loading "/int/.desktop.settings" +1019301 [I][SavedStruct] Loading "/int/.desktop.settings" +1019327 [I][SavedStruct] Loading "/int/.desktop.settings" +1019359 [I][SavedStruct] Loading "/int/.desktop.settings" +1019527 [I][SavedStruct] Loading "/int/.desktop.settings" +1019692 [I][SavedStruct] Loading "/int/.desktop.settings" +1019727 [I][SavedStruct] Loading "/int/.desktop.settings" +1019801 [I][SavedStruct] Loading "/int/.desktop.settings" +1019927 [I][SavedStruct] Loading "/int/.desktop.settings" +1020025 [I][SavedStruct] Loading "/int/.desktop.settings" +1020127 [I][SavedStruct] Loading "/int/.desktop.settings" +1020301 [I][SavedStruct] Loading "/int/.desktop.settings" +1020327 [I][SavedStruct] Loading "/int/.desktop.settings" +1020358 [I][SavedStruct] Loading "/int/.desktop.settings" +1020527 [I][SavedStruct] Loading "/int/.desktop.settings" +1020691 [I][SavedStruct] Loading "/int/.desktop.settings" +1020727 [I][SavedStruct] Loading "/int/.desktop.settings" +1020801 [I][SavedStruct] Loading "/int/.desktop.settings" +1020927 [I][SavedStruct] Loading "/int/.desktop.settings" +1021024 [I][SavedStruct] Loading "/int/.desktop.settings" +1021127 [I][SavedStruct] Loading "/int/.desktop.settings" +1021301 [I][SavedStruct] Loading "/int/.desktop.settings" +1021327 [I][SavedStruct] Loading "/int/.desktop.settings" +1021357 [I][SavedStruct] Loading "/int/.desktop.settings" +1021527 [I][SavedStruct] Loading "/int/.desktop.settings" +1021690 [I][SavedStruct] Loading "/int/.desktop.settings" +1021727 [I][SavedStruct] Loading "/int/.desktop.settings" +1021801 [I][SavedStruct] Loading "/int/.desktop.settings" +1021927 [I][SavedStruct] Loading "/int/.desktop.settings" +1022023 [I][SavedStruct] Loading "/int/.desktop.settings" +1022127 [I][SavedStruct] Loading "/int/.desktop.settings" +1022301 [I][SavedStruct] Loading "/int/.desktop.settings" +1022327 [I][SavedStruct] Loading "/int/.desktop.settings" +1022356 [I][SavedStruct] Loading "/int/.desktop.settings" + +>: + _.-------.._ -, + .-"```"--..,,_/ /`-, -, \ + .:" /:/ /'\ \ ,_..., `. | | + / ,----/:/ /`\ _\~`_-"` _; + ' / /`"""'\ \ \.~`_-' ,-"'/ + | | | 0 | | .-' ,/` / + | ,..\ \ ,.-"` ,/` / + ; : `/`""\` ,/--==,/-----, + | `-...| -.___-Z:_______J...---; + : ` _-' + _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ +| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| +| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | +|_| |____||___||_| |_| |___||_|_\ \___||____||___| + +Welcome to Flipper Zero Command Line Interface! +Read Manual https://docs.flipperzero.one + +Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) + +>: log +Press CTRL+C to stop... +27492 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 +27494 [D][BrowserWorker] Load offset: 0 cnt: 50 +28939 [D][BrowserWorker] End +28952 [I][BtGap] Stop advertising +28956 [D][BtGap] set_non_discoverable success +28970 [I][SavedStruct] Loading "/int/.desktop.settings" +29075 [I][SavedStruct] Loading "/int/.desktop.settings" +29162 [I][FuriHalBt] Disconnect and stop advertising +29164 [I][FuriHalBt] Stop current profile services +29173 [I][FuriHalBt] Stop BLE related RTOS threads +29197 [I][FuriHalBt] Reset SHCI +29275 [I][SavedStruct] Loading "/int/.desktop.settings" +29311 [I][FuriHalBt] Start BT initialization +29316 [I][Core2] Core2 started +29318 [I][Core2] C2 boot completed, mode: Stack +29321 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +29323 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +29327 [I][Core2] Radio stack started +29330 [I][Core2] Flash activity control switched to SEM7 +29332 [I][BtGap] Advertising name: Keyboard +29334 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +29350 [D][BtBatterySvc] Updating power state characteristic +29356 [I][BtGap] Start advertising +29358 [I][SavedStruct] Loading "/int/.bt.settings" +29376 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" +29385 [I][FuriHalBt] Disconnect and stop advertising +29387 [I][BtGap] Stop advertising +29390 [D][BtGap] set_non_discoverable success +29392 [I][FuriHalBt] Stop current profile services +29402 [I][FuriHalBt] Stop BLE related RTOS threads +29427 [I][FuriHalBt] Reset SHCI +29466 [I][SavedStruct] Loading "/int/.desktop.settings" +29482 [I][SavedStruct] Loading "/int/.desktop.settings" +29541 [I][FuriHalBt] Start BT initialization +29546 [I][Core2] Core2 started +29548 [I][Core2] C2 boot completed, mode: Stack +29551 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +29553 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +29557 [I][Core2] Radio stack started +29560 [I][Core2] Flash activity control switched to SEM7 +29562 [I][BtGap] Advertising name: Rumik1 +29565 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 +29582 [D][BtBatterySvc] Updating power state characteristic +29591 [I][BtSrv] Bt App started +29593 [I][BtGap] Start advertising +29596 [I][BadBleWorker] Init +29598 [I][SavedStruct] Loading "/int/.desktop.settings" +29625 [I][BadBleWorker] BLE Key timeout : 16 +29675 [I][SavedStruct] Loading "/int/.desktop.settings" +29875 [I][SavedStruct] Loading "/int/.desktop.settings" +29966 [I][SavedStruct] Loading "/int/.desktop.settings" +30075 [I][SavedStruct] Loading "/int/.desktop.settings" +30275 [I][SavedStruct] Loading "/int/.desktop.settings" +30466 [I][SavedStruct] Loading "/int/.desktop.settings" +30486 [I][SavedStruct] Loading "/int/.desktop.settings" +30675 [I][SavedStruct] Loading "/int/.desktop.settings" +30726 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 +30839 [D][BtGap] Slave security initiated +30875 [I][SavedStruct] Loading "/int/.desktop.settings" +30966 [I][SavedStruct] Loading "/int/.desktop.settings" +31018 [I][BtGap] Pairing complete +31021 [D][BtGap] RSSI: -47 +31023 [D][BtBatterySvc] Updating battery level characteristic +31026 [D][BtGap] RSSI: -47 +31028 [I][BadBleWorker] BLE Key timeout : 33 +31075 [I][SavedStruct] Loading "/int/.desktop.settings" +31136 [I][BtGap] Rx MTU size: 414 +31275 [I][SavedStruct] Loading "/int/.desktop.settings" +31466 [I][SavedStruct] Loading "/int/.desktop.settings" +31484 [I][SavedStruct] Loading "/int/.desktop.settings" +31675 [I][SavedStruct] Loading "/int/.desktop.settings" +31702 [I][BtGap] Connection parameters event complete +31704 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 +31706 [W][BtGap] Unsupported connection interval. Request connection parameters update +31739 [D][BtGap] Connection parameters accepted +31875 [I][SavedStruct] Loading "/int/.desktop.settings" +31902 [I][BtGap] Connection parameters event complete +31906 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 +31968 [I][SavedStruct] Loading "/int/.desktop.settings" +32075 [I][SavedStruct] Loading "/int/.desktop.settings" +32275 [I][SavedStruct] Loading "/int/.desktop.settings" +32466 [I][SavedStruct] Loading "/int/.desktop.settings" +32484 [I][SavedStruct] Loading "/int/.desktop.settings" +32675 [I][SavedStruct] Loading "/int/.desktop.settings" +32875 [I][SavedStruct] Loading "/int/.desktop.settings" +32966 [I][SavedStruct] Loading "/int/.desktop.settings" +33075 [I][SavedStruct] Loading "/int/.desktop.settings" +33275 [I][SavedStruct] Loading "/int/.desktop.settings" +33466 [I][SavedStruct] Loading "/int/.desktop.settings" +33484 [I][SavedStruct] Loading "/int/.desktop.settings" +33675 [I][SavedStruct] Loading "/int/.desktop.settings" +33875 [I][SavedStruct] Loading "/int/.desktop.settings" +33966 [I][SavedStruct] Loading "/int/.desktop.settings" +34075 [I][SavedStruct] Loading "/int/.desktop.settings" +34275 [I][SavedStruct] Loading "/int/.desktop.settings" +34466 [I][SavedStruct] Loading "/int/.desktop.settings" +34484 [I][SavedStruct] Loading "/int/.desktop.settings" +34675 [I][SavedStruct] Loading "/int/.desktop.settings" +34875 [I][SavedStruct] Loading "/int/.desktop.settings" +34966 [I][SavedStruct] Loading "/int/.desktop.settings" +35075 [I][SavedStruct] Loading "/int/.desktop.settings" +35275 [I][SavedStruct] Loading "/int/.desktop.settings" +35359 [D][DolphinState] icounter 1325, butthurt 0 +35362 [D][BtGap] RSSI: -53 +35363 [I][BadBleWorker] BLE Key timeout : 33 +35367 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone +35370 [D][BtGap] RSSI: -53 +35371 [I][BadBleWorker] BLE Key timeout : 33 +35373 [D][BadBleWorker] line:DELAY 1000 +35375 [D][BtGap] RSSI: -53 +35376 [I][BadBleWorker] BLE Key timeout : 33 +35475 [I][SavedStruct] Loading "/int/.desktop.settings" +35675 [I][SavedStruct] Loading "/int/.desktop.settings" +35860 [I][SavedStruct] Loading "/int/.desktop.settings" +35878 [I][SavedStruct] Loading "/int/.desktop.settings" +36075 [I][SavedStruct] Loading "/int/.desktop.settings" +36275 [I][SavedStruct] Loading "/int/.desktop.settings" +36360 [I][SavedStruct] Loading "/int/.desktop.settings" +36378 [D][BadBleWorker] line:GUI SPACE +36380 [I][BadBleWorker] Special key pressed 82c + +36418 [D][BtGap] RSSI: -57 +36420 [I][BadBleWorker] BLE Key timeout : 33 +36423 [D][BadBleWorker] line:DELAY 500 +36426 [D][BtGap] RSSI: -57 +36428 [I][BadBleWorker] BLE Key timeout : 33 +36475 [I][SavedStruct] Loading "/int/.desktop.settings" +36675 [I][SavedStruct] Loading "/int/.desktop.settings" +36860 [I][SavedStruct] Loading "/int/.desktop.settings" +36878 [I][SavedStruct] Loading "/int/.desktop.settings" +36930 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +37075 [I][SavedStruct] Loading "/int/.desktop.settings" +37275 [I][SavedStruct] Loading "/int/.desktop.settings" +37360 [I][SavedStruct] Loading "/int/.desktop.settings" +37475 [I][SavedStruct] Loading "/int/.desktop.settings" +37675 [I][SavedStruct] Loading "/int/.desktop.settings" +37860 [I][SavedStruct] Loading "/int/.desktop.settings" +37878 [I][SavedStruct] Loading "/int/.desktop.settings" +38075 [I][SavedStruct] Loading "/int/.desktop.settings" +38275 [I][SavedStruct] Loading "/int/.desktop.settings" +38360 [I][SavedStruct] Loading "/int/.desktop.settings" +38370 [D][BtGap] RSSI: -71 +38372 [I][BadBleWorker] BLE Key timeout : 37 +38376 [D][BadBleWorker] line:DELAY 200 +38380 [D][BtGap] RSSI: -74 +38382 [I][BadBleWorker] BLE Key timeout : 37 +38475 [I][SavedStruct] Loading "/int/.desktop.settings" +38586 [D][BadBleWorker] line:ENTER +38588 [I][BadBleWorker] Special key pressed 28 + +38629 [D][BtGap] RSSI: -88 +38631 [I][BadBleWorker] BLE Key timeout : 41 +38633 [D][BadBleWorker] line:GUI SPACE +38635 [I][BadBleWorker] Special key pressed 82c + +38675 [I][SavedStruct] Loading "/int/.desktop.settings" +38682 [D][BtGap] RSSI: -71 +38684 [I][BadBleWorker] BLE Key timeout : 37 +38688 [D][BadBleWorker] line:DELAY 500 +38692 [D][BtGap] RSSI: -71 +38694 [I][BadBleWorker] BLE Key timeout : 37 +38860 [I][SavedStruct] Loading "/int/.desktop.settings" +38878 [I][SavedStruct] Loading "/int/.desktop.settings" +39075 [I][SavedStruct] Loading "/int/.desktop.settings" +39198 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +39275 [I][SavedStruct] Loading "/int/.desktop.settings" +39360 [I][SavedStruct] Loading "/int/.desktop.settings" +39475 [I][SavedStruct] Loading "/int/.desktop.settings" +39675 [I][SavedStruct] Loading "/int/.desktop.settings" +39860 [I][SavedStruct] Loading "/int/.desktop.settings" +39878 [I][SavedStruct] Loading "/int/.desktop.settings" +40086 [I][SavedStruct] Loading "/int/.desktop.settings" +40275 [I][SavedStruct] Loading "/int/.desktop.settings" +40360 [I][SavedStruct] Loading "/int/.desktop.settings" +40475 [I][SavedStruct] Loading "/int/.desktop.settings" +40675 [I][SavedStruct] Loading "/int/.desktop.settings" +40803 [D][BtGap] RSSI: -98 +40805 [I][BadBleWorker] BLE Key timeout : 41 +40809 [D][BadBleWorker] line:DELAY 200 +40812 [D][BtGap] RSSI: -98 +40814 [I][BadBleWorker] BLE Key timeout : 41 +40860 [I][SavedStruct] Loading "/int/.desktop.settings" +40878 [I][SavedStruct] Loading "/int/.desktop.settings" +41017 [D][BadBleWorker] line:ENTER +41019 [I][BadBleWorker] Special key pressed 28 + +41063 [D][BtGap] RSSI: -89 +41065 [I][BadBleWorker] BLE Key timeout : 41 +41068 [D][BadBleWorker] line:GUI SPACE +41070 [I][BadBleWorker] Special key pressed 82c + +41075 [I][SavedStruct] Loading "/int/.desktop.settings" +41115 [D][BtGap] RSSI: -91 +41117 [I][BadBleWorker] BLE Key timeout : 41 +41119 [D][BadBleWorker] line:DELAY 500 +41121 [D][BtGap] RSSI: -91 +41122 [I][BadBleWorker] BLE Key timeout : 41 +41275 [I][SavedStruct] Loading "/int/.desktop.settings" +41360 [I][SavedStruct] Loading "/int/.desktop.settings" +41475 [I][SavedStruct] Loading "/int/.desktop.settings" +41624 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +41675 [I][SavedStruct] Loading "/int/.desktop.settings" +41860 [I][SavedStruct] Loading "/int/.desktop.settings" +41878 [I][SavedStruct] Loading "/int/.desktop.settings" +42075 [I][SavedStruct] Loading "/int/.desktop.settings" +42275 [I][SavedStruct] Loading "/int/.desktop.settings" +42360 [I][SavedStruct] Loading "/int/.desktop.settings" +42475 [I][SavedStruct] Loading "/int/.desktop.settings" +42675 [I][SavedStruct] Loading "/int/.desktop.settings" +42860 [I][SavedStruct] Loading "/int/.desktop.settings" +42878 [I][SavedStruct] Loading "/int/.desktop.settings" +43075 [I][SavedStruct] Loading "/int/.desktop.settings" +43275 [I][SavedStruct] Loading "/int/.desktop.settings" +43360 [I][SavedStruct] Loading "/int/.desktop.settings" +43389 [D][BtGap] RSSI: -85 +43391 [I][BadBleWorker] BLE Key timeout : 41 +43394 [D][BadBleWorker] line:DELAY 200 +43397 [D][BtGap] RSSI: -85 +43399 [I][BadBleWorker] BLE Key timeout : 41 +43475 [I][SavedStruct] Loading "/int/.desktop.settings" +43601 [D][BadBleWorker] line:ENTER +43603 [I][BadBleWorker] Special key pressed 28 + +43647 [D][BtGap] RSSI: -86 +43649 [I][BadBleWorker] BLE Key timeout : 41 +43652 [D][BadBleWorker] line:GUI SPACE +43654 [I][BadBleWorker] Special key pressed 82c + +43675 [I][SavedStruct] Loading "/int/.desktop.settings" +43699 [D][BtGap] RSSI: -91 +43701 [I][BadBleWorker] BLE Key timeout : 41 +43704 [D][BadBleWorker] line:DELAY 500 +43706 [D][BtGap] RSSI: -91 +43708 [I][BadBleWorker] BLE Key timeout : 41 +43860 [I][SavedStruct] Loading "/int/.desktop.settings" +43878 [I][SavedStruct] Loading "/int/.desktop.settings" +44075 [I][SavedStruct] Loading "/int/.desktop.settings" +44211 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html +44275 [I][SavedStruct] Loading "/int/.desktop.settings" +44360 [I][SavedStruct] Loading "/int/.desktop.settings" +44475 [I][SavedStruct] Loading "/int/.desktop.settings" +44675 [I][SavedStruct] Loading "/int/.desktop.settings" +44860 [I][SavedStruct] Loading "/int/.desktop.settings" +44878 [I][SavedStruct] Loading "/int/.desktop.settings" +45075 [I][SavedStruct] Loading "/int/.desktop.settings" +45275 [I][SavedStruct] Loading "/int/.desktop.settings" +45360 [I][SavedStruct] Loading "/int/.desktop.settings" +45475 [I][SavedStruct] Loading "/int/.desktop.settings" +45675 [I][SavedStruct] Loading "/int/.desktop.settings" +45860 [I][SavedStruct] Loading "/int/.desktop.settings" +45878 [I][SavedStruct] Loading "/int/.desktop.settings" +45978 [D][BtGap] RSSI: -89 +45980 [I][BadBleWorker] BLE Key timeout : 41 +45983 [D][BadBleWorker] line:DELAY 200 +45986 [D][BtGap] RSSI: -86 +45987 [I][BadBleWorker] BLE Key timeout : 41 +46075 [I][SavedStruct] Loading "/int/.desktop.settings" +46190 [D][BadBleWorker] line:ENTER +46192 [I][BadBleWorker] Special key pressed 28 + +46237 [D][BtGap] RSSI: -97 +46239 [I][BadBleWorker] BLE Key timeout : 41 +46242 [D][BtGap] RSSI: -97 +46244 [I][BadBleWorker] BLE Key timeout : 41 +46275 [I][SavedStruct] Loading "/int/.desktop.settings" +46360 [I][SavedStruct] Loading "/int/.desktop.settings" +46475 [I][SavedStruct] Loading "/int/.desktop.settings" +46675 [I][SavedStruct] Loading "/int/.desktop.settings" +46860 [I][SavedStruct] Loading "/int/.desktop.settings" +46879 [I][SavedStruct] Loading "/int/.desktop.settings" +47075 [I][SavedStruct] Loading "/int/.desktop.settings" +47275 [I][SavedStruct] Loading "/int/.desktop.settings" +47360 [I][SavedStruct] Loading "/int/.desktop.settings" +47475 [I][SavedStruct] Loading "/int/.desktop.settings" +47675 [I][SavedStruct] Loading "/int/.desktop.settings" +47860 [I][SavedStruct] Loading "/int/.desktop.settings" +47879 [I][SavedStruct] Loading "/int/.desktop.settings" +48075 [I][SavedStruct] Loading "/int/.desktop.settings" +48275 [I][SavedStruct] Loading "/int/.desktop.settings" +48360 [I][SavedStruct] Loading "/int/.desktop.settings" +48475 [I][SavedStruct] Loading "/int/.desktop.settings" +48675 [I][SavedStruct] Loading "/int/.desktop.settings" +48860 [I][SavedStruct] Loading "/int/.desktop.settings" +48879 [I][SavedStruct] Loading "/int/.desktop.settings" +49075 [I][SavedStruct] Loading "/int/.desktop.settings" +49275 [I][SavedStruct] Loading "/int/.desktop.settings" +49360 [I][SavedStruct] Loading "/int/.desktop.settings" +49475 [I][SavedStruct] Loading "/int/.desktop.settings" +49675 [I][SavedStruct] Loading "/int/.desktop.settings" +49860 [I][SavedStruct] Loading "/int/.desktop.settings" +49879 [I][SavedStruct] Loading "/int/.desktop.settings" +50075 [I][SavedStruct] Loading "/int/.desktop.settings" +50275 [I][SavedStruct] Loading "/int/.desktop.settings" +50360 [I][SavedStruct] Loading "/int/.desktop.settings" +50475 [I][SavedStruct] Loading "/int/.desktop.settings" +50675 [I][SavedStruct] Loading "/int/.desktop.settings" +50860 [I][SavedStruct] Loading "/int/.desktop.settings" +50879 [I][SavedStruct] Loading "/int/.desktop.settings" +51075 [I][SavedStruct] Loading "/int/.desktop.settings" +51275 [I][SavedStruct] Loading "/int/.desktop.settings" +51360 [I][SavedStruct] Loading "/int/.desktop.settings" +51475 [I][SavedStruct] Loading "/int/.desktop.settings" +51675 [I][SavedStruct] Loading "/int/.desktop.settings" +51860 [I][SavedStruct] Loading "/int/.desktop.settings" +51879 [I][SavedStruct] Loading "/int/.desktop.settings" +52075 [I][SavedStruct] Loading "/int/.desktop.settings" +52275 [I][SavedStruct] Loading "/int/.desktop.settings" +52360 [I][SavedStruct] Loading "/int/.desktop.settings" +52475 [I][SavedStruct] Loading "/int/.desktop.settings" +52675 [I][SavedStruct] Loading "/int/.desktop.settings" +52860 [I][SavedStruct] Loading "/int/.desktop.settings" +52879 [I][SavedStruct] Loading "/int/.desktop.settings" +53075 [I][SavedStruct] Loading "/int/.desktop.settings" +53275 [I][SavedStruct] Loading "/int/.desktop.settings" +53360 [I][SavedStruct] Loading "/int/.desktop.settings" +53475 [I][SavedStruct] Loading "/int/.desktop.settings" +53675 [I][SavedStruct] Loading "/int/.desktop.settings" +53860 [I][SavedStruct] Loading "/int/.desktop.settings" +53879 [I][SavedStruct] Loading "/int/.desktop.settings" +54075 [I][SavedStruct] Loading "/int/.desktop.settings" +54101 [I][BtGap] Stop advertising +54104 [D][BtGap] terminate success +54107 [E][BtGap] set_non_discoverable failed 12 +54111 [I][SavedStruct] Loading "/int/.desktop.settings" +54275 [I][SavedStruct] Loading "/int/.desktop.settings" +54311 [I][SavedStruct] Loading "/int/.bt.settings" +54324 [I][SavedStruct] Loading "/int/.bt.keys" +54335 [I][FuriHalBt] Disconnect and stop advertising +54337 [I][FuriHalBt] Stop current profile services +54339 [I][BtGap] Disconnect from client. Reason: 16 +54349 [I][FuriHalBt] Stop BLE related RTOS threads +54373 [I][FuriHalBt] Reset SHCI +54475 [I][SavedStruct] Loading "/int/.desktop.settings" +54487 [I][FuriHalBt] Start BT initialization +54494 [I][Core2] Core2 started +54496 [I][Core2] C2 boot completed, mode: Stack +54500 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages +54504 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages +54508 [I][Core2] Radio stack started +54511 [I][Core2] Flash activity control switched to SEM7 +54513 [I][BtGap] Advertising name: Keyboard +54516 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 +54534 [D][BtBatterySvc] Updating power state characteristic +54540 [I][BtSrv] Bt App started +54542 [I][BtGap] Start advertising +54545 [I][BadBleWorker] End +54547 [I][SavedStruct] Loading "/int/.desktop.settings" +54568 [I][SavedStruct] Loading "/int/.desktop.settings" +54570 [D][BrowserWorker] Start +54598 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 +54600 [D][BrowserWorker] Load offset: 0 cnt: 50 +54676 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 +54678 [D][BrowserWorker] Load offset: 0 cnt: 50 +54888 [D][BrowserWorker] End +54890 [I][SavedStruct] Loading "/int/.desktop.settings" +54946 [I][fap_loader_app] FAP app returned: 0 +54982 [I][LoaderSrv] Application stopped. Free heap: 140272 +55011 [I][AnimationStorage] Custom Manifest selected +55299 [I][AnimationManager] Select 'HANDS' animation +55303 [I][AnimationManager] Load animation 'HANDS' +55331 [I][SavedStruct] Loading "/int/.desktop.settings" +65361 [I][Dolphin] Flush stats +65363 [I][SavedStruct] Saving "/int/.dolphin.state" +65373 [D][StorageInt] Device erase: page 2, translated page: cf +65468 [D][StorageInt] Device sync: skipping +65473 [I][DolphinState] State saved +72183 [I][AnimationStorage] Custom Manifest selected +73040 [I][AnimationManager] Select 'SKULL' animation +103081 [I][AnimationStorage] Custom Manifest selected +104016 [I][AnimationManager] Select 'LOGO_WD2' animation +114771 [D][BtGap] set_non_discoverable success +134057 [I][AnimationStorage] Custom Manifest selected +134395 [I][AnimationManager] Select 'MUMMY' animation +164438 [I][AnimationStorage] Custom Manifest selected +164953 [I][AnimationManager] Select 'DEDSEC_TALK' animation +174773 [D][BtGap] set_non_discoverable success +194994 [I][AnimationStorage] Custom Manifest selected +195897 [I][AnimationManager] Select 'REAPER_ALT' animation +225938 [I][AnimationStorage] Custom Manifest selected +226509 [I][AnimationManager] Select 'DEDSEC_OLD' animation +234775 [D][BtGap] set_non_discoverable success +256552 [I][AnimationStorage] Custom Manifest selected +256819 [I][AnimationManager] Select 'JOIN_US' animation +286859 [I][AnimationStorage] Custom Manifest selected +287374 [I][AnimationManager] Select 'DEDSEC_TALK' animation +294777 [D][BtGap] set_non_discoverable success +317415 [I][AnimationStorage] Custom Manifest selected +318273 [I][AnimationManager] Select 'SKULL' animation +348316 [I][AnimationStorage] Custom Manifest selected +349083 [I][AnimationManager] Select 'SPIRAL' animation +354779 [D][BtGap] set_non_discoverable success +379124 [I][AnimationStorage] Custom Manifest selected +379787 [I][AnimationManager] Select 'SKULL_SPIN' animation +409828 [I][AnimationStorage] Custom Manifest selected +410316 [I][AnimationManager] Select 'DEDSEC_AD' animation +414781 [D][BtGap] set_non_discoverable success +440358 [I][AnimationStorage] Custom Manifest selected +440698 [I][AnimationManager] Select 'MUMMY' animation +470740 [I][AnimationStorage] Custom Manifest selected +471138 [I][AnimationManager] Select 'HANDS' animation +474783 [D][BtGap] set_non_discoverable success +501180 [I][AnimationStorage] Custom Manifest selected +502243 [I][AnimationManager] Select 'DEDSEC_ANIM' animation +532285 [I][AnimationStorage] Custom Manifest selected +532547 [I][AnimationManager] Select 'BOTTY_CALL' animation +534785 [D][BtGap] set_non_discoverable success +562592 [I][AnimationStorage] Custom Manifest selected +563449 [I][AnimationManager] Select 'SKULL' animation +593489 [I][AnimationStorage] Custom Manifest selected +594414 [I][AnimationManager] Select 'LOGO_WD2' animation +594787 [D][BtGap] set_non_discoverable success +624465 [I][AnimationStorage] Custom Manifest selected +624658 [I][AnimationManager] Select 'thank_you_128x64' animation +654700 [I][AnimationStorage] Custom Manifest selected +655440 [I][AnimationManager] Select 'GUNS_CAR' animation +655471 [D][BtGap] set_non_discoverable success +685480 [I][AnimationStorage] Custom Manifest selected +686535 [I][AnimationManager] Select 'DEDSEC_ANIM' animation +715480 [D][BtGap] set_non_discoverable success +716577 [I][AnimationStorage] Custom Manifest selected +717479 [I][AnimationManager] Select 'REAPER_ALT' animation +747521 [I][AnimationStorage] Custom Manifest selected +748026 [I][AnimationManager] Select 'DEDSEC_TALK' animation +775484 [D][BtGap] set_non_discoverable success +778069 [I][AnimationStorage] Custom Manifest selected +779065 [I][AnimationManager] Select 'REAPER' animation +809108 [I][AnimationStorage] Custom Manifest selected +809625 [I][AnimationManager] Select 'DEDSEC_TALK' animation +835486 [D][BtGap] set_non_discoverable success +839669 [I][AnimationStorage] Custom Manifest selected +840573 [I][AnimationManager] Select 'REAPER_ALT' animation +870614 [I][AnimationStorage] Custom Manifest selected +871392 [I][AnimationManager] Select 'SPIRAL' animation +895488 [D][BtGap] set_non_discoverable success +901433 [I][AnimationStorage] Custom Manifest selected +902487 [I][AnimationManager] Select 'DEDSEC_ANIM' animation +932529 [I][AnimationStorage] Custom Manifest selected +932843 [I][AnimationManager] Select 'FINGER' animation From 5a150fe4147459d151684cdda142aac71e6b85cb Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 27 Jan 2023 00:18:36 +0100 Subject: [PATCH 009/231] workflow stuff --- .github/workflows/build.yml | 130 ++++++++ .github/workflows/check_submodules.yml | 47 +++ .github/workflows/lint_c.yml | 47 +++ .github/workflows/lint_python.yml | 33 ++ .github/workflows/merge_report.yml | 45 +++ .github/workflows/pvs_studio.yml | 115 +++++++ .github/workflows/reindex.yml | 14 + .github/workflows/unit_tests.yml | 66 ++++ .github/workflows/updater_test.yml | 77 +++++ .../resources/nfc/assets/mf_classic_dict.nfc | 292 ++++++++++++++++-- 10 files changed, 846 insertions(+), 20 deletions(-) create mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/check_submodules.yml create mode 100644 .github/workflows/lint_c.yml create mode 100644 .github/workflows/lint_python.yml create mode 100644 .github/workflows/merge_report.yml create mode 100644 .github/workflows/pvs_studio.yml create mode 100644 .github/workflows/reindex.yml create mode 100644 .github/workflows/unit_tests.yml create mode 100644 .github/workflows/updater_test.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..6344bc846 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,130 @@ +name: 'Build' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /home/runner/work + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + id: names + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + echo random_hash=$(openssl rand -base64 40 | shasum -a 256 | awk '{print $1}') >> $GITHUB_OUTPUT + echo "event_type=$TYPE" >> $GITHUB_OUTPUT + + - name: 'Make artifacts directory' + run: | + rm -rf artifacts + mkdir artifacts + + - name: 'Bundle scripts' + if: ${{ !github.event.pull_request.head.repo.fork }} + run: | + tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ + copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} + done + + - name: 'Move upload files' + if: ${{ !github.event.pull_request.head.repo.fork }} + run: | + set -e + for TARGET in ${TARGETS}; do + mv dist/${TARGET}-*/* artifacts/ + done + + - name: "Check for uncommitted changes" + run: | + git diff --exit-code + + - name: 'Bundle resources' + if: ${{ !github.event.pull_request.head.repo.fork }} + run: | + tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources + + - name: 'Bundle core2 firmware' + if: ${{ !github.event.pull_request.head.repo.fork }} + run: | + cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" + + - name: 'Archive artifacts' + uses: actions/upload-artifact@v3 + with: + name: firmware + path: | + artifacts + + compact: + if: ${{ !startsWith(github.ref, 'refs/tags') }} + runs-on: ubuntu-latest + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ] + then + git submodule status \ + || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + submodules: true + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Build the firmware' + run: | + set -e + for TARGET in ${TARGETS}; do + ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ + updater_package DEBUG=0 COMPACT=1 + done diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml new file mode 100644 index 000000000..2eb2027c9 --- /dev/null +++ b/.github/workflows/check_submodules.yml @@ -0,0 +1,47 @@ +name: 'Check submodules branch' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +jobs: + check_protobuf: + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check protobuf branch' + run: | + git submodule update --init + SUB_PATH="assets/protobuf"; + SUB_BRANCH="dev"; + SUB_COMMITS_MIN=40; + cd "$SUB_PATH"; + SUBMODULE_HASH="$(git rev-parse HEAD)"; + BRANCHES=$(git branch -r --contains "$SUBMODULE_HASH"); + COMMITS_IN_BRANCH="$(git rev-list --count dev)"; + if [ $COMMITS_IN_BRANCH -lt $SUB_COMMITS_MIN ]; then + echo "name=fails::error" >> $GITHUB_OUTPUT + echo "::error::Error: Too low commits in $SUB_BRANCH of submodule $SUB_PATH: $COMMITS_IN_BRANCH(expected $SUB_COMMITS_MIN+)"; + exit 1; + fi + if ! grep -q "/$SUB_BRANCH" <<< "$BRANCHES"; then + echo "name=fails::error" >> $GITHUB_OUTPUT + echo "::error::Error: Submodule $SUB_PATH is not on branch $SUB_BRANCH"; + exit 1; + fi diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml new file mode 100644 index 000000000..a6fd5127c --- /dev/null +++ b/.github/workflows/lint_c.yml @@ -0,0 +1,47 @@ +name: 'Lint C/C++ with clang-format' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + TARGETS: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 + +jobs: + lint_c_cpp: + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check code formatting' + id: syntax_check + run: ./fbt lint + + - name: Report code formatting errors + if: failure() && steps.syntax_check.outputs.errors && github.event.pull_request + uses: peter-evans/create-or-update-comment@v1 + with: + issue-number: ${{ github.event.pull_request.number }} + body: | + Please fix following code formatting errors: + ``` + ${{ steps.syntax_check.outputs.errors }} + ``` + You might want to run `./fbt format` for an auto-fix. diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml new file mode 100644 index 000000000..66c36064c --- /dev/null +++ b/.github/workflows/lint_python.yml @@ -0,0 +1,33 @@ +name: 'Python Lint' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + FBT_TOOLCHAIN_PATH: /runner/_work + SET_GH_OUTPUT: 1 + +jobs: + lint_python: + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Check code formatting' + run: ./fbt lint_py diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml new file mode 100644 index 000000000..13fab0948 --- /dev/null +++ b/.github/workflows/merge_report.yml @@ -0,0 +1,45 @@ +name: 'Check FL ticket in PR name' + +on: + push: + branches: + - dev + +env: + FBT_TOOLCHAIN_PATH: /runner/_work + +jobs: + merge_report: + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Check ticket and report' + run: | + source scripts/toolchain/fbtenv.sh + python3 -m pip install slack_sdk + python3 scripts/merge_report_qa.py \ + ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ + ${{ secrets.QA_REPORT_SLACK_CHANNEL }} + diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml new file mode 100644 index 000000000..46ee8801d --- /dev/null +++ b/.github/workflows/pvs_studio.yml @@ -0,0 +1,115 @@ +name: 'Static C/C++ analysis with PVS-Studio' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + +jobs: + analyse_c_cpp: + if: ${{ !github.event.pull_request.head.repo.fork }} + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + id: names + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Make reports directory' + run: | + rm -rf reports/ + mkdir reports + + - name: 'Generate compile_comands.json' + run: | + ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons api_syms + + - name: 'Static code analysis' + run: | + source scripts/toolchain/fbtenv.sh + pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} + pvs-studio-analyzer analyze \ + @.pvsoptions \ + -C gccarm \ + -j$(grep -c processor /proc/cpuinfo) \ + -f build/f7-firmware-DC/compile_commands.json \ + -o PVS-Studio.log + + - name: 'Convert PVS-Studio output to html and detect warnings' + id: pvs-warn + run: | + WARNINGS=0 + plog-converter \ + -a GA:1,2,3 \ + -t fullhtml \ + --indicate-warnings \ + PVS-Studio.log \ + -o reports/${DEFAULT_TARGET}-${SUFFIX} || WARNINGS=1 + echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT + + - name: 'Upload artifacts to update server' + if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} + run: | + mkdir -p ~/.ssh + ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts + echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; + chmod 600 ./deploy_key; + rsync -avrzP --mkpath \ + -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ + reports/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/"; + rm ./deploy_key; + + - name: 'Find Previous Comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'PVS-Studio report for commit' + + - name: 'Create or update comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/create-or-update-comment@v1 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** + - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) + edit-mode: replace + + - name: 'Raise exception' + if: ${{ steps.pvs-warn.outputs.warnings != 0 }} + run: | + echo "Please fix all PVS varnings before merge" + exit 1 + diff --git a/.github/workflows/reindex.yml b/.github/workflows/reindex.yml new file mode 100644 index 000000000..ea850e705 --- /dev/null +++ b/.github/workflows/reindex.yml @@ -0,0 +1,14 @@ +name: 'Reindex' + +on: + release: + types: [prereleased,released] + +jobs: + reindex: + name: 'Reindex updates' + runs-on: [self-hosted,FlipperZeroShell] + steps: + - name: Trigger reindex + run: | + curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 000000000..ac3fc3684 --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,66 @@ +name: 'Unit tests' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt + +jobs: + run_units_on_bench: + runs-on: [self-hosted, FlipperZeroTest] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Flash unit tests firmware' + id: flashing + if: success() + run: | + ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 + + - name: 'Wait for flipper and format ext' + id: format_ext + if: steps.flashing.outcome == 'success' + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext + + - name: 'Copy assets and unit data, reboot and wait for flipper' + id: copy + if: steps.format_ext.outcome == 'success' + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests + python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Run units and validate results' + id: run_units + if: steps.copy.outcome == 'success' + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} + + - name: 'Check GDB output' + if: failure() + run: | + ./fbt gdb_trace_all OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml new file mode 100644 index 000000000..d4ca56fad --- /dev/null +++ b/.github/workflows/updater_test.yml @@ -0,0 +1,77 @@ +name: 'Updater test' + +on: + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /opt + +jobs: + test_updater_on_bench: + runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: Checkout code + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get flipper from device manager (mock)' + id: device + run: | + echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + + - name: 'Flashing target firmware' + id: first_full_flash + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Validating updater' + id: second_full_flash + if: success() + run: | + source scripts/toolchain/fbtenv.sh + ./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1 + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + + - name: 'Get last release tag' + id: release_tag + if: failure() + run: | + echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT + + - name: 'Decontaminate previous build leftovers' + if: failure() + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout latest release' + uses: actions/checkout@v3 + if: failure() + with: + fetch-depth: 0 + ref: ${{ steps.release_tag.outputs.tag }} + + - name: 'Flash last release' + if: failure() + run: | + ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FORCE=1 + + - name: 'Wait for flipper and format ext' + if: failure() + run: | + source scripts/toolchain/fbtenv.sh + python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} + python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext diff --git a/assets/resources/nfc/assets/mf_classic_dict.nfc b/assets/resources/nfc/assets/mf_classic_dict.nfc index ecb2c54dd..d62d0655b 100644 --- a/assets/resources/nfc/assets/mf_classic_dict.nfc +++ b/assets/resources/nfc/assets/mf_classic_dict.nfc @@ -1,31 +1,41 @@ ########################### # Do not edit, this file will be overwritten after firmware update # Use the user_dict file for user keys -# Last update 19th October, 2022 +# Last updated 25 January 2023 # ------------------------- + # MIFARE DEFAULT KEYS # -- ICEMAN FORK VERSION -- # -- CONTRIBUTE TO THIS LIST, SHARING IS CARING -- # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_default_keys.dic + # DEFAULTKEY(FIRSTKEYUSEDBYPROGRAMIFNOUSERDEFINEDKEY) FFFFFFFFFFFF + # BLANKKEY 000000000000 + # NFC FORUM MADKEY # MAD ACCESS KEY A (REVERSED) A5A4A3A2A1A0 + # MAD ACCESS KEY B 89ECA97F8C2A + # KEY A WIEN # KEY B WIEN + # ICOPY-X E00000000000 E7D6064C5860 B27CCAB30DBD + # LIB / NAT BIEB D2ECE8B9395E + # NSCP DEFAULT KEY 1494E81663D7 + # KIEV KEYS 569369C5A0E5 632193BE1C3C @@ -34,40 +44,52 @@ D2ECE8B9395E 9DE89E070277 EFF603E1EFE9 F14EE7CAE863 + # KIEV / OV-CHIPKAART B5FF67CBA951 + # RKF # VÄSTTRAFIKEN KEYA, RKF ÖSTGÖTATRAFIKEN KEYA FC00018778F7 0297927C0F77 54726176656C + # VÄSTTRAFIKEN KEYB 00000FFE2488 776974687573 EE0042F88840 + # RKF SLKEYA 26940B21FF5D A64598A77478 + # RKF SLKEYB 5C598C9C58B5 E4D2770A89BE + # RKF REJSKORTDANMARK KEYA 722BFCC5375F F1D83F964314 + # RKF JOJOPRIVAKEYA 505249564141 + # RKF JOJOPRIVAKEYB 505249564142 + # RKF JOJOGROUPKEYA 47524F555041 + # RKF JOJOGROUPKEYB 47524F555042 434F4D4D4F41 434F4D4D4F42 4B0B20107CCB + # TNP3XXX # ACCESS CONTROL SYSTEM 605F5E5D5C5B + # MORE KEYS FROM MFC_DEFAULT_KEYS.LUA 000000000001 000000000002 @@ -82,8 +104,10 @@ F1D83F964314 200000000000 222222222222 27DD91F1FCF1 + # DIRECTORYANDEVENTLOGKEYB 2BA9621E0A36 + # DIRECTORYANDEVENTLOGKEYA 4AF9D7ADEBE4 333333333333 @@ -104,6 +128,7 @@ A00000000000 A053A292A4AF A94133013401 AAAAAAAAAAAA + # KEYFROMLADYADA.NET B00000000000 B127C6F41436 @@ -113,31 +138,41 @@ C934FE34D934 CCCCCCCCCCCC DDDDDDDDDDDD EEEEEEEEEEEE + # ELEVATOR # DATA FROM FORUM FFFFFF545846 F1A97341A9FC + # HOTEL SYSTEM 44AB09010845 85FED980EA5A + # ARD (FR) KEY A 43454952534E + # ARD (FR) KEY B 4A2B29111213 4143414F5250 + # TEHRAN RAILWAY A9B43414F585 1FB235AC1388 + # DATA FROM HTTP://IRQ5.IO/2013/04/13/DECODING-BCARD-CONFERENCE-BADGES/ # BCARD KEYB F4A9EF2AFC6D + # DATA FROM ... # S0 B 89EAC97F8C2A + # S4 A 43C7600DEE6B + # S6 A 0120BF672A64 + # S6 B FB0B20DF1F34 A9F953DEF0A3 @@ -146,6 +181,7 @@ A9F953DEF0A3 21EDF95E7433 C121FF19F681 3D5D9996359A + # HERE BE BIP KEYS... 3A42F33AF429 1FC235AC1309 @@ -179,32 +215,41 @@ D49E2826664F 51284C3686A6 3DF14C8000A1 6A470D54127C + # DATA FROM HTTP://PASTEBIN.COM/AK9BFTPW # LÄNSTRAFIKEN I VÄSTERBOTTEN 48FFE71294A0 E3429281EFC1 16F21A82EC84 460722122510 + # 3DPRINTER # EPI ENVISIONTE 3DPRINTER AAFB06045877 + # GYM # FYSIKEN A 3E65E4FB65B3 + # FYSIKEN B 25094DF6F148 + # CLEVERFIT A05DBD98E0FC + # HOTEL KEYCARD D3B595E9DD63 AFBECD121004 + # SIMONSVOSS 6471A5EF2D1A + # ID06 4E3552426B32 22BDACF5A33F 6E7747394E63 763958704B78 + # 24-7 D21762B2DE3B 0E83A374B513 @@ -218,8 +263,10 @@ F101622750B7 710732200D34 7C335FB121B5 B39AE17435DC + # KEY A 454841585443 + # DATA FROM HTTP://PASTEBIN.COM/GQ6NK38G D39BB83F5297 85675B200017 @@ -245,9 +292,11 @@ FEE470A4CB58 75EDE6A84460 DF27A8F1CB8E B0C9DD55DD4D + # DATA FROM HTTP://BIT.LY/1BDSBJL A0B0C0D0E0F0 A1B1C1D1E1F1 + # DATA FROM MSK SOCIAL A229E68AD9E5 49C2B5296EF4 @@ -274,6 +323,7 @@ C7C0ADB3284F D8A274B2E026 B20B83CB145C 9AFA6CB4FC3D + # DATA FROM HTTP://PASTEBIN.COM/RRJUEDCM 0D258FE90296 E55A3CA71826 @@ -287,16 +337,19 @@ EEB420209D0C 1ACC3189578C C2B7EC7D4EB1 369A4663ACD2 + # DATA FROM HTTPS://GITHUB.COM/ZHANGJINGYE03/ZXCARDUMPER # ZXCARD KEY A/B 668770666644 003003003003 + # DATA FROM HTTP://PHREAKERCLUB.COM/FORUM/SHOWTHREAD.PHP?P=41266 26973EA74321 71F3A315AD26 51044EFB5AAB AC70CA327A04 EB0A8FF88ADE + # TRANSPORT SYSTEM METROMONEY 2803BCB0C7E1 9C616585E26D @@ -305,6 +358,7 @@ EB0A8FF88ADE A160FCD5EC4C 112233445566 361A62F35BC9 + # TRANSPORT SYSTEM SPAIN 83F3CB98C258 070D486BC555 @@ -338,14 +392,18 @@ C52876869800 5145C34DBA19 25352912CD8D 81B20C274C3F + # DATA FROM MALL # PLAYLAND BALIKESIR ABBA1234FCB0 + # A TRIO BOWLING BAHCELIEVLER 314F495254FF 4152414B4E41 + # KARINCA PARK NIGDE 4E474434FFFF + # DATA FROM HTTPS://GITHUB.COM/RADIOWAR/NFCGUI 44DD5A385AAF 21A600056CB0 @@ -383,10 +441,12 @@ CBA6AE869AD5 A7ABBC77CC9E F792C4C76A5C BFB6796A11DB + # DATA FROM SALTO A/B 6A1987C40A21 7F33625BC129 2338B4913111 + # DATA FROM STOYE CB779C50E1BD A27D3804C259 @@ -414,16 +474,22 @@ D9A37831DCE5 C5CFE06D9EA3 C0DECE673829 A56C2DF9A26D + # DATA FROM HTTPS://PASTEBIN.COM/VBWAST74 68D3F7307C89 + # SMART RIDER. WESTERN AUSTRALIAN PUBLIC TRANSPORT CARDS 568C9083F71C + # BANGKOK METRO KEY 97F5DA640B18 + # METRO VALENCIA KEY A8844B0BCA06 + # HTC EINDHOVEN KEY 857464D3AAD1 + # VIGIK KEYS # VARIOUS SOURCES : # * HTTPS://GITHUB.COM/DUMPDOS/VIGIK @@ -432,18 +498,24 @@ A8844B0BCA06 # FRENCH VIGIK # VIGIK1 A 314B49474956 + # VIGIK1 B 564C505F4D41 BA5B895DA162 + # VIGIK MYSTERY KEYS MIFARE 1K EV1 (S50) # 16 A 5C8FF9990DA2 + # 17 A 75CCB59C9BED + # 16 B D01AFEEB890A + # 17 B 4B791BEA7BCC + # BTCINO UNDETERMINED SPREAKD 0X01->0X13 KEY 021209197591 2EF720F2AF76 @@ -452,6 +524,7 @@ D01AFEEB890A 4A6352684677 BF1F4424AF76 536653644C65 + # INTRATONE COGELEC # DATA FROM HTTP://BOUZDECK.COM/RFID/32-CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML 484558414354 @@ -470,6 +543,7 @@ E64A986A5D94 66D2B7DC39EF 6BC1E1AE547D 22729A9BD40F + # DATA FROM HTTPS://DFIR.LU/BLOG/CLONING-A-MIFARE-CLASSIC-1K-TAG.HTML 925B158F796F FAD63ECB5891 @@ -485,8 +559,10 @@ CC6B3B3CD263 703140FD6D86 157C9A513FA5 E2A5DC8E066F + # DATA FROM FORUM, SCHLAGE 9691T FOB EF1232AB18A0 + # DATA FROM A OYSTER CARD 374BF468607F BFC8E353AF63 @@ -517,8 +593,10 @@ A2ABB693CE34 91F93A5564C9 E10623E7A016 B725F9CBF183 + # DATA FROM FDI TAG 8829DA9DAF76 + # DATA FROM GITHUB ISSUE 0A7932DC7E65 11428B5BCE06 @@ -543,14 +621,18 @@ D4FE03CE5B09 D4FE03CE5B0A D4FE03CE5B0F E241E8AFCBAF + # DATA FROM FORUM POST 123F8888F322 050908080008 + # DATA FROM HOIST 4F9F59C9C875 + # DATA FROM PASTEBIN 66F3ED00FED7 F7A39753D018 + # DATA FROM HTTPS://PASTEBIN.COM/Z7PEEZIF 386B4D634A65 666E564F4A44 @@ -582,19 +664,23 @@ F7A39753D018 6F506F493353 31646241686C 77646B633657 + # DATA FROM TRANSPERT 2031D1E57A3B 53C11F90822A 9189449EA24E + # DATA FROM GITHUB 410B9B40B872 2CB1A90071C8 + # DATA FROM 8697389ACA26 1AB23CD45EF6 013889343891 0000000018DE 16DDCB6B3F24 + # DATA FROM HTTPS://PASTEBIN.COM/VWDRZW7D # VINGCARD MIFARE 4K STAFF CARD EC0A9B1A9E06 @@ -608,6 +694,7 @@ B66AC040203A 2E641D99AD5B AD4FB33388BF 69FB7B7CD8EE + # HOTEL 2A6D9205E7CA 2A2C13CC242A @@ -615,25 +702,34 @@ AD4FB33388BF 01FA3FC68349 6D44B5AAF464 1717E34A7A8A + # RFIDEAS 6B6579737472 + # HID MIFARE CLASSIC 1K KEY 484944204953 204752454154 + # HID MIFARE SO 3B7E4FD575AD 11496F97752A + # LUXEO/AZTEK CASHLESS VENDING 415A54454B4D + # BQT 321958042333 + # APERIO KEY_A SECTOR 1, 12, 13, 14, 15 DATA START 0 LENGTH 48 160A91D29A9C + # GALLAGHER B7BF0C13066E + # PIK COMFORT MOSCOW KEYS (ISBC MIFARE PLUS SE 1K) 009FB42D98ED 002E626E2820 + # BOSTON, MA, USA TRANSIT - MBTA CHARLIE CARD # CHARLIE 3060206F5B0A @@ -670,8 +766,10 @@ D80511FC2AB4 BB467463ACD6 E67C8010502D FF58BA1B4478 + # DATA FROM HTTPS://PASTEBIN.COM/KZ8XP4EV FBF225DC5D58 + # DATA HTTPS://PASTEBIN.COM/BEM6BDAE # VINGCARD.TXT 4708111C8604 @@ -688,16 +786,19 @@ FBF225DC5D58 D9BCDE7FC489 0C03A720F208 6018522FAC02 + # DATA FROM HTTPS://PASTEBIN.COM/4T2YFMGT # MIFARE TECHNISCHE UNIVERSITÄT GRAZ TUG D58660D1ACDE 50A11381502C C01FC822C6E5 0854BF31111E + # MORE KEYS: 8A19D40CF2B5 AE8587108640 135B88A94B8B + # RUSSIAN TROIKA CARD 08B386463229 0E8F64340BA4 @@ -753,6 +854,7 @@ EAAC88E5DC99 F8493407799D 6B8BD9860763 D3A297DC2698 + # KEYS FROM MIFARECLASSICTOOL PROJECT 044CE1872BC3 045CECA15535 @@ -808,22 +910,28 @@ FD8705E721B0 00ADA2CD516D 237A4D0D9119 0ED7846C2BC9 + # HOTEL ADINA 9EBC3EB37130 + # MOST LIKELY DIVERSED INDIVIDUAL KEYS. # DATA FROM HTTPS://GITHUB.COM/KORSEHINDI/PROXMARK3/COMMIT/24FDBFA9A1D5C996AAA5C192BC07E4AB28DB4C5C 491CDC863104 A2F63A485632 98631ED2B229 19F1FFE02563 + # ARGENTINA 563A22C01FC8 43CA22C13091 25094DF2C1BD + # OMNITEC.ES HOTEL TIMECARD / MAINTENANCECARD AFBECD120454 + # OMNITEC.ES HOTEL EMERGENCYCARD 842146108088 + # TAPCARD PUBLIC TRANSPORT LA EA1B88DF0A76 D1991E71E2C5 @@ -857,6 +965,7 @@ B81846F06EDF C6A76CB2F3B5 E3AD9E9BA5D4 6C9EC046C1A4 + # ROC HIGHSCHOOL ACCESSCARD B021669B44BB B18CDCDE52B7 @@ -888,6 +997,7 @@ AE43F36C1A9A BE7C4F6C7A9A 5EC7938F140A 82D58AA49CCB + # MELONCARD 323334353637 CEE3632EEFF5 @@ -904,6 +1014,7 @@ A7FB4824ACBF 00F0BD116D70 4CFF128FA3EF 10F3BEBC01DF + # TRANSPORTES INSULAR LA PALMA 0172066B2F03 0000085F0000 @@ -937,6 +1048,7 @@ B1A862985913 3B0172066B2F 3F1A87298691 F3F0172066B2 + # TEHRAN EZPAY 38A88AEC1C43 CBD2568BC7C6 @@ -953,10 +1065,12 @@ D3B1C7EA5C53 604AC8D87C7E 8E7B29460F12 BB3D7B11D224 + # CHACO B210CFA436D2 B8B1CFA646A8 A9F95891F0A4 + # KEYS FROM APK APPLICATION "SCAN BADGE" 4A4C474F524D 444156494442 @@ -976,6 +1090,7 @@ A0004A000036 DFE73BE48AC6 B069D0D03D17 000131B93F28 + # FROM THE DFW AREA, TX, USA A506370E7C0F 26396F2042E7 @@ -992,6 +1107,7 @@ EF4C5A7AC6FC B47058139187 8268046CD154 67CC03B7D577 + # FROM THE HTL MÖDLING, NÖ, AT A5524645CD91 D964406E67B4 @@ -1000,32 +1116,40 @@ D964406E67B4 C27D999912EA 66A163BA82B4 4C60F4B15BA8 + # CAFE + CO, AT 35D850D10A24 4B511F4D28DD E45230E7A9E8 535F47D35E39 FB6C88B7E279 + # METRO CARD, AT 223C3427108A + # UNKNOWN, AT 23D4CDFF8DA3 E6849FCC324B 12FD3A94DF0E 0B83797A9C64 39AD2963D3D1 + # HOTEL BERLIN CLASSIC ROOM A KEY 34B16CD59FF8 + # HOTEL BERLIN CLASSIC ROOM B KEY BB2C0007D022 + # COINMATIC LAUNDRY SMART CARD # DATA FROM: HTTPS://PASTEBIN.COM/XZQILTUF 0734BFB93DAB 85A438F72A8A + # DATA FROM FORUM, CHINESE HOTEL 58AC17BF3629 B62307B62307 A2A3CCA2A3CC + # GRANADA, ES TRANSPORT CARD 000000270000 0F385FFB6529 @@ -1043,6 +1167,7 @@ B385EFA64290 C9739233861F F3864FCCA693 FC9839273862 + # VARIOUS HOTEL KEYS 34D3C568B348 91FF18E63887 @@ -1050,6 +1175,7 @@ FC9839273862 354A787087F1 4A306E62E9B6 B9C874AE63D0 + # DATA FROM OFFICIAL REPO F00DFEEDD0D0 0BB31DC123E5 @@ -1067,18 +1193,23 @@ B8937130B6BA D7744A1A0C44 82908B57EF4F FE04ECFE5577 + # COMFORT INN HOTEL 4D57414C5648 4D48414C5648 + # UNKNOWN HOTEL KEY 6D9B485A4845 + # BOSCH SOLUTION 6000 # FOUND IN TAGINFO APP # RATB KEY C1E51C63B8F5 1DB710648A65 + # E-GO CARD KEY 18F34C92A56E + # LIBRARY CARD MFP - SL1 4A832584637D CA679D6291B0 @@ -1094,6 +1225,7 @@ AADE86B1F9C1 C67BEB41FFBF B84D52971107 52B0D3F6116E + # DATA FROM HTTPS://PASTEBIN.COM/CLSQQ9XN CA3A24669D45 4087C6A75A96 @@ -1102,10 +1234,12 @@ D73438698EEA 5F31F6FCD3A0 A0974382C4C5 A82045A10949 + # DATA FROM HTTPS://PASTEBIN.COM/2IV8H93H # FUNNIVARIUM # FORUM ANKARA 2602FFFFFFFF + # MACERA ADASI # ANKARA KENTPARK # INACTIVE @@ -1113,16 +1247,20 @@ A82045A10949 DFF293979FA7 4D6F62692E45 4118D7EF0902 + # PETROL OFISI # POSITIVE CARD # ODE-GEC 0406080A0C0E + # KONYA ELKART 988ACDECDFB0 120D00FFFFFF + # BOWLINGO # SERDIVAN AVYM 4AE23A562A80 + # KART54 2AFFD6F88B97 A9F3F289B70C @@ -1131,18 +1269,23 @@ DB6819558A25 B16B2E573235 42EF7BF572AB 274E6101FC5E + # CRAZY PARK # KIZILAY AVM 00DD300F4F10 + # KARTSISTEM B FEE2A3FBC5B6 + # TORU ENT # TAURUS AVM 005078565703 + # VING? 0602721E8F06 FC0B50AF8700 F7BA51A9434E + # ESKART # ESKISEHIR TRANSPORT CARD E902395C1744 @@ -1151,6 +1294,7 @@ E902395C1744 D8BA1AA9ABA0 76939DDD9E97 3BF391815A8D + # MUZEKART # MUSEUM CARD FOR TURKEY 7C87013A648A @@ -1182,6 +1326,7 @@ D0DDDF2933EC 240F0BB84681 9E7168064993 2F8A867B06B4 + # BURSAKART # BURSA TRANSPORT CARD 755D49191A78 @@ -1191,18 +1336,22 @@ DAC7E0CBA8FD 0860318A3A89 1927A45A83D3 B2FE3B2875A6 + # PLAYLAND # MALTEPE PARK ABCC1276FCB0 AABAFFCC7612 + # LUNASAN # KOCAELI FAIR 26107E7006A0 + # GAMEFACTORY # OZDILEK 17D071403C20 534F4C415249 534F4C303232 + # NESPRESSO, SMART CARD # KEY-GEN ALGO, THESE KEYS ARE FOR ONE CARD FF9A84635BD2 @@ -1281,15 +1430,20 @@ AE76242931F1 124578ABFEDC ABFEDC124578 4578ABFEDC12 + # PREMIER INN HOTEL CHAIN 5E594208EF02 AF9E38D36582 + # NORWEGIAN BUILDING SITE IDENTICATION CARD. (HMS KORT) 10DF4D1859C8 + # KEY B B5244E79B0C8 + # UKRAINE HOTEL F5C1C4C5DE34 + # DATA FROM MIFARE CLASSIC TOOL REPO # ROTTERDAM UNIVERSITY OF APPLIED SCIENCES CAMPUS CARD BB7923232725 @@ -1314,6 +1468,7 @@ B5ADEFCA46C4 BF3FE47637EC B290401B0CAD AD11006B0601 + # ARMENIAN METRO E4410EF8ED2D 6A68A7D83E11 @@ -1323,6 +1478,7 @@ D3F3B958B8A3 2196FAD8115B 7C469FE86855 CE99FBC8BD26 + # KEYS FROM EUROTHERMES GROUP (SWITZERLAND) D66D91829013 75B691829013 @@ -1338,9 +1494,11 @@ FED791829013 29A791829013 668091829013 00008627C10A + # KEYS FROM NSP MANCHESTER UNIVERSITY UK ACCOMODATION STAFF AND STUDENTS 199404281970 199404281998 + # EASYCARD 310D51E539CA 2CCDA1358323 @@ -1348,6 +1506,7 @@ FED791829013 562E6EF73DB6 F53E9F4114A9 AD38C17DE7D2 + # SOME KEYS OF HTTPS://W3BSIT3-DNS.COM AND HTTPS://IKEY.RU # STRELKA EXTENSION 5C83859F2224 @@ -1358,6 +1517,7 @@ C4D3911AD1B3 CAD7D4A6A996 DA898ACBB854 FEA1295774F9 + # MOSCOW PUBLIC TOILETS CARD 807119F81418 22C8BCD10AAA @@ -1367,6 +1527,7 @@ DBF9F79AB7A2 34EDE51B4C22 C8BCD10AAABA BCD10AAABA42 + # MOSCOW SOCIAL CARD 2F87F74090D1 E53EAEFE478F @@ -1409,10 +1570,12 @@ F750C0095199 82DA4B93DB1C 9CF46DB5FD46 93EB64ACF43D + # KEYS FROM RFIDRESEARCHGROUP PROXMARK3 PROJECT # HTTPS://GITHUB.COM/RFIDRESEARCHGROUP/PROXMARK3/BLOB/MASTER/CLIENT/DICTIONARIES/MFC_DEFAULT_KEYS.DIC 13B91C226E56 5A7A52D5E20D + # IRON LOGIC A3A26EF4C6B0 2C3FEAAE99FC @@ -1431,9 +1594,11 @@ DEC0CEB0CE24 413BED2AE45B D6261A9A4B3F CB9D507CE56D + # MORE KEYS FROM THE PM3 REPO # KEYS OF ARMENIAN UNDERGROUND TICKET A0A1A2A8A4A5 + # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_bmp_sorted.dic 002DE0301481 004173272D18 @@ -2435,6 +2600,7 @@ EE17C426D25E EE487A4C806E EE5931913A8D EED56840AEBA + # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_icbmp_sorted.dic 00383D96411D 005307DB7853 @@ -3436,6 +3602,7 @@ EE3029556CEB EE49610E6121 EEB704D69BCA EED69A391464 + # https://github.com/RfidResearchGroup/proxmark3/blob/master/client/dictionaries/mfc_keys_mrzd_sorted.dic 010203040506 013940233313 @@ -3494,6 +3661,7 @@ F83466888612 F89C86B2A961 FFFFAE82366C FFFFD06F83E3 + # Unknown origin 2DEB57A3EA8F 32C1BB023F87 @@ -3577,27 +3745,26 @@ D27058C6E2C7 E19504C39461 FA1FBB3F0F1F FF16014FEFC7 -# + # Cracked by UberGuidoZ # https://github.com/UberGuidoZ -# # BadgeMaker Leaked 1A1B1C1D1E1F 1665FE2AE945 158B51947A8E -E167EC67C7FF -D537320FF9OE +EL67EC67C7FF +D53732OFF9OE 5E56BFA9E2C9 F81CED821B63 C81584EF5EDF 9551F8F9259D -36E1765CE3E8 +36EL765CE3E8 509052C8E42E 776C9B03BE71 -C608E13ADD50 +C608EL3ADD50 BEE8B345B949 -ED0EC56EEFDD -9716D5241E28 +EDOEC56EEFDD +9716D524LE28 05D1FC14DC31 3321FB75A356 F22A78E29880 @@ -3611,9 +3778,9 @@ DB32A6811327 8AA8544A2207 8C5819E780A4 7549E90353A2 -2E52ABE0CE95 +2E52ABEOCE95 E46210ED98AB -61D030C0D7A8 +61DO30COD7A8 18E20102821E DA59354DFB88 040047C12B75 @@ -3622,6 +3789,7 @@ D10008074A6F 446176696453 6F6674776172 6520446F7665 + # Apartment keyfobs in USA from Corvette830 E60F8387F0B9 FFD46FF6C5EE @@ -3630,6 +3798,7 @@ FFD46FF6C5EE 1C5179C4A8A1 16CA203B811B 11AC8C8F3AF2 + # The Westin Jakarta Indonesia from D4DB0D # Peppers Hotel Unknown location from D4DB0D 6E0DD4136B0A @@ -3644,15 +3813,19 @@ FC5AC7678BE3 F09BB8DD142D B4B3FFEDBE0A 540E0D2D1D08 + # Schlage 9691T Keyfob 7579B671051A 4F4553746B41 + # FOOD REPUBLIC 30C1DC9DD040 A9B9C1D0E3F1 + # iGuard Simple (and reverse) keys AAAAAAFFFFFF FFFFFFAAAAAA + # Vigik verified by quantum-x # https://github.com/RfidResearchGroup/proxmark3/pull/1742#issuecomment-1206113976 A00027000099 @@ -3670,19 +3843,20 @@ A00000043D79 A00000000064 A00025000030 A00003000057 -# + # BH USA 2013 conference 012279BAD3E5 + # Vigik ScanBadge App (fr.badgevigik.scanbadge) # Website https://badge-vigik.fr/ - By Alex` 0000A2B3C86F -021200c20307 +021200C20307 021209197507 1E34B127AF9C 303041534956 4143532D494E 41454E521985 -43412d627400 +43412D627400 455249524345 456666456666 45B722C63319 @@ -3701,10 +3875,12 @@ A00003000057 9EB7C8A6D4E3 A22AE12C9013 AFC984A3576E + # Spackular A/B # data from http://www.proxmark.org/forum/viewtopic.php?pid=45100#p45100 7CB033257498 1153AABAFF6C + # iGuard Simple and Reverse Keys D537320FF90E 36E1765CE3E8 @@ -3713,10 +3889,13 @@ ED0EC56EEFDD 9716D5241E28 2E52ABE0CE95 61D030C0D7A8 + # BadgeMaker Leaked from https://github.com/UberGuidoZ E167EC67C7FF + # Schlage 9691T Keyfob from seasnaill Added by VideoMan. 3111A3A303EB + # Transport cards E954024EE754 0CD464CDC100 @@ -3724,17 +3903,13 @@ BC305FE2DA65 CF0EC6ACF2F9 F7A545095C49 6862FD600F78 -#MISC KEYS -36El765CE3E8 -9716D524lE28 -C608El3ADD50 -El67EC67C7FF -# keys for transport cards by novacard.ru 72A0C485D3F7 6A530C91F85B + # RENFE MADRID (TRAIN) Extracted with detect reader 701AA491A4A5 12BA20088ED3 + # MISC KEYS FROM MY OLD ACCESS CARDS F18D91EE3033 0E726E11CFCC @@ -3784,3 +3959,80 @@ E10F0E7A8DD5 F833E24C3F1C FA8CA10C7D59 FE98F38F3EE2 +# +########################################## +# added by colonelborkmundus +# "the more, the marriott" mifare project +# + +# 1k - graduate hotel +C49DAE1C6049 +209A2B910545 + +# 1k - westin +8C29F8320617 +5697519A8F02 +7D0A1C277C05 +2058580A941F +C40964215509 +D44CFC178460 + +# 1k - marriott +7B4DFC6D6525 +23C9FDD9A366 +3119A70628EB +30AAD6A711EF +1330824CD356 +43012BD9EB87 +035C70558D7B +9966588CB9A0 +12AB4C37BB8B + +# 1k - AC hotels marriott +8EA8EC3F2320 +7B56B2B38725 + +# 1k - the ritz-carlton +30FB20D0EFEF +D20289CD9E6E +66A3B064CC4B +D18296CD9E6E + +# 1k - unknown +722538817225 + +# 1k - aria resort & casino +316B8FAA12EF +A18D9F4E75AF + +# 1k - fairfield inn & suites marriott +7AEB989A5525 +7B3B589A5525 +215E9DED9DDF +334E91BE3377 +310308EC52EF + +# 1k - residence inn marriott +F72CD208FDF9 + +# 1k - sheraton +42FC522DE987 + +# 1k - millenium hotels +132F641C948B + +# 1k - moxy hotels +20C166C00ADB +9EE3896C4530 + +# 1k - residence inn marriott +3122AE5341EB + +# 1k - americinn +8AC04C1A4A25 + +# 1k - the industrialist +2158E314C3DF + +# 1k - waldorf astoria +011C6CF459E8 \ No newline at end of file From b0c12822d210a037234c0a185ced7e1e14a4a239 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:21:26 +0100 Subject: [PATCH 010/231] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 60c5f54d6..9ab83205d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -7,7 +7,7 @@ [Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme#Known-bugs) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) ----- -This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), it also features some of the badly implemented ideas from RogueMaster, and lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). +This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), it also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). -----
From 5ff8dab68fc7d7dd48868657781db21a6ee8158e Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:43:28 +0100 Subject: [PATCH 011/231] Update check_submodules.yml --- .github/workflows/check_submodules.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check_submodules.yml b/.github/workflows/check_submodules.yml index 2eb2027c9..d1a1a64c3 100644 --- a/.github/workflows/check_submodules.yml +++ b/.github/workflows/check_submodules.yml @@ -11,7 +11,7 @@ on: jobs: check_protobuf: - runs-on: [self-hosted, FlipperZeroShell] + runs-on: ubuntu-latest steps: - name: 'Decontaminate previous build leftovers' run: | From 1f416ca99aec32b181ca2d189244494e5940fd9a Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:44:02 +0100 Subject: [PATCH 012/231] Update lint_c.yml --- .github/workflows/lint_c.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index a6fd5127c..d9d8a267e 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -16,7 +16,7 @@ env: jobs: lint_c_cpp: - runs-on: [self-hosted,FlipperZeroShell] + runs-on: ubuntu-latest steps: - name: 'Decontaminate previous build leftovers' run: | From c47c9b63a70bbcf0f9fda273be1341c904008500 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:44:21 +0100 Subject: [PATCH 013/231] Update lint_python.yml --- .github/workflows/lint_python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 66c36064c..0e96481fc 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -15,7 +15,7 @@ env: jobs: lint_python: - runs-on: [self-hosted,FlipperZeroShell] + runs-on: ubuntu-latest steps: - name: 'Decontaminate previous build leftovers' run: | From beea43d7f6b2f0b7e974f3ba1bb64f563a79c434 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:44:52 +0100 Subject: [PATCH 014/231] Delete merge_report.yml --- .github/workflows/merge_report.yml | 45 ------------------------------ 1 file changed, 45 deletions(-) delete mode 100644 .github/workflows/merge_report.yml diff --git a/.github/workflows/merge_report.yml b/.github/workflows/merge_report.yml deleted file mode 100644 index 13fab0948..000000000 --- a/.github/workflows/merge_report.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: 'Check FL ticket in PR name' - -on: - push: - branches: - - dev - -env: - FBT_TOOLCHAIN_PATH: /runner/_work - -jobs: - merge_report: - runs-on: [self-hosted,FlipperZeroShell] - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Check ticket and report' - run: | - source scripts/toolchain/fbtenv.sh - python3 -m pip install slack_sdk - python3 scripts/merge_report_qa.py \ - ${{ secrets.QA_REPORT_SLACK_TOKEN }} \ - ${{ secrets.QA_REPORT_SLACK_CHANNEL }} - From 82d6f1c19b3066c431e644b0bfabfce94162fe34 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:45:03 +0100 Subject: [PATCH 015/231] Delete reindex.yml --- .github/workflows/reindex.yml | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 .github/workflows/reindex.yml diff --git a/.github/workflows/reindex.yml b/.github/workflows/reindex.yml deleted file mode 100644 index ea850e705..000000000 --- a/.github/workflows/reindex.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 'Reindex' - -on: - release: - types: [prereleased,released] - -jobs: - reindex: - name: 'Reindex updates' - runs-on: [self-hosted,FlipperZeroShell] - steps: - - name: Trigger reindex - run: | - curl -X POST -F 'key=${{ secrets.REINDEX_KEY }}' ${{ secrets.REINDEX_URL }} From 6393fac9cdcbbb5817c0f9973760b47b6e3cfdd0 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:45:22 +0100 Subject: [PATCH 016/231] Delete unit_tests.yml --- .github/workflows/unit_tests.yml | 66 -------------------------------- 1 file changed, 66 deletions(-) delete mode 100644 .github/workflows/unit_tests.yml diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml deleted file mode 100644 index ac3fc3684..000000000 --- a/.github/workflows/unit_tests.yml +++ /dev/null @@ -1,66 +0,0 @@ -name: 'Unit tests' - -on: - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /opt - -jobs: - run_units_on_bench: - runs-on: [self-hosted, FlipperZeroTest] - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get flipper from device manager (mock)' - id: device - run: | - echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT - - - name: 'Flash unit tests firmware' - id: flashing - if: success() - run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 - - - name: 'Wait for flipper and format ext' - id: format_ext - if: steps.flashing.outcome == 'success' - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext - - - name: 'Copy assets and unit data, reboot and wait for flipper' - id: copy - if: steps.format_ext.outcome == 'success' - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/resources /ext - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} -f send assets/unit_tests /ext/unit_tests - python3 scripts/power.py -p ${{steps.device.outputs.flipper}} reboot - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Run units and validate results' - id: run_units - if: steps.copy.outcome == 'success' - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} - - - name: 'Check GDB output' - if: failure() - run: | - ./fbt gdb_trace_all OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FIRMWARE_APP_SET=unit_tests FORCE=1 From 3cbbad20cf9917536da266d413aaedcf29a0c1e8 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:45:32 +0100 Subject: [PATCH 017/231] Delete updater_test.yml --- .github/workflows/updater_test.yml | 77 ------------------------------ 1 file changed, 77 deletions(-) delete mode 100644 .github/workflows/updater_test.yml diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml deleted file mode 100644 index d4ca56fad..000000000 --- a/.github/workflows/updater_test.yml +++ /dev/null @@ -1,77 +0,0 @@ -name: 'Updater test' - -on: - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /opt - -jobs: - test_updater_on_bench: - runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: Checkout code - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get flipper from device manager (mock)' - id: device - run: | - echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT - - - name: 'Flashing target firmware' - id: first_full_flash - run: | - source scripts/toolchain/fbtenv.sh - ./fbt flash_usb_full PORT=${{steps.device.outputs.flipper}} FORCE=1 - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Validating updater' - id: second_full_flash - if: success() - run: | - source scripts/toolchain/fbtenv.sh - ./fbt flash_usb PORT=${{steps.device.outputs.flipper}} FORCE=1 - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - - - name: 'Get last release tag' - id: release_tag - if: failure() - run: | - echo "tag=$(git tag -l --sort=-version:refname | grep -v "rc\|RC" | head -1)" >> $GITHUB_OUTPUT - - - name: 'Decontaminate previous build leftovers' - if: failure() - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout latest release' - uses: actions/checkout@v3 - if: failure() - with: - fetch-depth: 0 - ref: ${{ steps.release_tag.outputs.tag }} - - - name: 'Flash last release' - if: failure() - run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FORCE=1 - - - name: 'Wait for flipper and format ext' - if: failure() - run: | - source scripts/toolchain/fbtenv.sh - python3 scripts/testing/await_flipper.py ${{steps.device.outputs.flipper}} - python3 scripts/storage.py -p ${{steps.device.outputs.flipper}} format_ext From a8b5158d583a3ec7d616a6f00b9f85abfc5e1aef Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 00:53:16 +0100 Subject: [PATCH 018/231] Delete pvs_studio.yml --- .github/workflows/pvs_studio.yml | 115 ------------------------------- 1 file changed, 115 deletions(-) delete mode 100644 .github/workflows/pvs_studio.yml diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml deleted file mode 100644 index 46ee8801d..000000000 --- a/.github/workflows/pvs_studio.yml +++ /dev/null @@ -1,115 +0,0 @@ -name: 'Static C/C++ analysis with PVS-Studio' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - TARGETS: f7 - DEFAULT_TARGET: f7 - FBT_TOOLCHAIN_PATH: /runner/_work - -jobs: - analyse_c_cpp: - if: ${{ !github.event.pull_request.head.repo.fork }} - runs-on: [self-hosted, FlipperZeroShell] - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Get commit details' - id: names - run: | - if [[ ${{ github.event_name }} == 'pull_request' ]]; then - TYPE="pull" - elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then - TYPE="tag" - else - TYPE="other" - fi - python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" - - - name: 'Make reports directory' - run: | - rm -rf reports/ - mkdir reports - - - name: 'Generate compile_comands.json' - run: | - ./fbt COMPACT=1 version_json proto_ver icons firmware_cdb dolphin_internal dolphin_blocking _fap_icons api_syms - - - name: 'Static code analysis' - run: | - source scripts/toolchain/fbtenv.sh - pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} - pvs-studio-analyzer analyze \ - @.pvsoptions \ - -C gccarm \ - -j$(grep -c processor /proc/cpuinfo) \ - -f build/f7-firmware-DC/compile_commands.json \ - -o PVS-Studio.log - - - name: 'Convert PVS-Studio output to html and detect warnings' - id: pvs-warn - run: | - WARNINGS=0 - plog-converter \ - -a GA:1,2,3 \ - -t fullhtml \ - --indicate-warnings \ - PVS-Studio.log \ - -o reports/${DEFAULT_TARGET}-${SUFFIX} || WARNINGS=1 - echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - - name: 'Upload artifacts to update server' - if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - run: | - mkdir -p ~/.ssh - ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avrzP --mkpath \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - reports/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/"; - rm ./deploy_key; - - - name: 'Find Previous Comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/find-comment@v2 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'PVS-Studio report for commit' - - - name: 'Create or update comment' - if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) - edit-mode: replace - - - name: 'Raise exception' - if: ${{ steps.pvs-warn.outputs.warnings != 0 }} - run: | - echo "Please fix all PVS varnings before merge" - exit 1 - From ff9f925e2f88b6fbf7be1515ee7701c9bd77d7e3 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 01:30:08 +0100 Subject: [PATCH 019/231] Update lint_python.yml --- .github/workflows/lint_python.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml index 0e96481fc..4b92e0e90 100644 --- a/.github/workflows/lint_python.yml +++ b/.github/workflows/lint_python.yml @@ -10,7 +10,6 @@ on: pull_request: env: - FBT_TOOLCHAIN_PATH: /runner/_work SET_GH_OUTPUT: 1 jobs: From 71ddd0f65eb0e576cd3884ec63a5113796bdcd2a Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 01:31:05 +0100 Subject: [PATCH 020/231] Update lint_c.yml --- .github/workflows/lint_c.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint_c.yml b/.github/workflows/lint_c.yml index d9d8a267e..232e3c689 100644 --- a/.github/workflows/lint_c.yml +++ b/.github/workflows/lint_c.yml @@ -11,7 +11,6 @@ on: env: TARGETS: f7 - FBT_TOOLCHAIN_PATH: /runner/_work SET_GH_OUTPUT: 1 jobs: From 54a9bfb1db7e57970568304e8d671954614af071 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 01:32:07 +0100 Subject: [PATCH 021/231] Update ReadMe.md --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index 9ab83205d..66b961fef 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -23,7 +23,7 @@ The goal of this Firmware is to regularly bring out amazing updates based on wha - Giving the level system a purpose: Right now, each level unlocks a new wallpaper. More on that below -- Clean upgraded code: RM wrote some updates to certain files. These are... painful, to say the least. Here its all built with perfection in mind and integrated in a mostly clean way. I invite you all to compare the code with theirs. +- Clean upgraded code: Some people wrote updates to certain files. These are... painful, to say the least. Here its all built with perfection in mind and integrated in a mostly clean way. I invite you all to compare the code with theirs. - Up2Date: This firmware receives updates from a few repositories, not just from its Upstream. If there are functional, but yet un-merged Pull requests on another flipper firmware that are good, they will be in here! From d9ae5b4cb2277cd4358cc2c3cb39a36144f63583 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 27 Jan 2023 00:54:51 +0000 Subject: [PATCH 022/231] Begone --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6344bc846..99f80fefa 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,7 +50,6 @@ jobs: mkdir artifacts - name: 'Bundle scripts' - if: ${{ !github.event.pull_request.head.repo.fork }} run: | tar czpf artifacts/flipper-z-any-scripts-${SUFFIX}.tgz scripts debug @@ -63,7 +62,6 @@ jobs: done - name: 'Move upload files' - if: ${{ !github.event.pull_request.head.repo.fork }} run: | set -e for TARGET in ${TARGETS}; do @@ -75,12 +73,10 @@ jobs: git diff --exit-code - name: 'Bundle resources' - if: ${{ !github.event.pull_request.head.repo.fork }} run: | tar czpf "artifacts/flipper-z-any-resources-${SUFFIX}.tgz" -C assets resources - name: 'Bundle core2 firmware' - if: ${{ !github.event.pull_request.head.repo.fork }} run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" From 0879f92e3de36799f2e38ca2a539858eff18e7bb Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 02:05:01 +0100 Subject: [PATCH 023/231] Update ReadMe.md --- ReadMe.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ReadMe.md b/ReadMe.md index 66b961fef..0a0d2adda 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -39,12 +39,16 @@ You can easily create your own pack, or find some user made ones in the discord
+
+ Once you have some packs, upload them to your Flipper in SD/dolphin_custom (if you did this right you should see SD/dolphin_custom/PackName/Anims and/or SD/dolphin_custom/PackName/Icons).
+
+ After installing the packs to Flipper, hit the Arrow UP button on the main menu and go to Xtreme Settings. Here choose which pack you want and tweak the other settings how you prefer, then press back to reboot and enjoy your new assets & animations! From a2b797fe01e7945e713eac651fb93cc36e87ccb4 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 27 Jan 2023 02:09:42 +0100 Subject: [PATCH 024/231] formatting --- applications/plugins/dap_link/dap_link.c | 3 +- applications/plugins/dice/dice.c | 1 - .../plugins/orgasmotron/orgasmotron.c | 37 +- applications/plugins/protoview/app.c | 174 ++++----- applications/plugins/protoview/app.h | 266 +++++++------ applications/plugins/protoview/app_subghz.c | 68 ++-- applications/plugins/protoview/crc.c | 9 +- .../plugins/protoview/custom_presets.h | 31 +- applications/plugins/protoview/data_feed.c | 17 +- applications/plugins/protoview/fields.c | 348 +++++++++--------- .../plugins/protoview/protocols/b4b1.c | 67 ++-- .../plugins/protoview/protocols/keeloq.c | 99 +++-- .../plugins/protoview/protocols/oregon2.c | 90 +++-- .../protoview/protocols/tpms/citroen.c | 46 +-- .../plugins/protoview/protocols/tpms/ford.c | 49 ++- .../protoview/protocols/tpms/renault.c | 104 +++--- .../protoview/protocols/tpms/schrader.c | 52 ++- .../protocols/tpms/schrader_eg53ma4.c | 47 ++- .../plugins/protoview/protocols/tpms/toyota.c | 57 ++- applications/plugins/protoview/raw_samples.c | 47 ++- applications/plugins/protoview/raw_samples.h | 27 +- applications/plugins/protoview/signal.c | 348 ++++++++++-------- applications/plugins/protoview/signal_file.c | 96 +++-- applications/plugins/protoview/ui.c | 99 +++-- applications/plugins/protoview/view_build.c | 211 ++++++----- .../plugins/protoview/view_direct_sampling.c | 45 ++- applications/plugins/protoview/view_info.c | 211 ++++++----- .../plugins/protoview/view_raw_signal.c | 76 ++-- .../plugins/protoview/view_settings.c | 74 ++-- applications/plugins/tama_p1/hal.c | 2 +- applications/plugins/tama_p1/tama.h | 1 - applications/plugins/tama_p1/tama_p1.c | 145 ++++---- .../desktop/animations/animation_manager.c | 35 +- .../desktop/animations/animation_storage.c | 9 +- .../desktop/views/desktop_view_lock_menu.c | 2 +- .../services/dolphin/helpers/dolphin_state.c | 7 +- .../services/power/power_service/power.c | 9 +- applications/settings/about/about.c | 19 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 76 ++-- .../settings/xtreme_settings/xtreme_assets.c | 207 +++++++---- .../settings/xtreme_settings/xtreme_assets.h | 7 +- .../xtreme_settings/xtreme_settings.c | 18 +- .../xtreme_settings/xtreme_settings_app.c | 20 +- 44 files changed, 1735 insertions(+), 1623 deletions(-) diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c index c46c68788..dd684810a 100644 --- a/applications/plugins/dap_link/dap_link.c +++ b/applications/plugins/dap_link/dap_link.c @@ -486,8 +486,7 @@ int32_t dap_link_app(void* p) { if(furi_hal_usb_is_locked()) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); dialog_message_set_text( message, "Disconnect from\nPC or phone to\nuse this function.", diff --git a/applications/plugins/dice/dice.c b/applications/plugins/dice/dice.c index 61aa7b4f5..dc748b68f 100644 --- a/applications/plugins/dice/dice.c +++ b/applications/plugins/dice/dice.c @@ -467,7 +467,6 @@ int32_t dice_app(void* p) { return 255; } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, dice_render_callback, plugin_state); view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue); diff --git a/applications/plugins/orgasmotron/orgasmotron.c b/applications/plugins/orgasmotron/orgasmotron.c index b28f392f5..684fc3d95 100644 --- a/applications/plugins/orgasmotron/orgasmotron.c +++ b/applications/plugins/orgasmotron/orgasmotron.c @@ -40,7 +40,7 @@ int32_t orgasmotron_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); ValueMutex state_mutex; - if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n"); free(plugin_state); return 255; @@ -61,10 +61,10 @@ int32_t orgasmotron_app(void* p) { //int mode = 0; bool processing = true; //while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { - while (processing) { + while(processing) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); - if (event_status == FuriStatusOk) { + if(event_status == FuriStatusOk) { if(event.key == InputKeyBack && event.type == InputTypeShort) { //Exit Application notification_message(notification, &sequence_reset_vibro); @@ -73,53 +73,58 @@ int32_t orgasmotron_app(void* p) { processing = false; //break; } - if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyOk && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 0; } - if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyLeft && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 1; } - if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyRight && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 3; } - if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyUp && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 2; } - if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyDown && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 4; } } - - if (plugin_state->mode == 0) { + + if(plugin_state->mode == 0) { //Stop Vibration notification_message(notification, &sequence_reset_vibro); notification_message(notification, &sequence_reset_green); - } else if (plugin_state->mode == 1) { + } else if(plugin_state->mode == 1) { //Full power notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); - } else if (plugin_state->mode == 2) { + } else if(plugin_state->mode == 2) { //Pulsed Vibration notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(100); notification_message(notification, &sequence_reset_vibro); - } else if (plugin_state->mode == 3) { + } else if(plugin_state->mode == 3) { //Soft power notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(50); notification_message(notification, &sequence_reset_vibro); - } else if (plugin_state->mode == 4) { + } else if(plugin_state->mode == 4) { //Special Sequence - for (int i = 0;i < 15;i++) { + for(int i = 0; i < 15; i++) { notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(50); notification_message(notification, &sequence_reset_vibro); delay(50); } - for (int i = 0;i < 2;i++) { + for(int i = 0; i < 2; i++) { notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(400); diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index f16457e55..d060e2242 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -40,8 +40,8 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry; /* The callback actually just passes the control to the actual active * view callback, after setting up basic stuff like cleaning the screen * and setting color to black. */ -static void render_callback(Canvas *const canvas, void *ctx) { - ProtoViewApp *app = ctx; +static void render_callback(Canvas* const canvas, void* ctx) { + ProtoViewApp* app = ctx; /* Clear screen. */ canvas_set_color(canvas, ColorWhite); @@ -51,14 +51,25 @@ static void render_callback(Canvas *const canvas, void *ctx) { /* Call who is in charge right now. */ switch(app->current_view) { - case ViewRawPulses: render_view_raw_pulses(canvas,app); break; - case ViewInfo: render_view_info(canvas,app); break; + case ViewRawPulses: + render_view_raw_pulses(canvas, app); + break; + case ViewInfo: + render_view_info(canvas, app); + break; case ViewFrequencySettings: case ViewModulationSettings: - render_view_settings(canvas,app); break; - case ViewDirectSampling: render_view_direct_sampling(canvas,app); break; - case ViewBuildMessage: render_view_build_message(canvas,app); break; - default: furi_crash(TAG "Invalid view selected"); break; + render_view_settings(canvas, app); + break; + case ViewDirectSampling: + render_view_direct_sampling(canvas, app); + break; + case ViewBuildMessage: + render_view_build_message(canvas, app); + break; + default: + furi_crash(TAG "Invalid view selected"); + break; } /* Draw the alert box if set. */ @@ -67,10 +78,9 @@ static void render_callback(Canvas *const canvas, void *ctx) { /* Here all we do is putting the events into the queue that will be handled * in the while() loop of the app entry point function. */ -static void input_callback(InputEvent* input_event, void* ctx) -{ - ProtoViewApp *app = ctx; - furi_message_queue_put(app->event_queue,input_event,FuriWaitForever); +static void input_callback(InputEvent* input_event, void* ctx) { + ProtoViewApp* app = ctx; + furi_message_queue_put(app->event_queue, input_event, FuriWaitForever); } /* Called to switch view (when left/right is pressed). Handles @@ -80,15 +90,15 @@ static void input_callback(InputEvent* input_event, void* ctx) * The 'switchto' parameter can be the identifier of a view, or the * special views ViewGoNext and ViewGoPrev in order to move to * the logical next/prev view. */ -static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { +static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { /* Switch to the specified view. */ ProtoViewCurrentView old = app->current_view; - if (switchto == ViewGoNext) { + if(switchto == ViewGoNext) { app->current_view++; - if (app->current_view == ViewLast) app->current_view = 0; - } else if (switchto == ViewGoPrev) { - if (app->current_view == 0) - app->current_view = ViewLast-1; + if(app->current_view == ViewLast) app->current_view = 0; + } else if(switchto == ViewGoPrev) { + if(app->current_view == 0) + app->current_view = ViewLast - 1; else app->current_view--; } else { @@ -103,20 +113,20 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { /* Reset the view private data each time, before calling the enter/exit * callbacks that may want to setup some state. */ - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); /* Call the enter/exit view callbacks if needed. */ - if (old == ViewDirectSampling) view_exit_direct_sampling(app); - if (new == ViewDirectSampling) view_enter_direct_sampling(app); - if (old == ViewBuildMessage) view_exit_build_message(app); - if (new == ViewBuildMessage) view_enter_build_message(app); - if (old == ViewInfo) view_exit_info(app); + if(old == ViewDirectSampling) view_exit_direct_sampling(app); + if(new == ViewDirectSampling) view_enter_direct_sampling(app); + if(old == ViewBuildMessage) view_exit_build_message(app); + if(new == ViewBuildMessage) view_enter_build_message(app); + if(old == ViewInfo) view_exit_info(app); /* The frequency/modulation settings are actually a single view: * as long as the user stays between the two modes of this view we * don't need to call the exit-view callback. */ - if ((old == ViewFrequencySettings && new != ViewModulationSettings) || - (old == ViewModulationSettings && new != ViewFrequencySettings)) + if((old == ViewFrequencySettings && new != ViewModulationSettings) || + (old == ViewModulationSettings && new != ViewFrequencySettings)) view_exit_settings(app); ui_dismiss_alert(app); @@ -125,7 +135,7 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { /* Allocate the application state and initialize a number of stuff. * This is called in the entry point to create the application state. */ ProtoViewApp* protoview_app_alloc() { - ProtoViewApp *app = malloc(sizeof(ProtoViewApp)); + ProtoViewApp* app = malloc(sizeof(ProtoViewApp)); // Init shared data structures RawSamples = raw_samples_alloc(); @@ -148,10 +158,10 @@ ProtoViewApp* protoview_app_alloc() { app->show_text_input = false; app->alert_dismiss_time = 0; app->current_view = ViewRawPulses; - for (int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; + for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; app->direct_sampling_enabled = false; app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN); - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); // Signal found and visualization defaults app->signal_bestlen = 0; @@ -176,17 +186,14 @@ ProtoViewApp* protoview_app_alloc() { app->txrx->environment = subghz_environment_alloc(); subghz_environment_set_protocol_registry( app->txrx->environment, (void*)&protoview_protocol_registry); - app->txrx->receiver = - subghz_receiver_alloc_init(app->txrx->environment); - subghz_receiver_set_filter(app->txrx->receiver, - SubGhzProtocolFlag_Decodable); + app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); + subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); subghz_worker_set_overrun_callback( - app->txrx->worker, - (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); subghz_worker_set_pair_callback( app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); - + app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ @@ -199,7 +206,7 @@ ProtoViewApp* protoview_app_alloc() { /* Free what the application allocated. It is not clear to me if the * Flipper OS, once the application exits, will be able to reclaim space * even if we forget to free something here. */ -void protoview_app_free(ProtoViewApp *app) { +void protoview_app_free(ProtoViewApp* app) { furi_assert(app); // Put CC1101 on sleep, this also restores charging. @@ -218,7 +225,7 @@ void protoview_app_free(ProtoViewApp *app) { subghz_setting_free(app->setting); // Worker stuff. - if (!app->txrx->debug_timer_sampling) { + if(!app->txrx->debug_timer_sampling) { subghz_receiver_free(app->txrx->receiver); subghz_environment_free(app->txrx->environment); subghz_worker_free(app->txrx->worker); @@ -236,8 +243,8 @@ void protoview_app_free(ProtoViewApp *app) { /* Called periodically. Do signal processing here. Data we process here * will be later displayed by the render callback. The side effect of this * function is to scan for signals and set DetectedSamples. */ -static void timer_callback(void *ctx) { - ProtoViewApp *app = ctx; +static void timer_callback(void* ctx) { + ProtoViewApp* app = ctx; uint32_t delta, lastidx = app->signal_last_scan_idx; /* scan_for_signal(), called by this function, deals with a @@ -245,14 +252,14 @@ static void timer_callback(void *ctx) { * cross-boundaries, it is enough if we scan each time the buffer fills * for 50% more compared to the last scan. Thanks to this check we * can avoid scanning too many times to just find the same data. */ - if (lastidx < RawSamples->idx) { + if(lastidx < RawSamples->idx) { delta = RawSamples->idx - lastidx; } else { delta = RawSamples->total - lastidx + RawSamples->idx; } - if (delta < RawSamples->total/2) return; + if(delta < RawSamples->total / 2) return; app->signal_last_scan_idx = RawSamples->idx; - scan_for_signal(app,RawSamples); + scan_for_signal(app, RawSamples); } /* This is the navigation callback we use in the view dispatcher used @@ -265,7 +272,7 @@ static void timer_callback(void *ctx) { * We just need a dummy callback returning false. We believe the * implementation should be changed and if no callback is set, it should be * the same as returning false. */ -static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { +static bool keyboard_view_dispatcher_navigation_callback(void* ctx) { UNUSED(ctx); return false; } @@ -273,10 +280,10 @@ static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { /* App entry point, as specified in application.fam. */ int32_t protoview_app_entry(void* p) { UNUSED(p); - ProtoViewApp *app = protoview_app_alloc(); + ProtoViewApp* app = protoview_app_alloc(); /* Create a timer. We do data analysis in the callback. */ - FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); /* Start listening to signals immediately. */ @@ -291,71 +298,68 @@ int32_t protoview_app_entry(void* p) { InputEvent input; while(app->running) { FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); - if (qstat == FuriStatusOk) { - if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", - input.type, input.key); + if(qstat == FuriStatusOk) { + if(DEBUG_MSG) + FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key); /* Handle navigation here. Then handle view-specific inputs * in the view specific handling function. */ - if (input.type == InputTypeShort && - input.key == InputKeyBack) - { - if (app->current_view != ViewRawPulses) { + if(input.type == InputTypeShort && input.key == InputKeyBack) { + if(app->current_view != ViewRawPulses) { /* If this is not the main app view, go there. */ - app_switch_view(app,ViewRawPulses); + app_switch_view(app, ViewRawPulses); } else { /* If we are in the main app view, warn the user * they needs to long press to really quit. */ - ui_show_alert(app,"Long press to exit",1000); + ui_show_alert(app, "Long press to exit", 1000); } - } else if (input.type == InputTypeLong && - input.key == InputKeyBack) - { + } else if(input.type == InputTypeLong && input.key == InputKeyBack) { app->running = 0; - } else if (input.type == InputTypeShort && - input.key == InputKeyRight && - ui_get_current_subview(app) == 0) - { + } else if( + input.type == InputTypeShort && input.key == InputKeyRight && + ui_get_current_subview(app) == 0) { /* Go to the next view. */ - app_switch_view(app,ViewGoNext); - } else if (input.type == InputTypeShort && - input.key == InputKeyLeft && - ui_get_current_subview(app) == 0) - { + app_switch_view(app, ViewGoNext); + } else if( + input.type == InputTypeShort && input.key == InputKeyLeft && + ui_get_current_subview(app) == 0) { /* Go to the previous view. */ - app_switch_view(app,ViewGoPrev); + app_switch_view(app, ViewGoPrev); } else { /* This is where we pass the control to the currently * active view input processing. */ switch(app->current_view) { case ViewRawPulses: - process_input_raw_pulses(app,input); + process_input_raw_pulses(app, input); break; case ViewInfo: - process_input_info(app,input); + process_input_info(app, input); break; case ViewFrequencySettings: case ViewModulationSettings: - process_input_settings(app,input); + process_input_settings(app, input); break; case ViewDirectSampling: - process_input_direct_sampling(app,input); + process_input_direct_sampling(app, input); break; case ViewBuildMessage: - process_input_build_message(app,input); + process_input_build_message(app, input); + break; + default: + furi_crash(TAG "Invalid view selected"); break; - default: furi_crash(TAG "Invalid view selected"); break; } } } else { /* Useful to understand if the app is still alive when it * does not respond because of bugs. */ - if (DEBUG_MSG) { - static int c = 0; c++; - if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + if(DEBUG_MSG) { + static int c = 0; + c++; + if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); } } - if (app->show_text_input) { + if(app->show_text_input) { /* Remove our viewport: we need to use a view dispatcher * in order to show the standard Flipper keyboard. */ gui_remove_view_port(app->gui, app->view_port); @@ -368,11 +372,11 @@ int32_t protoview_app_entry(void* p) { * otherwise when the user presses back on the keyboard to * abort, the dispatcher will not stop. */ view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, - keyboard_view_dispatcher_navigation_callback); + app->view_dispatcher, keyboard_view_dispatcher_navigation_callback); app->text_input = text_input_alloc(); - view_dispatcher_set_event_callback_context(app->view_dispatcher,app); - view_dispatcher_add_view(app->view_dispatcher, 0, text_input_get_view(app->text_input)); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_add_view( + app->view_dispatcher, 0, text_input_get_view(app->text_input)); view_dispatcher_switch_to_view(app->view_dispatcher, 0); /* Setup the text input view. The different parameters are set @@ -388,7 +392,8 @@ int32_t protoview_app_entry(void* p) { false); /* Run the dispatcher with the keyboard. */ - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); view_dispatcher_run(app->view_dispatcher); /* Undo all it: remove the view from the dispatcher, free it @@ -406,7 +411,7 @@ int32_t protoview_app_entry(void* p) { } /* App no longer running. Shut down and free. */ - if (app->txrx->txrx_state == TxRxStateRx) { + if(app->txrx->txrx_state == TxRxStateRx) { FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting."); radio_rx_end(app); radio_sleep(app); @@ -416,4 +421,3 @@ int32_t protoview_app_entry(void* p) { protoview_app_free(app); return 0; } - diff --git a/applications/plugins/protoview/app.h b/applications/plugins/protoview/app.h index 33bd85eb4..85007e345 100644 --- a/applications/plugins/protoview/app.h +++ b/applications/plugins/protoview/app.h @@ -66,11 +66,11 @@ typedef enum { /* ================================== RX/TX ================================= */ typedef struct { - const char *name; // Name to show to the user. - const char *id; // Identifier in the Flipper API/file. - FuriHalSubGhzPreset preset; // The preset ID. - uint8_t *custom; // If not null, a set of registers for - // the CC1101, specifying a custom preset. + const char* name; // Name to show to the user. + const char* id; // Identifier in the Flipper API/file. + FuriHalSubGhzPreset preset; // The preset ID. + uint8_t* custom; // If not null, a set of registers for + // the CC1101, specifying a custom preset. } ProtoViewModulation; extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ @@ -79,19 +79,19 @@ extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ * It receives data and we get our protocol "feed" callback called * with the level (1 or 0) and duration. */ struct ProtoViewTxRx { - bool freq_mod_changed; /* The user changed frequency and/or modulation + bool freq_mod_changed; /* The user changed frequency and/or modulation from the interface. There is to restart the radio with the right parameters. */ - SubGhzWorker* worker; /* Our background worker. */ + SubGhzWorker* worker; /* Our background worker. */ SubGhzEnvironment* environment; SubGhzReceiver* receiver; TxRxState txrx_state; /* Receiving, idle or sleeping? */ /* Timer sampling mode state. */ - bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only + bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only for testing. */ uint32_t last_g0_change_time; /* Last high->low (or reverse) switch. */ - bool last_g0_value; /* Current value (high or low): we are + bool last_g0_value; /* Current value (high or low): we are checking the duration in the timer handler. */ }; @@ -103,44 +103,44 @@ typedef struct ProtoViewTxRx ProtoViewTxRx; #define ALERT_MAX_LEN 32 struct ProtoViewApp { /* GUI */ - Gui *gui; - NotificationApp *notification; - ViewPort *view_port; /* We just use a raw viewport and we render + Gui* gui; + NotificationApp* notification; + ViewPort* view_port; /* We just use a raw viewport and we render everything into the low level canvas. */ - ProtoViewCurrentView current_view; /* Active left-right view ID. */ - int current_subview[ViewLast]; /* Active up-down subview ID. */ - FuriMessageQueue *event_queue; /* Keypress events go here. */ + ProtoViewCurrentView current_view; /* Active left-right view ID. */ + int current_subview[ViewLast]; /* Active up-down subview ID. */ + FuriMessageQueue* event_queue; /* Keypress events go here. */ /* Input text state. */ - ViewDispatcher *view_dispatcher; /* Used only when we want to show + ViewDispatcher* view_dispatcher; /* Used only when we want to show the text_input view for a moment. Otherwise it is set to null. */ - TextInput *text_input; + TextInput* text_input; bool show_text_input; - char *text_input_buffer; + char* text_input_buffer; uint32_t text_input_buffer_len; void (*text_input_done_callback)(void*); /* Alert state. */ - uint32_t alert_dismiss_time; /* Millisecond when the alert will be + uint32_t alert_dismiss_time; /* Millisecond when the alert will be no longer shown. Or zero if the alert is currently not set at all. */ char alert_text[ALERT_MAX_LEN]; /* Alert content. */ /* Radio related. */ - ProtoViewTxRx *txrx; /* Radio state. */ - SubGhzSetting *setting; /* A list of valid frequencies. */ + ProtoViewTxRx* txrx; /* Radio state. */ + SubGhzSetting* setting; /* A list of valid frequencies. */ /* Generic app state. */ - int running; /* Once false exists the app. */ + int running; /* Once false exists the app. */ uint32_t signal_bestlen; /* Longest coherent signal observed so far. */ uint32_t signal_last_scan_idx; /* Index of the buffer last time we performed the scan. */ - bool signal_decoded; /* Was the current signal decoded? */ - ProtoViewMsgInfo *msg_info; /* Decoded message info if not NULL. */ + bool signal_decoded; /* Was the current signal decoded? */ + ProtoViewMsgInfo* msg_info; /* Decoded message info if not NULL. */ bool direct_sampling_enabled; /* This special view needs an explicit acknowledge to work. */ - void *view_privdata; /* This is a piece of memory of total size + void* view_privdata; /* This is a piece of memory of total size PROTOVIEW_VIEW_PRIVDATA_LEN that it is initialized to zero when we switch to a a new view. While the view we are using @@ -149,12 +149,12 @@ struct ProtoViewApp { the pointer to a few specific-data structure. */ /* Raw view apps state. */ - uint32_t us_scale; /* microseconds per pixel. */ - uint32_t signal_offset; /* Long press left/right panning in raw view. */ + uint32_t us_scale; /* microseconds per pixel. */ + uint32_t signal_offset; /* Long press left/right panning in raw view. */ /* Configuration view app state. */ - uint32_t frequency; /* Current frequency. */ - uint8_t modulation; /* Current modulation ID, array index in the + uint32_t frequency; /* Current frequency. */ + uint8_t modulation; /* Current modulation ID, array index in the ProtoViewModulations table. */ }; @@ -165,18 +165,18 @@ struct ProtoViewApp { * in the message info view. */ #define PROTOVIEW_MSG_STR_LEN 32 typedef struct ProtoViewMsgInfo { - ProtoViewDecoder *decoder; /* The decoder that decoded the message. */ - ProtoViewFieldSet *fieldset; /* Decoded fields. */ + ProtoViewDecoder* decoder; /* The decoder that decoded the message. */ + ProtoViewFieldSet* fieldset; /* Decoded fields. */ /* Low level information of the detected signal: the following are filled * by the protocol decoding function: */ - uint32_t start_off; /* Pulses start offset in the bitmap. */ - uint32_t pulses_count; /* Number of pulses of the full message. */ + uint32_t start_off; /* Pulses start offset in the bitmap. */ + uint32_t pulses_count; /* Number of pulses of the full message. */ /* The following are passed already filled to the decoder. */ - uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ + uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ /* The following are filled by ProtoView core after the decoder returned * success. */ - uint8_t *bits; /* Bitmap with the signal. */ - uint32_t bits_bytes; /* Number of full bytes in the bitmap, that + uint8_t* bits; /* Bitmap with the signal. */ + uint32_t bits_bytes; /* Number of full bytes in the bitmap, that is 'pulses_count/8' rounded to the next integer. */ } ProtoViewMsgInfo; @@ -196,28 +196,28 @@ typedef enum { typedef struct { ProtoViewFieldType type; - uint32_t len; // Depends on type: - // Bits for integers (signed,unsigned,binary,hex). - // Number of characters for strings. - // Number of nibbles for bytes (1 for each 4 bits). - // Number of digits after dot for floats. - char *name; // Field name. + uint32_t len; // Depends on type: + // Bits for integers (signed,unsigned,binary,hex). + // Number of characters for strings. + // Number of nibbles for bytes (1 for each 4 bits). + // Number of digits after dot for floats. + char* name; // Field name. union { - char *str; // String type. - int64_t value; // Signed integer type. - uint64_t uvalue; // Unsigned integer type. - uint8_t *bytes; // Raw bytes type. - float fvalue; // Float type. + char* str; // String type. + int64_t value; // Signed integer type. + uint64_t uvalue; // Unsigned integer type. + uint8_t* bytes; // Raw bytes type. + float fvalue; // Float type. }; } ProtoViewField; typedef struct ProtoViewFieldSet { - ProtoViewField **fields; + ProtoViewField** fields; uint32_t numfields; } ProtoViewFieldSet; typedef struct ProtoViewDecoder { - const char *name; /* Protocol name. */ + const char* name; /* Protocol name. */ /* The decode function takes a buffer that is actually a bitmap, with * high and low levels represented as 0 and 1. The number of high/low * pulses represented by the bitmap is passed as the 'numbits' argument, @@ -225,15 +225,15 @@ typedef struct ProtoViewDecoder { * 'bits'. So 'numbytes' is mainly useful to pass as argument to other * functions that perform bit extraction with bound checking, such as * bitmap_get() and so forth. */ - bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info); + bool (*decode)(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info); /* This method is used by the decoder to return the fields it needs * in order to build a new message. This way the message builder view * can ask the user to fill the right set of fields of the specified * type. */ - void (*get_fields)(ProtoViewFieldSet *fields); + void (*get_fields)(ProtoViewFieldSet* fields); /* This method takes the fields supported by the decoder, and * renders a message in 'samples'. */ - void (*build_message)(RawSamplesBuffer *samples, ProtoViewFieldSet *fields); + void (*build_message)(RawSamplesBuffer* samples, ProtoViewFieldSet* fields); } ProtoViewDecoder; extern RawSamplesBuffer *RawSamples, *DetectedSamples; @@ -244,76 +244,118 @@ uint32_t radio_rx(ProtoViewApp* app); void radio_idle(ProtoViewApp* app); void radio_rx_end(ProtoViewApp* app); void radio_sleep(ProtoViewApp* app); -void raw_sampling_worker_start(ProtoViewApp *app); -void raw_sampling_worker_stop(ProtoViewApp *app); -void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx); +void raw_sampling_worker_start(ProtoViewApp* app); +void raw_sampling_worker_stop(ProtoViewApp* app); +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); -void reset_current_signal(ProtoViewApp *app); -void scan_for_signal(ProtoViewApp *app,RawSamplesBuffer *source); -bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos); -void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val); -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, uint8_t *s, uint32_t slen, uint32_t soff, uint32_t count); -void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat); -void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len); -bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits); -uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits); -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern); -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous); -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app); -void free_msg_info(ProtoViewMsgInfo *i); +void reset_current_signal(ProtoViewApp* app); +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source); +bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos); +void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val); +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count); +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat); +void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len); +bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits); +uint32_t bitmap_seek_bits( + uint8_t* b, + uint32_t blen, + uint32_t startpos, + uint32_t maxbits, + const char* bits); +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t offset, + const char* zero_pattern, + const char* one_pattern); +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous); +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app); +void free_msg_info(ProtoViewMsgInfo* i); /* signal_file.c */ -bool save_signal(ProtoViewApp *app, const char *filename); +bool save_signal(ProtoViewApp* app, const char* filename); /* view_*.c */ -void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app); -void process_input_raw_pulses(ProtoViewApp *app, InputEvent input); -void render_view_settings(Canvas *const canvas, ProtoViewApp *app); -void process_input_settings(ProtoViewApp *app, InputEvent input); -void render_view_info(Canvas *const canvas, ProtoViewApp *app); -void process_input_info(ProtoViewApp *app, InputEvent input); -void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app); -void process_input_direct_sampling(ProtoViewApp *app, InputEvent input); -void render_view_build_message(Canvas *const canvas, ProtoViewApp *app); -void process_input_build_message(ProtoViewApp *app, InputEvent input); -void view_enter_build_message(ProtoViewApp *app); -void view_exit_build_message(ProtoViewApp *app); -void view_enter_direct_sampling(ProtoViewApp *app); -void view_exit_direct_sampling(ProtoViewApp *app); -void view_exit_settings(ProtoViewApp *app); -void view_exit_info(ProtoViewApp *app); -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur); +void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app); +void process_input_raw_pulses(ProtoViewApp* app, InputEvent input); +void render_view_settings(Canvas* const canvas, ProtoViewApp* app); +void process_input_settings(ProtoViewApp* app, InputEvent input); +void render_view_info(Canvas* const canvas, ProtoViewApp* app); +void process_input_info(ProtoViewApp* app, InputEvent input); +void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app); +void process_input_direct_sampling(ProtoViewApp* app, InputEvent input); +void render_view_build_message(Canvas* const canvas, ProtoViewApp* app); +void process_input_build_message(ProtoViewApp* app, InputEvent input); +void view_enter_build_message(ProtoViewApp* app); +void view_exit_build_message(ProtoViewApp* app); +void view_enter_direct_sampling(ProtoViewApp* app); +void view_exit_direct_sampling(ProtoViewApp* app); +void view_exit_settings(ProtoViewApp* app); +void view_exit_info(ProtoViewApp* app); +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur); /* ui.c */ -int ui_get_current_subview(ProtoViewApp *app); -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview); -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview); -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)); -void ui_dismiss_keyboard(ProtoViewApp *app); -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl); -void ui_dismiss_alert(ProtoViewApp *app); -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app); -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color); +int ui_get_current_subview(ProtoViewApp* app); +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview); +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview); +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)); +void ui_dismiss_keyboard(ProtoViewApp* app); +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl); +void ui_dismiss_alert(ProtoViewApp* app); +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app); +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color); /* fields.c */ -void fieldset_free(ProtoViewFieldSet *fs); -ProtoViewFieldSet *fieldset_new(void); -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits); -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s); -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count); -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot); -const char *field_get_type_name(ProtoViewField *f); -int field_to_string(char *buf, size_t len, ProtoViewField *f); -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len); -bool field_incr_value(ProtoViewField *f, int incr); -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, ProtoViewFieldSet *src); -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src); +void fieldset_free(ProtoViewFieldSet* fs); +ProtoViewFieldSet* fieldset_new(void); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot); +const char* field_get_type_name(ProtoViewField* f); +int field_to_string(char* buf, size_t len, ProtoViewField* f); +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len); +bool field_incr_value(ProtoViewField* f, int incr); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src); +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src); /* crc.c */ -uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly); +uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly); diff --git a/applications/plugins/protoview/app_subghz.c b/applications/plugins/protoview/app_subghz.c index 55905e8a3..73e0e16ae 100644 --- a/applications/plugins/protoview/app_subghz.c +++ b/applications/plugins/protoview/app_subghz.c @@ -9,18 +9,20 @@ #include #include -void raw_sampling_worker_start(ProtoViewApp *app); -void raw_sampling_worker_stop(ProtoViewApp *app); +void raw_sampling_worker_start(ProtoViewApp* app); +void raw_sampling_worker_stop(ProtoViewApp* app); ProtoViewModulation ProtoViewModulations[] = { - {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", - FuriHalSubGhzPresetOok650Async, NULL}, - {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", - FuriHalSubGhzPresetOok270Async, NULL}, - {"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async", - FuriHalSubGhzPreset2FSKDev238Async, NULL}, - {"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async", - FuriHalSubGhzPreset2FSKDev476Async, NULL}, + {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL}, + {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL}, + {"2FSK 2.38Khz", + "FuriHalSubGhzPreset2FSKDev238Async", + FuriHalSubGhzPreset2FSKDev238Async, + NULL}, + {"2FSK 47.6Khz", + "FuriHalSubGhzPreset2FSKDev476Async", + FuriHalSubGhzPreset2FSKDev476Async, + NULL}, {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs}, {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs}, {"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs}, @@ -44,12 +46,10 @@ void radio_begin(ProtoViewApp* app) { /* The CC1101 preset can be either one of the standard presets, if * the modulation "custom" field is NULL, or a custom preset we * defined in custom_presets.h. */ - if (ProtoViewModulations[app->modulation].custom == NULL) { - furi_hal_subghz_load_preset( - ProtoViewModulations[app->modulation].preset); + if(ProtoViewModulations[app->modulation].custom == NULL) { + furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset); } else { - furi_hal_subghz_load_custom_preset( - ProtoViewModulations[app->modulation].custom); + furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); } furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = TxRxStateIDLE; @@ -61,10 +61,10 @@ void radio_begin(ProtoViewApp* app) { uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { - furi_crash(TAG" Incorrect RX frequency."); + furi_crash(TAG " Incorrect RX frequency."); } - if (app->txrx->txrx_state == TxRxStateRx) return app->frequency; + if(app->txrx->txrx_state == TxRxStateRx) return app->frequency; furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); @@ -72,10 +72,8 @@ uint32_t radio_rx(ProtoViewApp* app) { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); furi_hal_subghz_rx(); - if (!app->txrx->debug_timer_sampling) { - - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, - app->txrx->worker); + if(!app->txrx->debug_timer_sampling) { + furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); subghz_worker_start(app->txrx->worker); } else { raw_sampling_worker_start(app); @@ -88,8 +86,8 @@ uint32_t radio_rx(ProtoViewApp* app) { void radio_rx_end(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { - if (!app->txrx->debug_timer_sampling) { + if(app->txrx->txrx_state == TxRxStateRx) { + if(!app->txrx->debug_timer_sampling) { if(subghz_worker_is_running(app->txrx->worker)) { subghz_worker_stop(app->txrx->worker); furi_hal_subghz_stop_async_rx(); @@ -105,7 +103,7 @@ void radio_rx_end(ProtoViewApp* app) { /* Put radio on sleep. */ void radio_sleep(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { + if(app->txrx->txrx_state == TxRxStateRx) { /* We can't go from having an active RX worker to sleeping. * Stop the RX subsystems first. */ radio_rx_end(app); @@ -120,10 +118,10 @@ void radio_sleep(ProtoViewApp* app) { /* This function suspends the current RX state, switches to TX mode, * transmits the signal provided by the callback data_feeder, and later * restores the RX state if there was one. */ -void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx) { +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx) { TxRxState oldstate = app->txrx->txrx_state; - if (oldstate == TxRxStateRx) radio_rx_end(app); + if(oldstate == TxRxStateRx) radio_rx_end(app); radio_begin(app); furi_hal_subghz_idle(); @@ -138,7 +136,7 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder furi_hal_subghz_idle(); radio_begin(app); - if (oldstate == TxRxStateRx) radio_rx(app); + if(oldstate == TxRxStateRx) radio_rx(app); } /* ============================= Raw sampling mode ============================= @@ -148,15 +146,15 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder * Flipper system. * ===========================================================================*/ -void protoview_timer_isr(void *ctx) { - ProtoViewApp *app = ctx; +void protoview_timer_isr(void* ctx) { + ProtoViewApp* app = ctx; bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if (app->txrx->last_g0_value != level) { + if(app->txrx->last_g0_value != level) { uint32_t now = DWT->CYCCNT; uint32_t dur = now - app->txrx->last_g0_change_time; dur /= furi_hal_cortex_instructions_per_microsecond(); - if (dur > 15000) dur = 15000; + if(dur > 15000) dur = 15000; raw_samples_add(RawSamples, app->txrx->last_g0_value, dur); app->txrx->last_g0_value = level; app->txrx->last_g0_change_time = now; @@ -164,13 +162,13 @@ void protoview_timer_isr(void *ctx) { LL_TIM_ClearFlag_UPDATE(TIM2); } -void raw_sampling_worker_start(ProtoViewApp *app) { +void raw_sampling_worker_start(ProtoViewApp* app) { UNUSED(app); LL_TIM_InitTypeDef tim_init = { - .Prescaler = 63, /* CPU frequency is ~64Mhz. */ + .Prescaler = 63, /* CPU frequency is ~64Mhz. */ .CounterMode = LL_TIM_COUNTERMODE_UP, - .Autoreload = 5, /* Sample every 5 us */ + .Autoreload = 5, /* Sample every 5 us */ }; LL_TIM_Init(TIM2, &tim_init); @@ -183,7 +181,7 @@ void raw_sampling_worker_start(ProtoViewApp *app) { FURI_LOG_E(TAG, "Timer enabled"); } -void raw_sampling_worker_stop(ProtoViewApp *app) { +void raw_sampling_worker_stop(ProtoViewApp* app) { UNUSED(app); FURI_CRITICAL_ENTER(); LL_TIM_DisableCounter(TIM2); diff --git a/applications/plugins/protoview/crc.c b/applications/plugins/protoview/crc.c index 38a809e10..94d482972 100644 --- a/applications/plugins/protoview/crc.c +++ b/applications/plugins/protoview/crc.c @@ -3,14 +3,13 @@ /* CRC8 with the specified initialization value 'init' and * polynomial 'poly'. */ -uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly) -{ +uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) { uint8_t crc = init; size_t i, j; - for (i = 0; i < len; i++) { + for(i = 0; i < len; i++) { crc ^= data[i]; - for (j = 0; j < 8; j++) { - if ((crc & 0x80) != 0) + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) crc = (uint8_t)((crc << 1) ^ poly); else crc <<= 1; diff --git a/applications/plugins/protoview/custom_presets.h b/applications/plugins/protoview/custom_presets.h index cb9a421c6..00aa49945 100644 --- a/applications/plugins/protoview/custom_presets.h +++ b/applications/plugins/protoview/custom_presets.h @@ -76,7 +76,8 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz {CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz @@ -106,8 +107,10 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* This is like the default Flipper OOK 640Khz bandwidth preset, but * the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */ @@ -156,8 +159,10 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { {0, 0}, /* CC1101 OOK PATABLE. */ - {0, 0xC0}, {0,0}, {0,0}, {0,0} -}; + {0, 0xC0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { @@ -174,7 +179,8 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud {CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp {CC1101_DEVIATN, 0x41}, // Deviation 28kHz @@ -204,8 +210,10 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { @@ -250,6 +258,7 @@ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; - + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; diff --git a/applications/plugins/protoview/data_feed.c b/applications/plugins/protoview/data_feed.c index a3bed238e..81d1a8020 100644 --- a/applications/plugins/protoview/data_feed.c +++ b/applications/plugins/protoview/data_feed.c @@ -14,7 +14,7 @@ const SubGhzProtocol subghz_protocol_protoview; /* The feed() method puts data in the RawSamples global (protected by * a mutex). */ -extern RawSamplesBuffer *RawSamples; +extern RawSamplesBuffer* RawSamples; /* This is totally dummy: we just define the decoder base for the async * system to work but we don't really use it if not to collect raw @@ -26,8 +26,7 @@ typedef struct SubGhzProtocolDecoderprotoview { void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - SubGhzProtocolDecoderprotoview* instance = - malloc(sizeof(SubGhzProtocolDecoderprotoview)); + SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview)); instance->base.protocol = &subghz_protocol_protoview; return instance; } @@ -66,8 +65,7 @@ uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) { bool subghz_protocol_decoder_protoview_serialize( void* context, FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) -{ + SubGhzRadioPreset* preset) { UNUSED(context); UNUSED(flipper_format); UNUSED(preset); @@ -75,15 +73,13 @@ bool subghz_protocol_decoder_protoview_serialize( } /* Not used. */ -bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) -{ +bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) { UNUSED(context); UNUSED(flipper_format); return false; } -void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) -{ +void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) { furi_assert(context); furi_string_cat_printf(output, "Protoview"); } @@ -116,5 +112,4 @@ const SubGhzProtocol* protoview_protocol_registry_items[] = { const SubGhzProtocolRegistry protoview_protocol_registry = { .items = protoview_protocol_registry_items, - .size = COUNT_OF(protoview_protocol_registry_items) -}; + .size = COUNT_OF(protoview_protocol_registry_items)}; diff --git a/applications/plugins/protoview/fields.c b/applications/plugins/protoview/fields.c index bc62cda54..47d573f4f 100644 --- a/applications/plugins/protoview/fields.c +++ b/applications/plugins/protoview/fields.c @@ -7,8 +7,8 @@ /* Create a new field of the specified type. Without populating its * type-specific value. */ -static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { - ProtoViewField *f = malloc(sizeof(*f)); +static ProtoViewField* field_new(ProtoViewFieldType type, const char* name) { + ProtoViewField* f = malloc(sizeof(*f)); f->type = type; f->name = strdup(name); return f; @@ -16,72 +16,80 @@ static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { /* Free only the auxiliary data of a field, used to represent the * current type. Name and type are not touched. */ -static void field_free_aux_data(ProtoViewField *f) { +static void field_free_aux_data(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: free(f->str); break; - case FieldTypeBytes: free(f->bytes); break; - default: break; // Nothing to free for other types. + case FieldTypeStr: + free(f->str); + break; + case FieldTypeBytes: + free(f->bytes); + break; + default: + break; // Nothing to free for other types. } } /* Free a field an associated data. */ -static void field_free(ProtoViewField *f) { +static void field_free(ProtoViewField* f) { field_free_aux_data(f); free(f->name); free(f); } /* Return the type of the field as string. */ -const char *field_get_type_name(ProtoViewField *f) { +const char* field_get_type_name(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: return "str"; - case FieldTypeSignedInt: return "int"; - case FieldTypeUnsignedInt: return "uint"; - case FieldTypeBinary: return "bin"; - case FieldTypeHex: return "hex"; - case FieldTypeBytes: return "bytes"; - case FieldTypeFloat: return "float"; + case FieldTypeStr: + return "str"; + case FieldTypeSignedInt: + return "int"; + case FieldTypeUnsignedInt: + return "uint"; + case FieldTypeBinary: + return "bin"; + case FieldTypeHex: + return "hex"; + case FieldTypeBytes: + return "bytes"; + case FieldTypeFloat: + return "float"; } return "unknown"; } /* Set a string representation of the specified field in buf. */ -int field_to_string(char *buf, size_t len, ProtoViewField *f) { +int field_to_string(char* buf, size_t len, ProtoViewField* f) { switch(f->type) { case FieldTypeStr: - return snprintf(buf,len,"%s", f->str); + return snprintf(buf, len, "%s", f->str); case FieldTypeSignedInt: - return snprintf(buf,len,"%lld", (long long) f->value); + return snprintf(buf, len, "%lld", (long long)f->value); case FieldTypeUnsignedInt: - return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue); - case FieldTypeBinary: - { - uint64_t test_bit = (1 << (f->len-1)); - uint64_t idx = 0; - while(idx < len-1 && test_bit) { - buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; - test_bit >>= 1; - } - buf[idx] = 0; - return idx; + return snprintf(buf, len, "%llu", (unsigned long long)f->uvalue); + case FieldTypeBinary: { + uint64_t test_bit = (1 << (f->len - 1)); + uint64_t idx = 0; + while(idx < len - 1 && test_bit) { + buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; + test_bit >>= 1; } + buf[idx] = 0; + return idx; + } case FieldTypeHex: - return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue); + return snprintf(buf, len, "%*llX", (int)(f->len + 7) / 8, f->uvalue); case FieldTypeFloat: return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue); - case FieldTypeBytes: - { - uint64_t idx = 0; - while(idx < len-1 && idx < f->len) { - const char *charset = "0123456789ABCDEF"; - uint32_t nibble = idx & 1 ? - (f->bytes[idx/2] & 0xf) : - (f->bytes[idx/2] >> 4); - buf[idx++] = charset[nibble]; - } - buf[idx] = 0; - return idx; + case FieldTypeBytes: { + uint64_t idx = 0; + while(idx < len - 1 && idx < f->len) { + const char* charset = "0123456789ABCDEF"; + uint32_t nibble = idx & 1 ? (f->bytes[idx / 2] & 0xf) : (f->bytes[idx / 2] >> 4); + buf[idx++] = charset[nibble]; } + buf[idx] = 0; + return idx; + } } return 0; } @@ -96,7 +104,7 @@ int field_to_string(char *buf, size_t len, ProtoViewField *f) { * The function returns true if the filed was successfully set to the * new value, otherwise if the specified value is invalid for the * field type, false is returned. */ -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len) { // Initialize values to zero since the Flipper sscanf() implementation // is fuzzy... may populate only part of the value. long long val = 0; @@ -107,80 +115,78 @@ bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { case FieldTypeStr: free(f->str); f->len = len; - f->str = malloc(len+1); - memcpy(f->str,buf,len+1); + f->str = malloc(len + 1); + memcpy(f->str, buf, len + 1); break; case FieldTypeSignedInt: - if (!sscanf(buf,"%lld",&val)) return false; + if(!sscanf(buf, "%lld", &val)) return false; f->value = val; break; case FieldTypeUnsignedInt: - if (!sscanf(buf,"%llu",&uval)) return false; + if(!sscanf(buf, "%llu", &uval)) return false; f->uvalue = uval; break; - case FieldTypeBinary: - { - uint64_t bit_to_set = (1 << (len-1)); - uint64_t idx = 0; - uval = 0; - while(buf[idx]) { - if (buf[idx] == '1') uval |= bit_to_set; - else if (buf[idx] != '0') return false; - bit_to_set >>= 1; - idx++; - } - f->uvalue = uval; + case FieldTypeBinary: { + uint64_t bit_to_set = (1 << (len - 1)); + uint64_t idx = 0; + uval = 0; + while(buf[idx]) { + if(buf[idx] == '1') + uval |= bit_to_set; + else if(buf[idx] != '0') + return false; + bit_to_set >>= 1; + idx++; } - break; + f->uvalue = uval; + } break; case FieldTypeHex: - if (!sscanf(buf,"%llx",&uval) && - !sscanf(buf,"%llX",&uval)) return false; + if(!sscanf(buf, "%llx", &uval) && !sscanf(buf, "%llX", &uval)) return false; f->uvalue = uval; break; case FieldTypeFloat: - if (!sscanf(buf,"%f",&fval)) return false; + if(!sscanf(buf, "%f", &fval)) return false; f->fvalue = fval; break; - case FieldTypeBytes: - { - if (len > f->len) return false; - uint64_t idx = 0; - while(buf[idx]) { - uint8_t nibble = 0; - char c = toupper(buf[idx]); - if (c >= '0' && c <= '9') nibble = c-'0'; - else if (c >= 'A' && c <= 'F') nibble = 10+(c-'A'); - else return false; + case FieldTypeBytes: { + if(len > f->len) return false; + uint64_t idx = 0; + while(buf[idx]) { + uint8_t nibble = 0; + char c = toupper(buf[idx]); + if(c >= '0' && c <= '9') + nibble = c - '0'; + else if(c >= 'A' && c <= 'F') + nibble = 10 + (c - 'A'); + else + return false; - if (idx & 1) { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0xF0) | nibble; - } else { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0x0F) | (nibble<<4); - } - idx++; + if(idx & 1) { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0xF0) | nibble; + } else { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0x0F) | (nibble << 4); } - buf[idx] = 0; + idx++; } - break; + buf[idx] = 0; + } break; } return true; } /* Set the 'dst' field to contain a copy of the value of the 'src' * field. The field name is not modified. */ -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src) { field_free_aux_data(dst); dst->type = src->type; dst->len = src->len; - switch(src->type) { + switch(src->type) { case FieldTypeStr: dst->str = strdup(src->str); break; case FieldTypeBytes: dst->bytes = malloc(src->len); - memcpy(dst->bytes,src->bytes,dst->len); + memcpy(dst->bytes, src->bytes, dst->len); break; case FieldTypeSignedInt: dst->value = src->value; @@ -199,159 +205,159 @@ void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { /* Increment the specified field value of 'incr'. If the field type * does not support increments false is returned, otherwise the * action is performed. */ -bool field_incr_value(ProtoViewField *f, int incr) { +bool field_incr_value(ProtoViewField* f, int incr) { switch(f->type) { - case FieldTypeStr: return false; - case FieldTypeSignedInt: { - /* Wrap around depending on the number of bits (f->len) + case FieldTypeStr: + return false; + case FieldTypeSignedInt: { + /* Wrap around depending on the number of bits (f->len) * the integer was declared to have. */ - int64_t max = (1ULL << (f->len-1))-1; - int64_t min = -max-1; - int64_t v = (int64_t)f->value + incr; - if (v > max) v = min+(v-max-1); - if (v < min) v = max+(v-min+1); - f->value = v; - break; - } - case FieldTypeBinary: - case FieldTypeHex: - case FieldTypeUnsignedInt: { - /* Wrap around like for the unsigned case, but here + int64_t max = (1ULL << (f->len - 1)) - 1; + int64_t min = -max - 1; + int64_t v = (int64_t)f->value + incr; + if(v > max) v = min + (v - max - 1); + if(v < min) v = max + (v - min + 1); + f->value = v; + break; + } + case FieldTypeBinary: + case FieldTypeHex: + case FieldTypeUnsignedInt: { + /* Wrap around like for the unsigned case, but here * is simpler. */ - uint64_t max = (1ULL << f->len)-1; // Broken for 64 bits. - uint64_t uv = (uint64_t)f->value + incr; - if (uv > max) uv = uv & max; - f->uvalue = uv; - break; - } - case FieldTypeFloat: - f->fvalue += incr; - break; - case FieldTypeBytes: { - // For bytes we only support single unit increments. - if (incr != -1 && incr != 1) return false; - for (int j = f->len-1; j >= 0; j--) { - uint8_t nibble = (j&1) ? (f->bytes[j/2] & 0x0F) : - ((f->bytes[j/2] & 0xF0) >> 4); + uint64_t max = (1ULL << f->len) - 1; // Broken for 64 bits. + uint64_t uv = (uint64_t)f->value + incr; + if(uv > max) uv = uv & max; + f->uvalue = uv; + break; + } + case FieldTypeFloat: + f->fvalue += incr; + break; + case FieldTypeBytes: { + // For bytes we only support single unit increments. + if(incr != -1 && incr != 1) return false; + for(int j = f->len - 1; j >= 0; j--) { + uint8_t nibble = (j & 1) ? (f->bytes[j / 2] & 0x0F) : ((f->bytes[j / 2] & 0xF0) >> 4); - nibble += incr; - nibble &= 0x0F; + nibble += incr; + nibble &= 0x0F; - f->bytes[j/2] = (j&1) ? ((f->bytes[j/2] & 0xF0) | nibble) : - ((f->bytes[j/2] & 0x0F) | (nibble<<4)); + f->bytes[j / 2] = (j & 1) ? ((f->bytes[j / 2] & 0xF0) | nibble) : + ((f->bytes[j / 2] & 0x0F) | (nibble << 4)); - /* Propagate the operation on overflow of this nibble. */ - if ((incr == 1 && nibble == 0) || - (incr == -1 && nibble == 0xf)) - { - continue; - } - break; // Otherwise stop the loop here. + /* Propagate the operation on overflow of this nibble. */ + if((incr == 1 && nibble == 0) || (incr == -1 && nibble == 0xf)) { + continue; } - break; + break; // Otherwise stop the loop here. } + break; + } } return true; } - /* Free a field set and its contained fields. */ -void fieldset_free(ProtoViewFieldSet *fs) { - for (uint32_t j = 0; j < fs->numfields; j++) - field_free(fs->fields[j]); +void fieldset_free(ProtoViewFieldSet* fs) { + for(uint32_t j = 0; j < fs->numfields; j++) field_free(fs->fields[j]); free(fs->fields); free(fs); } /* Allocate and init an empty field set. */ -ProtoViewFieldSet *fieldset_new(void) { - ProtoViewFieldSet *fs = malloc(sizeof(*fs)); +ProtoViewFieldSet* fieldset_new(void) { + ProtoViewFieldSet* fs = malloc(sizeof(*fs)); fs->numfields = 0; fs->fields = NULL; return fs; } /* Append an already allocated field at the end of the specified field set. */ -static void fieldset_add_field(ProtoViewFieldSet *fs, ProtoViewField *field) { +static void fieldset_add_field(ProtoViewFieldSet* fs, ProtoViewField* field) { fs->numfields++; - fs->fields = realloc(fs->fields,sizeof(ProtoViewField*)*fs->numfields); - fs->fields[fs->numfields-1] = field; + fs->fields = realloc(fs->fields, sizeof(ProtoViewField*) * fs->numfields); + fs->fields[fs->numfields - 1] = field; } /* Allocate and append an integer field. */ -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeSignedInt,name); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeSignedInt, name); f->value = val; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append an unsigned field. */ -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeUnsignedInt,name); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeUnsignedInt, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a hex field. This is an unsigned number but * with an hex representation. */ -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeHex,name); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeHex, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bin field. This is an unsigned number but * with a binary representation. */ -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeBinary,name); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeBinary, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a string field. */ -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s) { - ProtoViewField *f = field_new(FieldTypeStr,name); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s) { + ProtoViewField* f = field_new(FieldTypeStr, name); f->str = strdup(s); f->len = strlen(s); - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bytes field. Note that 'count' is specified in * nibbles (bytes*2). */ -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count_nibbles) { - uint32_t numbytes = (count_nibbles+count_nibbles%2)/2; - ProtoViewField *f = field_new(FieldTypeBytes,name); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count_nibbles) { + uint32_t numbytes = (count_nibbles + count_nibbles % 2) / 2; + ProtoViewField* f = field_new(FieldTypeBytes, name); f->bytes = malloc(numbytes); - memcpy(f->bytes,bytes,numbytes); + memcpy(f->bytes, bytes, numbytes); f->len = count_nibbles; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a float field. */ -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot) { - ProtoViewField *f = field_new(FieldTypeFloat,name); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot) { + ProtoViewField* f = field_new(FieldTypeFloat, name); f->fvalue = val; f->len = digits_after_dot; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* For each field of the destination filedset 'dst', look for a matching * field name/type in the source fieldset 'src', and if one is found copy * its value into the 'dst' field. */ -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, - ProtoViewFieldSet *src) -{ - for (uint32_t j = 0; j < dst->numfields; j++) { - for (uint32_t i = 0; i < src->numfields; i++) { - if (dst->fields[j]->type == src->fields[i]->type && - !strcmp(dst->fields[j]->name,src->fields[i]->name)) - { - field_set_from_field(dst->fields[j], - src->fields[i]); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src) { + for(uint32_t j = 0; j < dst->numfields; j++) { + for(uint32_t i = 0; i < src->numfields; i++) { + if(dst->fields[j]->type == src->fields[i]->type && + !strcmp(dst->fields[j]->name, src->fields[i]->name)) { + field_set_from_field(dst->fields[j], src->fields[i]); } } } diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/plugins/protoview/protocols/b4b1.c index 7308d1211..52c59d24b 100644 --- a/applications/plugins/protoview/protocols/b4b1.c +++ b/applications/plugins/protoview/protocols/b4b1.c @@ -9,9 +9,9 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - if (numbits < 30) return false; - const char *sync_patterns[3] = { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits < 30) return false; + const char* sync_patterns[3] = { "10000000000000000000000000000001", /* 30 zero bits. */ "100000000000000000000000000000001", /* 31 zero bits. */ "1000000000000000000000000000000001", /* 32 zero bits. */ @@ -19,70 +19,67 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint32_t off; int j; - for (j = 0; j < 3; j++) { - off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[j]); - if (off != BITMAP_SEEK_NOT_FOUND) break; + for(j = 0; j < 3; j++) { + off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_patterns[j]); + if(off != BITMAP_SEEK_NOT_FOUND) break; } - if (off == BITMAP_SEEK_NOT_FOUND) return false; - if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off); + if(off == BITMAP_SEEK_NOT_FOUND) return false; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off); info->start_off = off; // Seek data setction. Why -1? Last bit is data. - off += strlen(sync_patterns[j])-1; + off += strlen(sync_patterns[j]) - 1; uint8_t d[3]; /* 24 bits of data. */ - uint32_t decoded = - convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110"); + uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110"); - if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded); - if (decoded < 24) return false; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded); + if(decoded < 24) return false; - off += 24*4; // seek to end symbol offset to calculate the length. + off += 24 * 4; // seek to end symbol offset to calculate the length. off++; // In this protocol there is a final pulse as terminator. info->pulses_count = off - info->start_off; - fieldset_add_bytes(info->fieldset,"id",d,5); - fieldset_add_uint(info->fieldset,"button",d[2]&0xf,4); + fieldset_add_bytes(info->fieldset, "id", d, 5); + fieldset_add_uint(info->fieldset, "button", d[2] & 0xf, 4); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xE0}; - fieldset_add_bytes(fieldset,"id",default_id,5); - fieldset_add_uint(fieldset,"button",1,4); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xE0}; + fieldset_add_bytes(fieldset, "id", default_id, 5); + fieldset_add_uint(fieldset, "button", 1, 4); } /* Create a signal. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) { uint32_t te = 334; // Short pulse duration in microseconds. // Sync: 1 te pulse, 31 te gap. - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*31); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 31); // ID + button state uint8_t data[3]; - memcpy(data,fs->fields[0]->bytes,3); - data[2] = (data[2]&0xF0) | (fs->fields[1]->uvalue & 0xF); - for (uint32_t j = 0; j < 24; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add(samples,true,te*3); - raw_samples_add(samples,false,te); + memcpy(data, fs->fields[0]->bytes, 3); + data[2] = (data[2] & 0xF0) | (fs->fields[1]->uvalue & 0xF); + for(uint32_t j = 0; j < 24; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add(samples, true, te * 3); + raw_samples_add(samples, false, te); } else { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*3); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 3); } } // Signal terminator. Just a single short pulse. - raw_samples_add(samples,true,te); + raw_samples_add(samples, true, te); } ProtoViewDecoder B4B1Decoder = { .name = "PT/SC remote", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/plugins/protoview/protocols/keeloq.c index 0741eac47..298c690d4 100644 --- a/applications/plugins/protoview/protocols/keeloq.c +++ b/applications/plugins/protoview/protocols/keeloq.c @@ -24,16 +24,16 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { /* In the sync pattern, we require the 12 high/low pulses and at least * half the gap we expect (5 pulses times, one is the final zero in the * 24 symbols high/low sequence, then other 4). */ - const char *sync_pattern = "101010101010101010101010" "0000"; - uint8_t sync_len = 24+4; - if (numbits-sync_len+sync_len < 3*66) return false; - uint32_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "101010101010101010101010" + "0000"; + uint8_t sync_len = 24 + 4; + if(numbits - sync_len + sync_len < 3 * 66) return false; + uint32_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; info->start_off = off; off += sync_len; // Seek start of message. @@ -42,84 +42,77 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * symbols of gap, to avoid missing the signal for a matter of wrong * timing. */ uint8_t gap_len = 0; - while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0) - gap_len++; - if (gap_len < 3 || gap_len > 7) return false; + while(gap_len <= 7 && bitmap_get(bits, numbytes, off + gap_len) == 0) gap_len++; + if(gap_len < 3 || gap_len > 7) return false; off += gap_len; FURI_LOG_E(TAG, "Keeloq preamble+sync found"); uint8_t raw[9] = {0}; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "110","100"); /* Pulse width modulation. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "110", "100"); /* Pulse width modulation. */ FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded); - if (decoded < 66) return false; /* Require the full 66 bits. */ + if(decoded < 66) return false; /* Require the full 66 bits. */ - info->pulses_count = (off+66*3) - info->start_off; + info->pulses_count = (off + 66 * 3) - info->start_off; - bitmap_reverse_bytes_bits(raw,sizeof(raw)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(raw, sizeof(raw)); /* Keeloq is LSB first. */ - int buttons = raw[7]>>4; - int lowbat = (raw[8]&0x1) == 0; // Actual bit meaning: good battery level - int alwaysone = (raw[8]&0x2) != 0; + int buttons = raw[7] >> 4; + int lowbat = (raw[8] & 0x1) == 0; // Actual bit meaning: good battery level + int alwaysone = (raw[8] & 0x2) != 0; - fieldset_add_bytes(info->fieldset,"encr",raw,8); - raw[7] = raw[7]<<4; // Make ID bits contiguous - fieldset_add_bytes(info->fieldset,"id",raw+4,7); // 28 bits, 7 nibbles - fieldset_add_bin(info->fieldset,"s[2,1,0,3]",buttons,4); - fieldset_add_bin(info->fieldset,"low battery",lowbat,1); - fieldset_add_bin(info->fieldset,"always one",alwaysone,1); + fieldset_add_bytes(info->fieldset, "encr", raw, 8); + raw[7] = raw[7] << 4; // Make ID bits contiguous + fieldset_add_bytes(info->fieldset, "id", raw + 4, 7); // 28 bits, 7 nibbles + fieldset_add_bin(info->fieldset, "s[2,1,0,3]", buttons, 4); + fieldset_add_bin(info->fieldset, "low battery", lowbat, 1); + fieldset_add_bin(info->fieldset, "always one", alwaysone, 1); return true; } -static void get_fields(ProtoViewFieldSet *fieldset) { +static void get_fields(ProtoViewFieldSet* fieldset) { uint8_t remote_id[4] = {0xab, 0xcd, 0xef, 0xa0}; uint8_t encr[4] = {0xab, 0xab, 0xab, 0xab}; - fieldset_add_bytes(fieldset,"encr",encr,8); - fieldset_add_bytes(fieldset,"id",remote_id,7); - fieldset_add_bin(fieldset,"s[2,1,0,3]",2,4); - fieldset_add_bin(fieldset,"low battery",0,1); - fieldset_add_bin(fieldset,"always one",1,1); + fieldset_add_bytes(fieldset, "encr", encr, 8); + fieldset_add_bytes(fieldset, "id", remote_id, 7); + fieldset_add_bin(fieldset, "s[2,1,0,3]", 2, 4); + fieldset_add_bin(fieldset, "low battery", 0, 1); + fieldset_add_bin(fieldset, "always one", 1, 1); } -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 380; // Short pulse duration in microseconds. // Sync: 12 pairs of pulse/gap + 9 times gap - for (int j = 0; j < 12; j++) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te); + for(int j = 0; j < 12; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); } - raw_samples_add(samples,false,te*9); + raw_samples_add(samples, false, te * 9); // Data, 66 bits. uint8_t data[9] = {0}; - memcpy(data,fieldset->fields[0]->bytes,4); // Encrypted part. - memcpy(data+4,fieldset->fields[1]->bytes,4); // ID. - data[7] = data[7]>>4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] + memcpy(data, fieldset->fields[0]->bytes, 4); // Encrypted part. + memcpy(data + 4, fieldset->fields[1]->bytes, 4); // ID. + data[7] = data[7] >> 4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] int low_battery = fieldset->fields[3] != 0; int always_one = fieldset->fields[4] != 0; low_battery = !low_battery; // Bit real meaning is good battery level. data[8] |= low_battery; data[8] |= (always_one << 1); - bitmap_reverse_bytes_bits(data,sizeof(data)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(data, sizeof(data)); /* Keeloq is LSB first. */ - for (int j = 0; j < 66; j++) { - if (bitmap_get(data,9,j)) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*2); + for(int j = 0; j < 66; j++) { + if(bitmap_get(data, 9, j)) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 2); } else { - raw_samples_add(samples,true,te*2); - raw_samples_add(samples,false,te); + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te); } } } -ProtoViewDecoder KeeloqDecoder = { - .name = "Keeloq", - .decode = decode, - .get_fields = get_fields, - .build_message = build_message -}; +ProtoViewDecoder KeeloqDecoder = + {.name = "Keeloq", .decode = decode, .get_fields = get_fields, .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/plugins/protoview/protocols/oregon2.c index 1d909a504..f67e85a2d 100644 --- a/applications/plugins/protoview/protocols/oregon2.c +++ b/applications/plugins/protoview/protocols/oregon2.c @@ -6,11 +6,14 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - if (numbits < 32) return false; - const char *sync_pattern = "01100110" "01100110" "10010110" "10010110"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits < 32) return false; + const char* sync_pattern = "01100110" + "01100110" + "10010110" + "10010110"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Oregon2 preamble+sync found"); info->start_off = off; @@ -18,50 +21,61 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t buffer[8], raw[8] = {0}; uint32_t decoded = - convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110"); + convert_from_line_code(buffer, sizeof(buffer), bits, numbytes, off, "1001", "0110"); FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded); - if (decoded < 11*4) return false; /* Minimum len to extract some data. */ - info->pulses_count = (off+11*4*4) - info->start_off; + if(decoded < 11 * 4) return false; /* Minimum len to extract some data. */ + info->pulses_count = (off + 11 * 4 * 4) - info->start_off; char temp[3] = {0}, hum[2] = {0}; uint8_t deviceid[2]; - for (int j = 0; j < 64; j += 4) { + for(int j = 0; j < 64; j += 4) { uint8_t nib[1]; - nib[0] = (bitmap_get(buffer,8,j+0) | - bitmap_get(buffer,8,j+1) << 1 | - bitmap_get(buffer,8,j+2) << 2 | - bitmap_get(buffer,8,j+3) << 3); - if (DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j/4, (unsigned int)nib[0]); - raw[j/8] |= nib[0] << (4-(j%4)); - switch(j/4) { - case 1: deviceid[0] |= nib[0]; break; - case 0: deviceid[0] |= nib[0] << 4; break; - case 3: deviceid[1] |= nib[0]; break; - case 2: deviceid[1] |= nib[0] << 4; break; - case 10: temp[0] = nib[0]; break; + nib[0] = + (bitmap_get(buffer, 8, j + 0) | bitmap_get(buffer, 8, j + 1) << 1 | + bitmap_get(buffer, 8, j + 2) << 2 | bitmap_get(buffer, 8, j + 3) << 3); + if(DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j / 4, (unsigned int)nib[0]); + raw[j / 8] |= nib[0] << (4 - (j % 4)); + switch(j / 4) { + case 1: + deviceid[0] |= nib[0]; + break; + case 0: + deviceid[0] |= nib[0] << 4; + break; + case 3: + deviceid[1] |= nib[0]; + break; + case 2: + deviceid[1] |= nib[0] << 4; + break; + case 10: + temp[0] = nib[0]; + break; /* Fixme: take the temperature sign from nibble 11. */ - case 9: temp[1] = nib[0]; break; - case 8: temp[2] = nib[0]; break; - case 13: hum[0] = nib[0]; break; - case 12: hum[1] = nib[0]; break; + case 9: + temp[1] = nib[0]; + break; + case 8: + temp[2] = nib[0]; + break; + case 13: + hum[0] = nib[0]; + break; + case 12: + hum[1] = nib[0]; + break; } } - float tempval = ((temp[0]-'0')*10) + - (temp[1]-'0') + - ((float)(temp[2]-'0')*0.1); - int humval = (hum[0]-'0')*10 + (hum[1]-'0'); + float tempval = ((temp[0] - '0') * 10) + (temp[1] - '0') + ((float)(temp[2] - '0') * 0.1); + int humval = (hum[0] - '0') * 10 + (hum[1] - '0'); - fieldset_add_bytes(info->fieldset,"Sensor ID",deviceid,4); - fieldset_add_float(info->fieldset,"Temperature",tempval,1); - fieldset_add_uint(info->fieldset,"Humidity",humval,7); + fieldset_add_bytes(info->fieldset, "Sensor ID", deviceid, 4); + fieldset_add_float(info->fieldset, "Temperature", tempval, 1); + fieldset_add_uint(info->fieldset, "Humidity", humval, 7); return true; } -ProtoViewDecoder Oregon2Decoder = { - .name = "Oregon2", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder Oregon2Decoder = + {.name = "Oregon2", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/plugins/protoview/protocols/tpms/citroen.c index d8a1681e4..ecd8fb983 100644 --- a/applications/plugins/protoview/protocols/tpms/citroen.c +++ b/applications/plugins/protoview/protocols/tpms/citroen.c @@ -7,55 +7,49 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { /* We consider a preamble of 17 symbols. They are more, but the decoding * is more likely to happen if we don't pretend to receive from the * very start of the message. */ uint32_t sync_len = 17; - const char *sync_pattern = "10101010101010110"; - if (numbits-sync_len < 8*10) return false; /* Expect 10 bytes. */ + const char* sync_pattern = "10101010101010110"; + if(numbits - sync_len < 8 * 10) return false; /* Expect 10 bytes. */ - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); info->start_off = off; off += sync_len; /* Skip preamble + sync. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded); - if (decoded < 8*10) return false; /* Require the full 10 bytes. */ + if(decoded < 8 * 10) return false; /* Require the full 10 bytes. */ /* Check the CRC. It's a simple XOR of bytes 1-9, the first byte * is not included. The meaning of the first byte is unknown and * we don't display it. */ uint8_t crc = 0; - for (int j = 1; j < 10; j++) crc ^= raw[j]; - if (crc != 0) return false; /* Require sane checksum. */ + for(int j = 1; j < 10; j++) crc ^= raw[j]; + if(crc != 0) return false; /* Require sane checksum. */ - info->pulses_count = (off+8*10*2) - info->start_off; + info->pulses_count = (off + 8 * 10 * 2) - info->start_off; int repeat = raw[5] & 0xf; - float kpa = (float)raw[6]*1.364; - int temp = raw[7]-50; + float kpa = (float)raw[6] * 1.364; + int temp = raw[7] - 50; int battery = raw[8]; /* This may be the battery. It's not clear. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+1,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_uint(info->fieldset,"Repeat",repeat,4); - fieldset_add_uint(info->fieldset,"Battery",battery,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 1, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_uint(info->fieldset, "Repeat", repeat, 4); + fieldset_add_uint(info->fieldset, "Battery", battery, 8); return true; } -ProtoViewDecoder CitroenTPMSDecoder = { - .name = "Citroen TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder CitroenTPMSDecoder = + {.name = "Citroen TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/plugins/protoview/protocols/tpms/ford.c index abdb692ee..3816e72f9 100644 --- a/applications/plugins/protoview/protocols/tpms/ford.c +++ b/applications/plugins/protoview/protocols/tpms/ford.c @@ -10,54 +10,49 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "010101010101" + "0110"; + uint8_t sync_len = 12 + 4; /* We just use 12 preamble symbols + sync. */ + if(numbits - sync_len < 8 * 8) return false; - const char *sync_pattern = "010101010101" "0110"; - uint8_t sync_len = 12+4; /* We just use 12 preamble symbols + sync. */ - if (numbits-sync_len < 8*8) return false; - - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Fort TPMS preamble+sync found"); info->start_off = off; off += sync_len; /* Skip preamble and sync. */ uint8_t raw[8]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded); - if (decoded < 8*8) return false; /* Require the full 8 bytes. */ + if(decoded < 8 * 8) return false; /* Require the full 8 bytes. */ /* CRC is just the sum of the first 7 bytes MOD 256. */ uint8_t crc = 0; - for (int j = 0; j < 7; j++) crc += raw[j]; - if (crc != raw[7]) return false; /* Require sane CRC. */ + for(int j = 0; j < 7; j++) crc += raw[j]; + if(crc != raw[7]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float psi = 0.25 * (((raw[6]&0x20)<<3)|raw[4]); + float psi = 0.25 * (((raw[6] & 0x20) << 3) | raw[4]); /* Temperature apperas to be valid only if the most significant * bit of the value is not set. Otherwise its meaning is unknown. * Likely useful to alternatively send temperature or other info. */ - int temp = raw[5] & 0x80 ? 0 : raw[5]-56; + int temp = raw[5] & 0x80 ? 0 : raw[5] - 56; int flags = raw[5] & 0x7f; int car_moving = (raw[6] & 0x44) == 0x44; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,7); - fieldset_add_uint(info->fieldset,"Moving",car_moving,1); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 7); + fieldset_add_uint(info->fieldset, "Moving", car_moving, 1); return true; } -ProtoViewDecoder FordTPMSDecoder = { - .name = "Ford TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder FordTPMSDecoder = + {.name = "Ford TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/plugins/protoview/protocols/tpms/renault.c index 09de77d17..3d8fc43d5 100644 --- a/applications/plugins/protoview/protocols/tpms/renault.c +++ b/applications/plugins/protoview/protocols/tpms/renault.c @@ -6,85 +6,82 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = +static const char* test_vector = "...01010101010101010110" // Preamble + sync /* The following is Marshal encoded, so each two characters are * actaully one bit. 01 = 0, 10 = 1. */ "010110010110" // Flags. "10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa. - // 244 kpa here. - "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. + // 244 kpa here. + "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. "1001010101101001" "0101100110010101" - "1001010101100110" // Tire ID. 0x7AD779 here. + "1001010101100110" // Tire ID. 0x7AD779 here. "0101010101010101" - "0101010101010101" // Two FF bytes (usually). Unknown. + "0101010101010101" // Two FF bytes (usually). Unknown. "0110010101010101"; // CRC8 with (poly 7, initialization 0). -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ - bitmap_set_pattern(bits,numbytes,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits-12 < 9*8) return false; + if(numbits - 12 < 9 * 8) return false; - const char *sync_pattern = "01010101010101010110"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "01010101010101010110"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); info->start_off = off; off += 20; /* Skip preamble. */ uint8_t raw[9]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded); - if (decoded < 8*9) return false; /* Require the full 9 bytes. */ - if (crc8(raw,8,0,7) != raw[8]) return false; /* Require sane CRC. */ + if(decoded < 8 * 9) return false; /* Require the full 9 bytes. */ + if(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - uint8_t flags = raw[0]>>2; - float kpa = 0.75 * ((uint32_t)((raw[0]&3)<<8) | raw[1]); - int temp = raw[2]-30; + uint8_t flags = raw[0] >> 2; + float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]); + int temp = raw[2] - 30; - fieldset_add_bytes(info->fieldset,"Tire ID",raw+3,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,6); - fieldset_add_bytes(info->fieldset,"Unknown1",raw+6,2); - fieldset_add_bytes(info->fieldset,"Unknown2",raw+7,2); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 3, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 6); + fieldset_add_bytes(info->fieldset, "Unknown1", raw + 6, 2); + fieldset_add_bytes(info->fieldset, "Unknown2", raw + 7, 2); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xEF}; - fieldset_add_bytes(fieldset,"Tire ID",default_id,3*2); - fieldset_add_float(fieldset,"Pressure kpa",123,2); - fieldset_add_int(fieldset,"Temperature C",20,8); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xEF}; + fieldset_add_bytes(fieldset, "Tire ID", default_id, 3 * 2); + fieldset_add_float(fieldset, "Pressure kpa", 123, 2); + fieldset_add_int(fieldset, "Temperature C", 20, 8); // We don't know what flags are, but 1B is a common value. - fieldset_add_hex(fieldset,"Flags",0x1b,6); - fieldset_add_bytes(fieldset,"Unknown1",(uint8_t*)"\xff",2); - fieldset_add_bytes(fieldset,"Unknown2",(uint8_t*)"\xff",2); + fieldset_add_hex(fieldset, "Flags", 0x1b, 6); + fieldset_add_bytes(fieldset, "Unknown1", (uint8_t*)"\xff", 2); + fieldset_add_bytes(fieldset, "Unknown2", (uint8_t*)"\xff", 2); } /* Create a Renault TPMS signal, according to the fields provided. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 50; // Short pulse duration in microseconds. // Preamble + sync - const char *psync = "01010101010101010101010101010110"; - const char *p = psync; + const char* psync = "01010101010101010101010101010110"; + const char* p = psync; while(*p) { - raw_samples_add_or_update(samples,*p == '1',te); + raw_samples_add_or_update(samples, *p == '1', te); p++; } @@ -93,21 +90,21 @@ static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset unsigned int raw_pressure = fieldset->fields[1]->fvalue * 4 / 3; data[0] = fieldset->fields[3]->uvalue << 2; // Flags data[0] |= (raw_pressure >> 8) & 3; // Pressure kpa high 2 bits - data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits + data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits data[2] = fieldset->fields[2]->value + 30; // Temperature C - memcpy(data+3,fieldset->fields[0]->bytes,6); // ID, 24 bits. - data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 - data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 - data[8] = crc8(data,8,0,7); + memcpy(data + 3, fieldset->fields[0]->bytes, 6); // ID, 24 bits. + data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 + data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 + data[8] = crc8(data, 8, 0, 7); // Generate Manchester code for each bit - for (uint32_t j = 0; j < 9*8; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add_or_update(samples,true,te); - raw_samples_add_or_update(samples,false,te); + for(uint32_t j = 0; j < 9 * 8; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add_or_update(samples, true, te); + raw_samples_add_or_update(samples, false, te); } else { - raw_samples_add_or_update(samples,false,te); - raw_samples_add_or_update(samples,true,te); + raw_samples_add_or_update(samples, false, te); + raw_samples_add_or_update(samples, true, te); } } } @@ -116,5 +113,4 @@ ProtoViewDecoder RenaultTPMSDecoder = { .name = "Renault TPMS", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/plugins/protoview/protocols/tpms/schrader.c index ae25e39bb..7dc85a2cb 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader.c +++ b/applications/plugins/protoview/protocols/tpms/schrader.c @@ -11,20 +11,21 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; +static const char* test_vector = + "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ - bitmap_set_pattern(bits,numbytes,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits < 64) return false; /* Preamble + data. */ + if(numbits < 64) return false; /* Preamble + data. */ - const char *sync_pattern = "1111010101" "01011010"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "1111010101" + "01011010"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Schrader TPMS gap+preamble found"); info->start_off = off; @@ -34,38 +35,33 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t raw[8]; uint8_t id[4]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded); - if (decoded < 64) return false; /* Require the full 8 bytes. */ + if(decoded < 64) return false; /* Require the full 8 bytes. */ raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation. - uint8_t cksum = crc8(raw,sizeof(raw)-1,0xf0,0x7); - if (cksum != raw[7]) { + uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7); + if(cksum != raw[7]) { FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch"); return false; } - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float kpa = (float)raw[5]*2.5; - int temp = raw[6]-50; - id[0] = raw[1]&7; + float kpa = (float)raw[5] * 2.5; + int temp = raw[6] - 50; + id[0] = raw[1] & 7; id[1] = raw[2]; id[2] = raw[3]; id[3] = raw[4]; - fieldset_add_bytes(info->fieldset,"Tire ID",id,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", id, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder SchraderTPMSDecoder = { - .name = "Schrader TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderTPMSDecoder = + {.name = "Schrader TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c index 0105010bd..45accf1a1 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c +++ b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c @@ -15,50 +15,45 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "010101010101" + "01100101"; + uint8_t sync_len = 12 + 8; /* We just use 12 preamble symbols + sync. */ + if(numbits - sync_len + 8 < 8 * 10) return false; - const char *sync_pattern = "010101010101" "01100101"; - uint8_t sync_len = 12+8; /* We just use 12 preamble symbols + sync. */ - if (numbits-sync_len+8 < 8*10) return false; - - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS preamble+sync found"); info->start_off = off; - off += sync_len-8; /* Skip preamble, not sync that is part of the data. */ + off += sync_len - 8; /* Skip preamble, not sync that is part of the data. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded); - if (decoded < 10*8) return false; /* Require the full 10 bytes. */ + if(decoded < 10 * 8) return false; /* Require the full 10 bytes. */ /* CRC is just all bytes added mod 256. */ uint8_t crc = 0; - for (int j = 0; j < 9; j++) crc += raw[j]; - if (crc != raw[9]) return false; /* Require sane CRC. */ + for(int j = 0; j < 9; j++) crc += raw[j]; + if(crc != raw[9]) return false; /* Require sane CRC. */ - info->pulses_count = (off+10*8*2) - info->start_off; + info->pulses_count = (off + 10 * 8 * 2) - info->start_off; /* To convert the raw pressure to kPa, RTL433 uses 2.5, but is likely * wrong. Searching on Google for users experimenting with the value * reported, the value appears to be 2.75. */ - float kpa = (float)raw[7]*2.75; + float kpa = (float)raw[7] * 2.75; int temp_f = raw[8]; - int temp_c = (temp_f-32)*5/9; /* Convert Fahrenheit to Celsius. */ + int temp_c = (temp_f - 32) * 5 / 9; /* Convert Fahrenheit to Celsius. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+4,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp_c,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 4, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp_c, 8); return true; } -ProtoViewDecoder SchraderEG53MA4TPMSDecoder = { - .name = "Schrader EG53MA4 TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderEG53MA4TPMSDecoder = + {.name = "Schrader EG53MA4 TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/plugins/protoview/protocols/tpms/toyota.c index b9dd1d959..b80af7647 100644 --- a/applications/plugins/protoview/protocols/tpms/toyota.c +++ b/applications/plugins/protoview/protocols/tpms/toyota.c @@ -24,40 +24,33 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (numbits-6 < 64*2) return false; /* Ask for 64 bit of data (each bit +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits - 6 < 64 * 2) + return false; /* Ask for 64 bit of data (each bit is two symbols in the bitmap). */ - char *sync[] = { - "00111100", - "001111100", - "00111101", - "001111101", - NULL - }; + char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL}; int j; uint32_t off = 0; - for (j = 0; sync[j]; j++) { - off = bitmap_seek_bits(bits,numbytes,0,numbits,sync[j]); - if (off != BITMAP_SEEK_NOT_FOUND) { + for(j = 0; sync[j]; j++) { + off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]); + if(off != BITMAP_SEEK_NOT_FOUND) { info->start_off = off; - off += strlen(sync[j])-2; + off += strlen(sync[j]) - 2; break; - } + } } - if (off == BITMAP_SEEK_NOT_FOUND) return false; + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Toyota TPMS sync[%s] found", sync[j]); uint8_t raw[9]; - uint32_t decoded = - convert_from_diff_manchester(raw,sizeof(raw),bits,numbytes,off,true); + uint32_t decoded = convert_from_diff_manchester(raw, sizeof(raw), bits, numbytes, off, true); FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded); - if (decoded < 8*9) return false; /* Require the full 8 bytes. */ - if (crc8(raw,8,0x80,7) != raw[8]) return false; /* Require sane CRC. */ + if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */ + if(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */ /* We detected a valid signal. However now info->start_off is actually * pointing to the sync part, not the preamble of alternating 0 and 1. @@ -65,25 +58,21 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * for the decoder itself to fix the signal if neeeded, so that its * logical representation will be more accurate and better to save * and retransmit. */ - if (info->start_off >= 12) { + if(info->start_off >= 12) { info->start_off -= 12; - bitmap_set_pattern(bits,numbytes,info->start_off,"010101010101"); + bitmap_set_pattern(bits, numbytes, info->start_off, "010101010101"); } - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - float psi = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7; - int temp = ((raw[5]&0x7f)<<1 | raw[6]>>7) - 40; + float psi = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7; + int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder ToyotaTPMSDecoder = { - .name = "Toyota TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder ToyotaTPMSDecoder = + {.name = "Toyota TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/raw_samples.c b/applications/plugins/protoview/raw_samples.c index f83cca361..54773f43f 100644 --- a/applications/plugins/protoview/raw_samples.c +++ b/applications/plugins/protoview/raw_samples.c @@ -8,15 +8,15 @@ #include "raw_samples.h" /* Allocate and initialize a samples buffer. */ -RawSamplesBuffer *raw_samples_alloc(void) { - RawSamplesBuffer *buf = malloc(sizeof(*buf)); +RawSamplesBuffer* raw_samples_alloc(void) { + RawSamplesBuffer* buf = malloc(sizeof(*buf)); buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal); raw_samples_reset(buf); return buf; } /* Free a sample buffer. Should be called when the mutex is released. */ -void raw_samples_free(RawSamplesBuffer *s) { +void raw_samples_free(RawSamplesBuffer* s) { furi_mutex_free(s->mutex); free(s); } @@ -24,27 +24,27 @@ void raw_samples_free(RawSamplesBuffer *s) { /* This just set all the samples to zero and also resets the internal * index. There is no need to call it after raw_samples_alloc(), but only * when one wants to reset the whole buffer of samples. */ -void raw_samples_reset(RawSamplesBuffer *s) { - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_reset(RawSamplesBuffer* s) { + furi_mutex_acquire(s->mutex, FuriWaitForever); s->total = RAW_SAMPLES_NUM; s->idx = 0; s->short_pulse_dur = 0; - memset(s->samples,0,sizeof(s->samples)); + memset(s->samples, 0, sizeof(s->samples)); furi_mutex_release(s->mutex); } /* Set the raw sample internal index so that what is currently at * offset 'offset', will appear to be at 0 index. */ -void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) { - s->idx = (s->idx+offset) % RAW_SAMPLES_NUM; +void raw_samples_center(RawSamplesBuffer* s, uint32_t offset) { + s->idx = (s->idx + offset) % RAW_SAMPLES_NUM; } /* Add the specified sample in the circular buffer. */ -void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); s->samples[s->idx].level = level; s->samples[s->idx].dur = dur; - s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; furi_mutex_release(s->mutex); } @@ -56,28 +56,25 @@ void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { * * This function is a bit slower so the internal data sampling should * be performed with raw_samples_add(). */ -void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex,FuriWaitForever); - uint32_t previdx = (s->idx-1) % RAW_SAMPLES_NUM; - if (s->samples[previdx].level == level && - s->samples[previdx].dur != 0) - { +void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); + uint32_t previdx = (s->idx - 1) % RAW_SAMPLES_NUM; + if(s->samples[previdx].level == level && s->samples[previdx].dur != 0) { /* Update the last sample: it has the same level. */ s->samples[previdx].dur += dur; } else { /* Add a new sample. */ s->samples[s->idx].level = level; s->samples[s->idx].dur = dur; - s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; } furi_mutex_release(s->mutex); } /* Get the sample from the buffer. It is possible to use out of range indexes * as 'idx' because the modulo operation will rewind back from the start. */ -void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur) -{ - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); idx = (s->idx + idx) % RAW_SAMPLES_NUM; *level = s->samples[idx].level; *dur = s->samples[idx].dur; @@ -85,12 +82,12 @@ void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *d } /* Copy one buffer to the other, including current index. */ -void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) { - furi_mutex_acquire(src->mutex,FuriWaitForever); - furi_mutex_acquire(dst->mutex,FuriWaitForever); +void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src) { + furi_mutex_acquire(src->mutex, FuriWaitForever); + furi_mutex_acquire(dst->mutex, FuriWaitForever); dst->idx = src->idx; dst->short_pulse_dur = src->short_pulse_dur; - memcpy(dst->samples,src->samples,sizeof(dst->samples)); + memcpy(dst->samples, src->samples, sizeof(dst->samples)); furi_mutex_release(src->mutex); furi_mutex_release(dst->mutex); } diff --git a/applications/plugins/protoview/raw_samples.h b/applications/plugins/protoview/raw_samples.h index 0b0422025..3493f07fd 100644 --- a/applications/plugins/protoview/raw_samples.h +++ b/applications/plugins/protoview/raw_samples.h @@ -4,16 +4,17 @@ /* Our circular buffer of raw samples, used in order to display * the signal. */ -#define RAW_SAMPLES_NUM 2048 /* Use a power of two: we take the modulo +#define RAW_SAMPLES_NUM \ + 2048 /* Use a power of two: we take the modulo of the index quite often to normalize inside the range, and division is slow. */ typedef struct RawSamplesBuffer { - FuriMutex *mutex; + FuriMutex* mutex; struct { - uint16_t level:1; - uint16_t dur:15; + uint16_t level : 1; + uint16_t dur : 15; } samples[RAW_SAMPLES_NUM]; - uint32_t idx; /* Current idx (next to write). */ + uint32_t idx; /* Current idx (next to write). */ uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide this field for a cleaner interface with the user, but we always use RAW_SAMPLES_NUM when taking the modulo so @@ -22,11 +23,11 @@ typedef struct RawSamplesBuffer { uint32_t short_pulse_dur; /* Duration of the shortest pulse. */ } RawSamplesBuffer; -RawSamplesBuffer *raw_samples_alloc(void); -void raw_samples_reset(RawSamplesBuffer *s); -void raw_samples_center(RawSamplesBuffer *s, uint32_t offset); -void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur); -void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur); -void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur); -void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src); -void raw_samples_free(RawSamplesBuffer *s); +RawSamplesBuffer* raw_samples_alloc(void); +void raw_samples_reset(RawSamplesBuffer* s); +void raw_samples_center(RawSamplesBuffer* s, uint32_t offset); +void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur); +void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur); +void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur); +void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src); +void raw_samples_free(RawSamplesBuffer* s); diff --git a/applications/plugins/protoview/signal.c b/applications/plugins/protoview/signal.c index f4c5ebedf..a1c4b2b8f 100644 --- a/applications/plugins/protoview/signal.c +++ b/applications/plugins/protoview/signal.c @@ -3,7 +3,7 @@ #include "app.h" -bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info); +bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info); /* ============================================================================= * Raw signal detection @@ -16,7 +16,7 @@ uint32_t duration_delta(uint32_t a, uint32_t b) { } /* Reset the current signal, so that a new one can be detected. */ -void reset_current_signal(ProtoViewApp *app) { +void reset_current_signal(ProtoViewApp* app) { app->signal_bestlen = 0; app->signal_offset = 0; app->signal_decoded = false; @@ -39,47 +39,47 @@ void reset_current_signal(ProtoViewApp *app) { * For instance Oregon2 sensors, in the case of protocol 2.1 will send * pulses of ~400us (RF on) VS ~580us (RF off). */ #define SEARCH_CLASSES 3 -uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { +uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { struct { - uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ - uint32_t count[2]; /* Associated observed frequency. */ + uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ + uint32_t count[2]; /* Associated observed frequency. */ } classes[SEARCH_CLASSES]; - memset(classes,0,sizeof(classes)); + memset(classes, 0, sizeof(classes)); uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we allow for high and low. */ uint32_t len = 0; /* Observed len of coherent samples. */ s->short_pulse_dur = 0; - for (uint32_t j = idx; j < idx+500; j++) { + for(uint32_t j = idx; j < idx + 500; j++) { bool level; uint32_t dur; raw_samples_get(s, j, &level, &dur); - if (dur < minlen || dur > maxlen) break; /* return. */ + if(dur < minlen || dur > maxlen) break; /* return. */ /* Let's see if it matches a class we already have or if we * can populate a new (yet empty) class. */ uint32_t k; - for (k = 0; k < SEARCH_CLASSES; k++) { - if (classes[k].count[level] == 0) { + for(k = 0; k < SEARCH_CLASSES; k++) { + if(classes[k].count[level] == 0) { classes[k].dur[level] = dur; classes[k].count[level] = 1; break; /* Sample accepted. */ } else { uint32_t classavg = classes[k].dur[level]; uint32_t count = classes[k].count[level]; - uint32_t delta = duration_delta(dur,classavg); + uint32_t delta = duration_delta(dur, classavg); /* Is the difference in duration between this signal and * the class we are inspecting less than a given percentage? * If so, accept this signal. */ - if (delta < classavg/5) { /* 100%/5 = 20%. */ + if(delta < classavg / 5) { /* 100%/5 = 20%. */ /* It is useful to compute the average of the class * we are observing. We know how many samples we got so * far, so we can recompute the average easily. * By always having a better estimate of the pulse len * we can avoid missing next samples in case the first * observed samples are too off. */ - classavg = ((classavg * count) + dur) / (count+1); + classavg = ((classavg * count) + dur) / (count + 1); classes[k].dur[level] = classavg; classes[k].count[level]++; break; /* Sample accepted. */ @@ -87,7 +87,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { } } - if (k == SEARCH_CLASSES) break; /* No match, return. */ + if(k == SEARCH_CLASSES) break; /* No match, return. */ /* If we are here, we accepted this sample. Try with the next * one. */ @@ -97,14 +97,12 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { /* Update the buffer setting the shortest pulse we found * among the three classes. This will be used when scaling * for visualization. */ - uint32_t short_dur[2] = {0,0}; - for (int j = 0; j < SEARCH_CLASSES; j++) { - for (int level = 0; level < 2; level++) { - if (classes[j].dur[level] == 0) continue; - if (classes[j].count[level] < 3) continue; - if (short_dur[level] == 0 || - short_dur[level] > classes[j].dur[level]) - { + uint32_t short_dur[2] = {0, 0}; + for(int j = 0; j < SEARCH_CLASSES; j++) { + for(int level = 0; level < 2; level++) { + if(classes[j].dur[level] == 0) continue; + if(classes[j].count[level] < 3) continue; + if(short_dur[level] == 0 || short_dur[level] > classes[j].dur[level]) { short_dur[level] = classes[j].dur[level]; } } @@ -113,33 +111,28 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { /* Use the average between high and low short pulses duration. * Often they are a bit different, and using the average is more robust * when we do decoding sampling at short_pulse_dur intervals. */ - if (short_dur[0] == 0) short_dur[0] = short_dur[1]; - if (short_dur[1] == 0) short_dur[1] = short_dur[0]; - s->short_pulse_dur = (short_dur[0]+short_dur[1])/2; + if(short_dur[0] == 0) short_dur[0] = short_dur[1]; + if(short_dur[1] == 0) short_dur[1] = short_dur[0]; + s->short_pulse_dur = (short_dur[0] + short_dur[1]) / 2; return len; } /* Called when we detect a message. Just blinks when the message was * not decoded. Vibrates, too, when the message was correctly decoded. */ -void notify_signal_detected(ProtoViewApp *app, bool decoded) { +void notify_signal_detected(ProtoViewApp* app, bool decoded) { static const NotificationSequence decoded_seq = { &message_vibro_on, &message_green_255, &message_delay_50, &message_green_0, &message_vibro_off, - NULL - }; + NULL}; static const NotificationSequence unknown_seq = { - &message_red_255, - &message_delay_50, - &message_red_0, - NULL - }; + &message_red_255, &message_delay_50, &message_red_0, NULL}; - if (decoded) + if(decoded) notification_message(app->notification, &decoded_seq); else notification_message(app->notification, &unknown_seq); @@ -149,57 +142,59 @@ void notify_signal_detected(ProtoViewApp *app, bool decoded) { * in order to find a coherent signal. If a signal that does not appear to * be just noise is found, it is set in DetectedSamples global signal * buffer, that is what is rendered on the screen. */ -void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) { +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { /* We need to work on a copy: the source buffer may be populated * by the background thread receiving data. */ - RawSamplesBuffer *copy = raw_samples_alloc(); - raw_samples_copy(copy,source); + RawSamplesBuffer* copy = raw_samples_alloc(); + raw_samples_copy(copy, source); /* Try to seek on data that looks to have a regular high low high low * pattern. */ - uint32_t minlen = 18; /* Min run of coherent samples. With less + uint32_t minlen = 18; /* Min run of coherent samples. With less than a few samples it's very easy to mistake noise for signal. */ uint32_t i = 0; - while (i < copy->total-1) { - uint32_t thislen = search_coherent_signal(copy,i); + while(i < copy->total - 1) { + uint32_t thislen = search_coherent_signal(copy, i); /* For messages that are long enough, attempt decoding. */ - if (thislen > minlen) { + if(thislen > minlen) { /* Allocate the message information that some decoder may * fill, in case it is able to decode a message. */ - ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo)); - init_msg_info(info,app); + ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo)); + init_msg_info(info, app); info->short_pulse_dur = copy->short_pulse_dur; uint32_t saved_idx = copy->idx; /* Save index, see later. */ /* decode_signal() expects the detected signal to start * from index zero .*/ - raw_samples_center(copy,i); - bool decoded = decode_signal(copy,thislen,info); + raw_samples_center(copy, i); + bool decoded = decode_signal(copy, thislen, info); copy->idx = saved_idx; /* Restore the index as we are scanning the signal in the loop. */ /* Accept this signal as the new signal if either it's longer * than the previous undecoded one, or the previous one was * unknown and this is decoded. */ - if ((thislen > app->signal_bestlen && app->signal_decoded == false) - || (app->signal_decoded == false && decoded)) - { + if((thislen > app->signal_bestlen && app->signal_decoded == false) || + (app->signal_decoded == false && decoded)) { free_msg_info(app->msg_info); app->msg_info = info; app->signal_bestlen = thislen; app->signal_decoded = decoded; - raw_samples_copy(DetectedSamples,copy); - raw_samples_center(DetectedSamples,i); - FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)", - (int)thislen, DetectedSamples->short_pulse_dur); + raw_samples_copy(DetectedSamples, copy); + raw_samples_center(DetectedSamples, i); + FURI_LOG_E( + TAG, + "===> Displayed sample updated (%d samples %lu us)", + (int)thislen, + DetectedSamples->short_pulse_dur); - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - notify_signal_detected(app,decoded); + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + notify_signal_detected(app, decoded); } else { /* If the structure was not filled, discard it. Otherwise * now the owner is app->msg_info. */ @@ -227,38 +222,42 @@ void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) { /* Set the 'bitpos' bit to value 'val', in the specified bitmap * 'b' of len 'blen'. * Out of range bits will silently be discarded. */ -void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) { - uint32_t byte = bitpos/8; - uint32_t bit = 7-(bitpos&7); - if (byte >= blen) return; - if (val) - b[byte] |= 1<= blen) return; + if(val) + b[byte] |= 1 << bit; else - b[byte] &= ~(1<= blen) return 0; - return (b[byte] & (1<= blen) return 0; + return (b[byte] & (1 << bit)) != 0; } /* Copy 'count' bits from the bitmap 's' of 'slen' total bytes, to the * bitmap 'd' of 'dlen' total bytes. The bits are copied starting from * offset 'soff' of the source bitmap to the offset 'doff' of the * destination bitmap. */ -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, - uint8_t *s, uint32_t slen, uint32_t soff, - uint32_t count) -{ +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count) { /* If we are byte-aligned in both source and destination, use a fast * path for the number of bytes we can consume this way. */ - if ((doff & 7) == 0 && (soff & 7) == 0) { - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + if((doff & 7) == 0 && (soff & 7) == 0) { + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { d[didx++] = s[sidx++]; count -= 8; @@ -271,9 +270,9 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Copy the bits needed to reach an offset where we can copy * two half bytes of src to a full byte of destination. */ - while(count > 8 && (doff&7) != 0) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + while(count > 8 && (doff & 7) != 0) { + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } @@ -316,13 +315,12 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, * src[2] << 5, that is "WORLDS!!" >> 5 = ".....WOR" * That is "HELLOWOR" */ - if (count > 8) { + if(count > 8) { uint8_t skew = soff % 8; /* Don't worry, compiler will optimize. */ - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { - d[didx] = ((s[sidx] << skew) | - (s[sidx+1] >> (8-skew))); + d[didx] = ((s[sidx] << skew) | (s[sidx + 1] >> (8 - skew))); sidx++; didx++; soff += 8; @@ -334,8 +332,8 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Here count is guaranteed to be < 8. * Copy the final bits bit by bit. */ while(count) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } } @@ -343,15 +341,15 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* We decode bits assuming the first bit we receive is the MSB * (see bitmap_set/get functions). Certain devices send data * encoded in the reverse way. */ -void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { - for (uint32_t j = 0; j < len; j++) { +void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len) { + for(uint32_t j = 0; j < len; j++) { uint32_t b = p[j]; /* Step 1: swap the two nibbles: 12345678 -> 56781234 */ - b = (b&0xf0)>>4 | (b&0x0f)<<4; + b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; /* Step 2: swap adjacent pairs : 56781234 -> 78563412 */ - b = (b&0xcc)>>2 | (b&0x33)<<2; + b = (b & 0xcc) >> 2 | (b & 0x33) << 2; /* Step 3: swap adjacent bits : 78563412 -> 87654321 */ - b = (b&0xaa)>>1 | (b&0x55)<<1; + b = (b & 0xaa) >> 1 | (b & 0x55) << 1; p[j] = b; } } @@ -359,10 +357,10 @@ void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { /* Return true if the specified sequence of bits, provided as a string in the * form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos' * position. */ -bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) { - for (size_t j = 0; bits[j]; j++) { +bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits) { + for(size_t j = 0; bits[j]; j++) { bool expected = (bits[j] == '1') ? true : false; - if (bitmap_get(b,blen,bitpos+j) != expected) return false; + if(bitmap_get(b, blen, bitpos + j) != expected) return false; } return true; } @@ -375,12 +373,17 @@ bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *b * Note: there are better algorithms, such as Boyer-Moore. Here we hope that * for the kind of patterns we search we'll have a lot of early stops so * we use a vanilla approach. */ -uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits) { - uint32_t endpos = startpos+blen*8; - uint32_t end2 = startpos+maxbits; - if (end2 < endpos) endpos = end2; - for (uint32_t j = startpos; j < endpos; j++) - if (bitmap_match_bits(b,blen,j,bits)) return j; +uint32_t bitmap_seek_bits( + uint8_t* b, + uint32_t blen, + uint32_t startpos, + uint32_t maxbits, + const char* bits) { + uint32_t endpos = startpos + blen * 8; + uint32_t end2 = startpos + maxbits; + if(end2 < endpos) endpos = end2; + for(uint32_t j = startpos; j < endpos; j++) + if(bitmap_match_bits(b, blen, j, bits)) return j; return BITMAP_SEEK_NOT_FOUND; } @@ -391,10 +394,10 @@ uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t * This function is useful in order to set the test vectors in the protocol * decoders, to see if the decoding works regardless of the fact we are able * to actually receive a given signal. */ -void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat) { +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat) { uint32_t i = 0; while(pat[i]) { - bitmap_set(b,blen,i+off,pat[i] == '1'); + bitmap_set(b, blen, i + off, pat[i] == '1'); i++; } } @@ -426,31 +429,36 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat * bits set into the buffer 'b'. The 'rate' argument, in microseconds, is * the detected short-pulse duration. We expect the line code to be * meaningful when interpreted at multiples of 'rate'. */ -uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) { - if (rate == 0) return 0; /* We can't perform the conversion. */ +uint32_t convert_signal_to_bits( + uint8_t* b, + uint32_t blen, + RawSamplesBuffer* s, + uint32_t idx, + uint32_t count, + uint32_t rate) { + if(rate == 0) return 0; /* We can't perform the conversion. */ uint32_t bitpos = 0; - for (uint32_t j = 0; j < count; j++) { + for(uint32_t j = 0; j < count; j++) { uint32_t dur; bool level; - raw_samples_get(s, j+idx, &level, &dur); + raw_samples_get(s, j + idx, &level, &dur); uint32_t numbits = dur / rate; /* full bits that surely fit. */ - uint32_t rest = dur % rate; /* How much we are left with. */ - if (rest > rate/2) numbits++; /* There is another one. */ + uint32_t rest = dur % rate; /* How much we are left with. */ + if(rest > rate / 2) numbits++; /* There is another one. */ /* Limit how much a single sample can spawn. There are likely no * protocols doing such long pulses when the rate is low. */ - if (numbits > 1024) numbits = 1024; + if(numbits > 1024) numbits = 1024; - if (0) /* Super verbose, so not under the DEBUG_MSG define. */ - FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", - dur,numbits,(int)level); + if(0) /* Super verbose, so not under the DEBUG_MSG define. */ + FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur, numbits, (int)level); /* If the signal is too short, let's claim it an interference * and ignore it completely. */ - if (numbits == 0) continue; + if(numbits == 0) continue; - while(numbits--) bitmap_set(b,blen,bitpos++,level); + while(numbits--) bitmap_set(b, blen, bitpos++, level); } return bitpos; } @@ -467,23 +475,29 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, * specified in bytes by the caller, via the 'len' parameters). * * The decoding starts at the specified offset (in bits) 'off'. */ -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern) -{ +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + const char* zero_pattern, + const char* one_pattern) { uint32_t decoded = 0; /* Number of bits extracted. */ len *= 8; /* Convert bytes to bits. */ while(off < len) { bool bitval; - if (bitmap_match_bits(bits,len,off,zero_pattern)) { + if(bitmap_match_bits(bits, len, off, zero_pattern)) { bitval = false; off += strlen(zero_pattern); - } else if (bitmap_match_bits(bits,len,off,one_pattern)) { + } else if(bitmap_match_bits(bits, len, off, one_pattern)) { bitval = true; off += strlen(one_pattern); } else { break; } - bitmap_set(buf,buflen,decoded++,bitval); - if (decoded/8 == buflen) break; /* No space left on target buffer. */ + bitmap_set(buf, buflen, decoded++, bitval); + if(decoded / 8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -494,17 +508,22 @@ uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, ui * in differential codings the next bits depend on the previous one. * * Parameters and return values are like convert_from_line_code(). */ -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous) -{ +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous) { uint32_t decoded = 0; len *= 8; /* Conver to bits. */ - for (uint32_t j = off; j < len; j += 2) { - bool b0 = bitmap_get(bits,len,j); - bool b1 = bitmap_get(bits,len,j+1); - if (b0 == previous) break; /* Each new bit must switch value. */ - bitmap_set(buf,buflen,decoded++,b0 == b1); + for(uint32_t j = off; j < len; j += 2) { + bool b0 = bitmap_get(bits, len, j); + bool b1 = bitmap_get(bits, len, j + 1); + if(b0 == previous) break; /* Each new bit must switch value. */ + bitmap_set(buf, buflen, decoded++, b0 == b1); previous = b1; - if (decoded/8 == buflen) break; /* No space left on target buffer. */ + if(decoded / 8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -522,22 +541,21 @@ extern ProtoViewDecoder CitroenTPMSDecoder; extern ProtoViewDecoder FordTPMSDecoder; extern ProtoViewDecoder KeeloqDecoder; -ProtoViewDecoder *Decoders[] = { - &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ - &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ - &RenaultTPMSDecoder, /* Renault TPMS. */ - &ToyotaTPMSDecoder, /* Toyota TPMS. */ - &SchraderTPMSDecoder, /* Schrader TPMS. */ - &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ - &CitroenTPMSDecoder, /* Citroen TPMS. */ - &FordTPMSDecoder, /* Ford TPMS. */ - &KeeloqDecoder, /* Keeloq remote. */ - NULL -}; +ProtoViewDecoder* Decoders[] = { + &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ + &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ + &RenaultTPMSDecoder, /* Renault TPMS. */ + &ToyotaTPMSDecoder, /* Toyota TPMS. */ + &SchraderTPMSDecoder, /* Schrader TPMS. */ + &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ + &CitroenTPMSDecoder, /* Citroen TPMS. */ + &FordTPMSDecoder, /* Ford TPMS. */ + &KeeloqDecoder, /* Keeloq remote. */ + NULL}; /* Free the message info and allocated data. */ -void free_msg_info(ProtoViewMsgInfo *i) { - if (i == NULL) return; +void free_msg_info(ProtoViewMsgInfo* i) { + if(i == NULL) return; fieldset_free(i->fieldset); free(i->bits); free(i); @@ -545,9 +563,9 @@ void free_msg_info(ProtoViewMsgInfo *i) { /* Reset the message info structure before passing it to the decoding * functions. */ -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app) { UNUSED(app); - memset(i,0,sizeof(ProtoViewMsgInfo)); + memset(i, 0, sizeof(ProtoViewMsgInfo)); i->bits = NULL; i->fieldset = fieldset_new(); } @@ -556,23 +574,29 @@ void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { * to a bitstream, and the calls the protocol specific functions for * decoding. If the signal was decoded correctly by some protocol, true * is returned. Otherwise false is returned. */ -bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { - uint32_t bitmap_bits_size = 4096*8; - uint32_t bitmap_size = bitmap_bits_size/8; +bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) { + uint32_t bitmap_bits_size = 4096 * 8; + uint32_t bitmap_size = bitmap_bits_size / 8; /* We call the decoders with an offset a few samples before the actual * signal detected and for a len of a few bits after its end. */ uint32_t before_samples = 32; uint32_t after_samples = 100; - uint8_t *bitmap = malloc(bitmap_size); - uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_samples,len+before_samples+after_samples,s->short_pulse_dur); + uint8_t* bitmap = malloc(bitmap_size); + uint32_t bits = convert_signal_to_bits( + bitmap, + bitmap_size, + s, + -before_samples, + len + before_samples + after_samples, + s->short_pulse_dur); - if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ - char *str = malloc(1024); + if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ + char* str = malloc(1024); uint32_t j; - for (j = 0; j < bits && j < 1023; j++) { - str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0'; + for(j = 0; j < bits && j < 1023; j++) { + str[j] = bitmap_get(bitmap, bitmap_size, j) ? '1' : '0'; } str[j] = 0; FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str); @@ -585,18 +609,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { bool decoded = false; while(Decoders[j]) { uint32_t start_time = furi_get_tick(); - decoded = Decoders[j]->decode(bitmap,bitmap_size,bits,info); + decoded = Decoders[j]->decode(bitmap, bitmap_size, bits, info); uint32_t delta = furi_get_tick() - start_time; - FURI_LOG_E(TAG, "Decoder %s took %lu ms", - Decoders[j]->name, (unsigned long)delta); - if (decoded) { + FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta); + if(decoded) { info->decoder = Decoders[j]; break; } j++; } - if (!decoded) { + if(!decoded) { FURI_LOG_E(TAG, "No decoding possible"); } else { FURI_LOG_E(TAG, "+++ Decoded %s", info->decoder->name); @@ -604,12 +627,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { * with the decoded signal. The decoder may not implement offset/len * filling of the structure. In such case we have no info and * pulses_count will be set to zero. */ - if (info->pulses_count) { - info->bits_bytes = (info->pulses_count+7)/8; // Round to full byte. + if(info->pulses_count) { + info->bits_bytes = (info->pulses_count + 7) / 8; // Round to full byte. info->bits = malloc(info->bits_bytes); - bitmap_copy(info->bits,info->bits_bytes,0, - bitmap,bitmap_size,info->start_off, - info->pulses_count); + bitmap_copy( + info->bits, + info->bits_bytes, + 0, + bitmap, + bitmap_size, + info->start_off, + info->pulses_count); } } free(bitmap); diff --git a/applications/plugins/protoview/signal_file.c b/applications/plugins/protoview/signal_file.c index 31c8726fb..c60a6a181 100644 --- a/applications/plugins/protoview/signal_file.c +++ b/applications/plugins/protoview/signal_file.c @@ -13,57 +13,56 @@ * but it's logical representation stored in the app->msg_info bitmap, where * each 1 or 0 means a puls or gap for the specified short pulse duration time * (te). */ -bool save_signal(ProtoViewApp *app, const char *filename) { +bool save_signal(ProtoViewApp* app, const char* filename) { /* We have a message at all? */ - if (app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; - - Storage *storage = furi_record_open(RECORD_STORAGE); - FlipperFormat *file = flipper_format_file_alloc(storage); - Stream *stream = flipper_format_get_raw_stream(file); - FuriString *file_content = NULL; + if(app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + Stream* stream = flipper_format_get_raw_stream(file); + FuriString* file_content = NULL; bool success = true; - if (flipper_format_file_open_always(file, filename)) { + if(flipper_format_file_open_always(file, filename)) { /* Write the file header. */ - FuriString *file_content = furi_string_alloc(); - const char *preset_id = ProtoViewModulations[app->modulation].id; + FuriString* file_content = furi_string_alloc(); + const char* preset_id = ProtoViewModulations[app->modulation].id; - furi_string_printf(file_content, - "Filetype: Flipper SubGhz RAW File\n" - "Version: 1\n" - "Frequency: %ld\n" - "Preset: %s\n", - app->frequency, - preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); + furi_string_printf( + file_content, + "Filetype: Flipper SubGhz RAW File\n" + "Version: 1\n" + "Frequency: %ld\n" + "Preset: %s\n", + app->frequency, + preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); /* For custom modulations, we need to emit a set of registers. */ - if (preset_id == NULL) { - FuriString *custom = furi_string_alloc(); - uint8_t *regs = ProtoViewModulations[app->modulation].custom; - furi_string_printf(custom, + if(preset_id == NULL) { + FuriString* custom = furi_string_alloc(); + uint8_t* regs = ProtoViewModulations[app->modulation].custom; + furi_string_printf( + custom, "Custom_preset_module: CC1101\n" - "Custom_preset_data: "); - for (int j = 0; regs[j]; j += 2) { - furi_string_cat_printf(custom, "%02X %02X ", - (int)regs[j], (int)regs[j+1]); + "Custom_preset_data: "); + for(int j = 0; regs[j]; j += 2) { + furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } size_t len = furi_string_size(file_content); - furi_string_set_char(custom,len-1,'\n'); - furi_string_cat(file_content,custom); + furi_string_set_char(custom, len - 1, '\n'); + furi_string_cat(file_content, custom); furi_string_free(custom); } /* We always save raw files. */ - furi_string_cat_printf(file_content, - "Protocol: RAW\n" - "RAW_Data: -10000\n"); // Start with 10 ms of gap + furi_string_cat_printf( + file_content, + "Protocol: RAW\n" + "RAW_Data: -10000\n"); // Start with 10 ms of gap /* Write header. */ size_t len = furi_string_size(file_content); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), len) - != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -76,15 +75,13 @@ bool save_signal(ProtoViewApp *app, const char *filename) { uint32_t this_line_samples = 0; uint32_t max_line_samples = 100; uint32_t idx = 0; // Iindex in the signal bitmap. - ProtoViewMsgInfo *i = app->msg_info; + ProtoViewMsgInfo* i = app->msg_info; while(idx < i->pulses_count) { - bool level = bitmap_get(i->bits,i->bits_bytes,idx); + bool level = bitmap_get(i->bits, i->bits_bytes, idx); uint32_t te_times = 1; idx++; /* Count the duration of the current pulse/gap. */ - while(idx < i->pulses_count && - bitmap_get(i->bits,i->bits_bytes,idx) == level) - { + while(idx < i->pulses_count && bitmap_get(i->bits, i->bits_bytes, idx) == level) { te_times++; idx++; } @@ -92,32 +89,29 @@ bool save_signal(ProtoViewApp *app, const char *filename) { // next gap or pulse. int32_t dur = (int32_t)i->short_pulse_dur * te_times; - if (level == 0) dur = -dur; /* Negative is gap in raw files. */ + if(level == 0) dur = -dur; /* Negative is gap in raw files. */ /* Emit the sample. If this is the first sample of the line, * also emit the RAW_Data: field. */ - if (this_line_samples == 0) - furi_string_cat_printf(file_content,"RAW_Data: "); - furi_string_cat_printf(file_content,"%d ",(int)dur); + if(this_line_samples == 0) furi_string_cat_printf(file_content, "RAW_Data: "); + furi_string_cat_printf(file_content, "%d ", (int)dur); this_line_samples++; /* Store the current set of samples on disk, when we reach a * given number or the end of the signal. */ bool end_reached = (idx == i->pulses_count); - if (this_line_samples == max_line_samples || end_reached) { + if(this_line_samples == max_line_samples || end_reached) { /* If that's the end, terminate the signal with a long * gap. */ - if (end_reached) furi_string_cat_printf(file_content,"-10000 "); + if(end_reached) furi_string_cat_printf(file_content, "-10000 "); /* We always have a trailing space in the last sample. Make it * a newline. */ size_t len = furi_string_size(file_content); - furi_string_set_char(file_content,len-1,'\n'); + furi_string_set_char(file_content, len - 1, '\n'); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), - len) != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != + len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -136,6 +130,6 @@ bool save_signal(ProtoViewApp *app, const char *filename) { write_err: furi_record_close(RECORD_STORAGE); flipper_format_free(file); - if (file_content != NULL) furi_string_free(file_content); + if(file_content != NULL) furi_string_free(file_content); return success; } diff --git a/applications/plugins/protoview/ui.c b/applications/plugins/protoview/ui.c index 8badab5bf..b0251f09f 100644 --- a/applications/plugins/protoview/ui.c +++ b/applications/plugins/protoview/ui.c @@ -10,36 +10,31 @@ /* Return the ID of the currently selected subview, of the current * view. */ -int ui_get_current_subview(ProtoViewApp *app) { +int ui_get_current_subview(ProtoViewApp* app) { return app->current_subview[app->current_view]; } /* Called by view rendering callback that has subviews, to show small triangles * facing down/up if there are other subviews the user can access with up * and down. */ -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, - int last_subview) -{ +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) { int subview = ui_get_current_subview(app); - if (subview != 0) - canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop); - if (subview != last_subview-1) - canvas_draw_triangle(canvas,120,59,8,5,CanvasDirectionTopToBottom); + if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop); + if(subview != last_subview - 1) + canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom); } /* Handle up/down keys when we are in a subview. If the function catched * such keypress, it returns true, so that the actual view input callback * knows it can just return ASAP without doing anything. */ -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) { +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) { int subview = ui_get_current_subview(app); - if (input.type == InputTypePress) { - if (input.key == InputKeyUp) { - if (subview != 0) - app->current_subview[app->current_view]--; + if(input.type == InputTypePress) { + if(input.key == InputKeyUp) { + if(subview != 0) app->current_subview[app->current_view]--; return true; - } else if (input.key == InputKeyDown) { - if (subview != last_subview-1) - app->current_subview[app->current_view]++; + } else if(input.key == InputKeyDown) { + if(subview != last_subview - 1) app->current_subview[app->current_view]++; return true; } } @@ -62,16 +57,18 @@ bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_sub * * Note: if the buffer is not a null-termined zero string, what it contains will * be used as initial input for the user. */ -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)) -{ +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)) { app->show_text_input = true; app->text_input_buffer = buffer; app->text_input_buffer_len = buflen; app->text_input_done_callback = done_callback; } -void ui_dismiss_keyboard(ProtoViewApp *app) { +void ui_dismiss_keyboard(ProtoViewApp* app) { view_dispatcher_stop(app->view_dispatcher); } @@ -79,24 +76,24 @@ void ui_dismiss_keyboard(ProtoViewApp *app) { /* Set an alert message to be shown over any currently active view, for * the specified amount of time of 'ttl' milliseconds. */ -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) { +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl) { app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl); - snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text); + snprintf(app->alert_text, ALERT_MAX_LEN, "%s", text); } /* Cancel the alert before its time has elapsed. */ -void ui_dismiss_alert(ProtoViewApp *app) { +void ui_dismiss_alert(ProtoViewApp* app) { app->alert_dismiss_time = 0; } /* Show the alert if an alert is set. This is called after the currently * active view displayed its stuff, so we overwrite the screen with the * alert message. */ -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { - if (app->alert_dismiss_time == 0) { +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app) { + if(app->alert_dismiss_time == 0) { /* No active alert. */ return; - } else if (app->alert_dismiss_time < furi_get_tick()) { + } else if(app->alert_dismiss_time < furi_get_tick()) { /* Alert just expired. */ ui_dismiss_alert(app); return; @@ -106,41 +103,43 @@ void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t w = canvas_string_width(canvas, app->alert_text); uint8_t h = 8; // Font height. - uint8_t text_x = 64-(w/2); - uint8_t text_y = 32+4; + uint8_t text_x = 64 - (w / 2); + uint8_t text_y = 32 + 4; uint8_t padding = 3; - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2); - canvas_set_color(canvas,ColorWhite); - canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2); - canvas_set_color(canvas,ColorBlack); - canvas_draw_str(canvas,text_x,text_y,app->alert_text); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, text_x - padding, text_y - padding - h, w + padding * 2, h + padding * 2); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, + text_x - padding + 1, + text_y - padding - h + 1, + w + padding * 2 - 2, + h + padding * 2 - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, text_x, text_y, app->alert_text); } /* =========================== Canvas extensions ============================ */ -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color) -{ +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color) { struct { - uint8_t x; uint8_t y; - } dir[8] = { - {-1,-1}, - {0,-1}, - {1,-1}, - {1,0}, - {1,1}, - {0,1}, - {-1,1}, - {-1,0} - }; + uint8_t x; + uint8_t y; + } dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}}; /* Rotate in all the directions writing the same string to create a * border, then write the actual string in the other color in the * middle. */ canvas_set_color(canvas, border_color); - for (int j = 0; j < 8; j++) - canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str); + for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str); canvas_set_color(canvas, text_color); - canvas_draw_str(canvas,x,y,str); + canvas_draw_str(canvas, x, y, str); canvas_set_color(canvas, ColorBlack); } diff --git a/applications/plugins/protoview/view_build.c b/applications/plugins/protoview/view_build.c index fd276b61d..955855902 100644 --- a/applications/plugins/protoview/view_build.c +++ b/applications/plugins/protoview/view_build.c @@ -3,39 +3,38 @@ #include "app.h" -extern ProtoViewDecoder *Decoders[]; // Defined in signal.c. +extern ProtoViewDecoder* Decoders[]; // Defined in signal.c. /* Our view private data. */ #define USER_VALUE_LEN 64 typedef struct { - ProtoViewDecoder *decoder; /* Decoder we are using to create a + ProtoViewDecoder* decoder; /* Decoder we are using to create a message. */ - uint32_t cur_decoder; /* Decoder index when we are yet selecting + uint32_t cur_decoder; /* Decoder index when we are yet selecting a decoder. Used when decoder is NULL. */ - ProtoViewFieldSet *fieldset; /* The fields to populate. */ - uint32_t cur_field; /* Field we are editing right now. This + ProtoViewFieldSet* fieldset; /* The fields to populate. */ + uint32_t cur_field; /* Field we are editing right now. This is the index inside the 'fieldset' fields. */ - char *user_value; /* Keyboard input to replace the current + char* user_value; /* Keyboard input to replace the current field value goes here. */ } BuildViewPrivData; /* Not all the decoders support message bulding, so we can't just * increment / decrement the cur_decoder index here. */ -static void select_next_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - do { +static void select_next_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + do { privdata->cur_decoder++; - if (Decoders[privdata->cur_decoder] == NULL) - privdata->cur_decoder = 0; + if(Decoders[privdata->cur_decoder] == NULL) privdata->cur_decoder = 0; } while(Decoders[privdata->cur_decoder]->get_fields == NULL); } /* Like select_next_decoder() but goes backward. */ -static void select_prev_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void select_prev_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; do { - if (privdata->cur_decoder == 0) { + if(privdata->cur_decoder == 0) { /* Go one after the last one to wrap around. */ while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++; } @@ -45,69 +44,73 @@ static void select_prev_decoder(ProtoViewApp *app) { /* Render the view to select the decoder, among the ones that * support message building. */ -static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_select_decoder(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 9, "Signal creator"); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose"); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter, - Decoders[privdata->cur_decoder]->name); + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignCenter, Decoders[privdata->cur_decoder]->name); } /* Render the view that allows the user to populate the fields needed * for the selected decoder to build a message. */ -static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_set_fields(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; char buf[32]; - snprintf(buf,sizeof(buf), "%s field %d/%d", - privdata->decoder->name, (int)privdata->cur_field+1, + snprintf( + buf, + sizeof(buf), + "%s field %d/%d", + privdata->decoder->name, + (int)privdata->cur_field + 1, (int)privdata->fieldset->numfields); - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,0,0,128,21); - canvas_set_color(canvas,ColorWhite); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 128, 21); + canvas_set_color(canvas, ColorWhite); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 1, 9, buf); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit"); /* Write the field name, type, current content. */ - canvas_set_color(canvas,ColorBlack); - ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field]; - snprintf(buf,sizeof(buf), "%s %s:%d", field->name, - field_get_type_name(field), (int)field->len); + canvas_set_color(canvas, ColorBlack); + ProtoViewField* field = privdata->fieldset->fields[privdata->cur_field]; + snprintf( + buf, sizeof(buf), "%s %s:%d", field->name, field_get_type_name(field), (int)field->len); buf[0] = toupper(buf[0]); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, buf); canvas_set_font(canvas, FontSecondary); /* Render the current value between "" */ - unsigned int written = (unsigned int) field_to_string(buf+1,sizeof(buf)-1,field); + unsigned int written = (unsigned int)field_to_string(buf + 1, sizeof(buf) - 1, field); buf[0] = '"'; - if (written+3 < sizeof(buf)) memcpy(buf+written+1,"\"\x00",2); - canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,buf); + if(written + 3 < sizeof(buf)) memcpy(buf + written + 1, "\"\x00", 2); + canvas_draw_str_aligned(canvas, 63, 45, AlignCenter, AlignCenter, buf); /* Footer instructions. */ canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr"); } /* Render the build message view. */ -void render_view_build_message(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void render_view_build_message(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; - if (privdata->decoder) - render_view_set_fields(canvas,app); + if(privdata->decoder) + render_view_set_fields(canvas, app); else - render_view_select_decoder(canvas,app); + render_view_select_decoder(canvas, app); } /* Handle input for the decoder selection. */ -static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { +static void process_input_select_decoder(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { privdata->decoder = Decoders[privdata->cur_decoder]; privdata->fieldset = fieldset_new(); privdata->decoder->get_fields(privdata->fieldset); @@ -116,11 +119,8 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { * same decoder the user selected, let's populate the * defaults with the current values. So the user will * actaully edit the current message. */ - if (app->signal_decoded && - app->msg_info->decoder == privdata->decoder) - { - fieldset_copy_matching_fields(privdata->fieldset, - app->msg_info->fieldset); + if(app->signal_decoded && app->msg_info->decoder == privdata->decoder) { + fieldset_copy_matching_fields(privdata->fieldset, app->msg_info->fieldset); } /* Now we use the subview system in order to protect the @@ -128,10 +128,10 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { Since we are technically into a subview now, we'll have control of < and >. */ InputEvent ii = {.type = InputTypePress, .key = InputKeyDown}; - ui_process_subview_updown(app,ii,2); - } else if (input.key == InputKeyDown) { + ui_process_subview_updown(app, ii, 2); + } else if(input.key == InputKeyDown) { select_next_decoder(app); - } else if (input.key == InputKeyUp) { + } else if(input.key == InputKeyUp) { select_prev_decoder(app); } } @@ -140,12 +140,13 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { /* Called after the user typed the new field value in the keyboard. * Let's save it and remove the keyboard view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - BuildViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + BuildViewPrivData* privdata = app->view_privdata; - if (field_set_from_string(privdata->fieldset->fields[privdata->cur_field], - privdata->user_value, strlen(privdata->user_value)) == false) - { + if(field_set_from_string( + privdata->fieldset->fields[privdata->cur_field], + privdata->user_value, + strlen(privdata->user_value)) == false) { ui_show_alert(app, "Invalid value", 1500); } @@ -160,94 +161,88 @@ static void text_input_done_callback(void* context) { * decrement the current field in a much simpler way. * * The current filed is changed by 'incr' amount. */ -static bool increment_current_field(ProtoViewApp *app, int incr) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; - ProtoViewField *f = fs->fields[privdata->cur_field]; - return field_incr_value(f,incr); +static bool increment_current_field(ProtoViewApp* app, int incr) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; + ProtoViewField* f = fs->fields[privdata->cur_field]; + return field_incr_value(f, incr); } /* Handle input for fields editing mode. */ -static void process_input_set_fields(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; +static void process_input_set_fields(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; - if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show the keyboard to let the user type the new * value. */ - if (privdata->user_value == NULL) - privdata->user_value = malloc(USER_VALUE_LEN); - field_to_string(privdata->user_value, USER_VALUE_LEN, - fs->fields[privdata->cur_field]); - ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyDown) { - privdata->cur_field = (privdata->cur_field+1) % fs->numfields; - } else if (input.type == InputTypeShort && input.key == InputKeyUp) { - if (privdata->cur_field == 0) - privdata->cur_field = fs->numfields-1; + if(privdata->user_value == NULL) privdata->user_value = malloc(USER_VALUE_LEN); + field_to_string(privdata->user_value, USER_VALUE_LEN, fs->fields[privdata->cur_field]); + ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyDown) { + privdata->cur_field = (privdata->cur_field + 1) % fs->numfields; + } else if(input.type == InputTypeShort && input.key == InputKeyUp) { + if(privdata->cur_field == 0) + privdata->cur_field = fs->numfields - 1; else privdata->cur_field--; - } else if (input.type == InputTypeShort && input.key == InputKeyRight) { - increment_current_field(app,1); - } else if (input.type == InputTypeShort && input.key == InputKeyLeft) { - increment_current_field(app,-1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyRight) { + } else if(input.type == InputTypeShort && input.key == InputKeyRight) { + increment_current_field(app, 1); + } else if(input.type == InputTypeShort && input.key == InputKeyLeft) { + increment_current_field(app, -1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyRight) { // The reason why we don't use a large increment directly // is that certain field types only support +1 -1 increments. int times = 10; - while(times--) increment_current_field(app,1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyLeft) { + while(times--) increment_current_field(app, 1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyLeft) { int times = 10; - while(times--) increment_current_field(app,-1); - } else if (input.type == InputTypeLong && input.key == InputKeyOk) { + while(times--) increment_current_field(app, -1); + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // Build the message in a fresh raw buffer. - if (privdata->decoder->build_message) { - RawSamplesBuffer *rs = raw_samples_alloc(); - privdata->decoder->build_message(rs,privdata->fieldset); + if(privdata->decoder->build_message) { + RawSamplesBuffer* rs = raw_samples_alloc(); + privdata->decoder->build_message(rs, privdata->fieldset); app->signal_decoded = false; // So that the new signal will be - // accepted as the current signal. - scan_for_signal(app,rs); + // accepted as the current signal. + scan_for_signal(app, rs); raw_samples_free(rs); - ui_show_alert(app,"Done: press back key",3000); + ui_show_alert(app, "Done: press back key", 3000); } } } /* Handle input for the build message view. */ -void process_input_build_message(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->decoder) - process_input_set_fields(app,input); +void process_input_build_message(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->decoder) + process_input_set_fields(app, input); else - process_input_select_decoder(app,input); + process_input_select_decoder(app, input); } /* Enter view callback. */ -void view_enter_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void view_enter_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; // When we enter the view, the current decoder is just set to zero. // Seek the next valid if needed. - if (Decoders[privdata->cur_decoder]->get_fields == NULL) { + if(Decoders[privdata->cur_decoder]->get_fields == NULL) { select_next_decoder(app); } // However if there is currently a decoded message, and the // decoder of such message supports message building, let's // select it. - if (app->signal_decoded && - app->msg_info->decoder->get_fields && - app->msg_info->decoder->build_message) - { - while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) - select_next_decoder(app); + if(app->signal_decoded && app->msg_info->decoder->get_fields && + app->msg_info->decoder->build_message) { + while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) select_next_decoder(app); } } /* Called on exit for cleanup. */ -void view_exit_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->fieldset) fieldset_free(privdata->fieldset); - if (privdata->user_value) free(privdata->user_value); +void view_exit_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->fieldset) fieldset_free(privdata->fieldset); + if(privdata->user_value) free(privdata->user_value); } diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/plugins/protoview/view_direct_sampling.c index 251a289b8..1ab90f096 100644 --- a/applications/plugins/protoview/view_direct_sampling.c +++ b/applications/plugins/protoview/view_direct_sampling.c @@ -7,47 +7,46 @@ /* Read directly from the G0 CC1101 pin, and draw a black or white * dot depending on the level. */ -void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) { - if (!app->direct_sampling_enabled) { +void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) { + if(!app->direct_sampling_enabled) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas,2,9,"Direct sampling is a special"); - canvas_draw_str(canvas,2,18,"mode that displays the signal"); - canvas_draw_str(canvas,2,27,"captured in real time. Like in"); - canvas_draw_str(canvas,2,36,"a old CRT TV. It's very slow."); - canvas_draw_str(canvas,2,45,"Can crash your Flipper."); + canvas_draw_str(canvas, 2, 9, "Direct sampling is a special"); + canvas_draw_str(canvas, 2, 18, "mode that displays the signal"); + canvas_draw_str(canvas, 2, 27, "captured in real time. Like in"); + canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow."); + canvas_draw_str(canvas, 2, 45, "Can crash your Flipper."); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas,14,60,"To enable press OK"); + canvas_draw_str(canvas, 14, 60, "To enable press OK"); return; } - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 128; x++) { + for(int y = 0; y < 64; y++) { + for(int x = 0; x < 128; x++) { bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if (level) canvas_draw_dot(canvas,x,y); + if(level) canvas_draw_dot(canvas, x, y); /* Busy loop: this is a terrible approach as it blocks * everything else, but for now it's the best we can do * to obtain direct data with some spacing. */ - uint32_t x = 250; while(x--); + uint32_t x = 250; + while(x--) + ; } } canvas_set_font(canvas, FontSecondary); - canvas_draw_str_with_border(canvas,36,60,"Direct sampling", - ColorWhite,ColorBlack); + canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack); } /* Handle input */ -void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypePress && input.key == InputKeyOk) { +void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypePress && input.key == InputKeyOk) { app->direct_sampling_enabled = !app->direct_sampling_enabled; } } /* Enter view. Stop the subghz thread to prevent access as we read * the CC1101 data directly. */ -void view_enter_direct_sampling(ProtoViewApp *app) { - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { +void view_enter_direct_sampling(ProtoViewApp* app) { + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { subghz_worker_stop(app->txrx->worker); } else { raw_sampling_worker_stop(app); @@ -55,10 +54,8 @@ void view_enter_direct_sampling(ProtoViewApp *app) { } /* Exit view. Restore the subghz thread. */ -void view_exit_direct_sampling(ProtoViewApp *app) { - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { +void view_exit_direct_sampling(ProtoViewApp* app) { + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { subghz_worker_start(app->txrx->worker); } else { raw_sampling_worker_start(app); diff --git a/applications/plugins/protoview/view_info.c b/applications/plugins/protoview/view_info.c index 6aa69739c..75fc58411 100644 --- a/applications/plugins/protoview/view_info.c +++ b/applications/plugins/protoview/view_info.c @@ -20,31 +20,29 @@ typedef struct { * so that the user can see what they are saving. With left/right * you can move to next rows. Here we store where we are. */ uint32_t signal_display_start_row; - char *filename; + char* filename; uint8_t cur_info_page; // Info page to display. Useful when there are - // too many fields populated by the decoder that - // a single page is not enough. + // too many fields populated by the decoder that + // a single page is not enough. } InfoViewPrivData; /* Draw the text label and value of the specified info field at x,y. */ -static void render_info_field(Canvas *const canvas, - ProtoViewField *f, uint8_t x, uint8_t y) -{ +static void render_info_field(Canvas* const canvas, ProtoViewField* f, uint8_t x, uint8_t y) { char buf[64]; char strval[32]; - field_to_string(strval,sizeof(strval),f); - snprintf(buf,sizeof(buf),"%s: %s", f->name, strval); + field_to_string(strval, sizeof(strval), f); + snprintf(buf, sizeof(buf), "%s: %s", f->name, strval); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, x, y, buf); } /* Render the view with the detected message information. */ #define INFO_LINES_PER_PAGE 5 -static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; - uint8_t pages = (app->msg_info->fieldset->numfields - +(INFO_LINES_PER_PAGE-1)) / INFO_LINES_PER_PAGE; +static void render_subview_main(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; + uint8_t pages = + (app->msg_info->fieldset->numfields + (INFO_LINES_PER_PAGE - 1)) / INFO_LINES_PER_PAGE; privdata->cur_info_page %= pages; uint8_t current_page = privdata->cur_info_page; char buf[32]; @@ -53,9 +51,9 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t y = 8, lineheight = 10; - if (pages > 1) { - snprintf(buf,sizeof(buf),"%s %u/%u", app->msg_info->decoder->name, - current_page+1, pages); + if(pages > 1) { + snprintf( + buf, sizeof(buf), "%s %u/%u", app->msg_info->decoder->name, current_page + 1, pages); canvas_draw_str(canvas, 0, y, buf); } else { canvas_draw_str(canvas, 0, y, app->msg_info->decoder->name); @@ -64,26 +62,30 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { /* Draw the info fields. */ uint8_t max_lines = INFO_LINES_PER_PAGE; - uint32_t j = current_page*max_lines; - while (j < app->msg_info->fieldset->numfields) { - render_info_field(canvas,app->msg_info->fieldset->fields[j++],0,y); + uint32_t j = current_page * max_lines; + while(j < app->msg_info->fieldset->numfields) { + render_info_field(canvas, app->msg_info->fieldset->fields[j++], 0, y); y += lineheight; - if (--max_lines == 0) break; + if(--max_lines == 0) break; } /* Draw a vertical "save" label. Temporary solution, to switch to * something better ASAP. */ y = 37; lineheight = 7; - canvas_draw_str(canvas, 119, y, "s"); y += lineheight; - canvas_draw_str(canvas, 119, y, "a"); y += lineheight; - canvas_draw_str(canvas, 119, y, "v"); y += lineheight; - canvas_draw_str(canvas, 119, y, "e"); y += lineheight; + canvas_draw_str(canvas, 119, y, "s"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "a"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "v"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "e"); + y += lineheight; } /* Render view with save option. */ -static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +static void render_subview_save(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; /* Display our signal in digital form: here we don't show the * signal with the exact timing of the received samples, but as it @@ -92,21 +94,20 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { uint8_t rowheight = 11; uint8_t bitwidth = 4; uint8_t bitheight = 5; - uint32_t idx = privdata->signal_display_start_row * (128/4); + uint32_t idx = privdata->signal_display_start_row * (128 / 4); bool prevbit = false; - for (uint8_t y = bitheight+12; y <= rows*rowheight; y += rowheight) { - for (uint8_t x = 0; x < 128; x += 4) { - bool bit = bitmap_get(app->msg_info->bits, - app->msg_info->bits_bytes,idx); - uint8_t prevy = y + prevbit*(bitheight*-1) - 1; - uint8_t thisy = y + bit*(bitheight*-1) - 1; - canvas_draw_line(canvas,x,prevy,x,thisy); - canvas_draw_line(canvas,x,thisy,x+bitwidth-1,thisy); + for(uint8_t y = bitheight + 12; y <= rows * rowheight; y += rowheight) { + for(uint8_t x = 0; x < 128; x += 4) { + bool bit = bitmap_get(app->msg_info->bits, app->msg_info->bits_bytes, idx); + uint8_t prevy = y + prevbit * (bitheight * -1) - 1; + uint8_t thisy = y + bit * (bitheight * -1) - 1; + canvas_draw_line(canvas, x, prevy, x, thisy); + canvas_draw_line(canvas, x, thisy, x + bitwidth - 1, thisy); prevbit = bit; - if (idx >= app->msg_info->pulses_count) { + if(idx >= app->msg_info->pulses_count) { canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, x+1,thisy); - canvas_draw_dot(canvas, x+3,thisy); + canvas_draw_dot(canvas, x + 1, thisy); + canvas_draw_dot(canvas, x + 3, thisy); canvas_set_color(canvas, ColorBlack); } idx++; // Draw next bit @@ -118,28 +119,32 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { } /* Render the selected subview of this view. */ -void render_view_info(Canvas *const canvas, ProtoViewApp *app) { - if (app->signal_decoded == false) { +void render_view_info(Canvas* const canvas, ProtoViewApp* app) { + if(app->signal_decoded == false) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 30,36,"No signal decoded"); + canvas_draw_str(canvas, 30, 36, "No signal decoded"); return; } - ui_show_available_subviews(canvas,app,SubViewInfoLast); + ui_show_available_subviews(canvas, app, SubViewInfoLast); switch(app->current_subview[app->current_view]) { - case SubViewInfoMain: render_subview_main(canvas,app); break; - case SubViewInfoSave: render_subview_save(canvas,app); break; + case SubViewInfoMain: + render_subview_main(canvas, app); + break; + case SubViewInfoSave: + render_subview_save(canvas, app); + break; } } /* The user typed the file name. Let's save it and remove the keyboard * view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - InfoViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + InfoViewPrivData* privdata = app->view_privdata; - FuriString *save_path = furi_string_alloc_printf( - "%s/%s.sub", EXT_PATH("subghz"), privdata->filename); + FuriString* save_path = + furi_string_alloc_printf("%s/%s.sub", EXT_PATH("subghz"), privdata->filename); save_signal(app, furi_string_get_cstr(save_path)); furi_string_free(save_path); @@ -151,22 +156,22 @@ static void text_input_done_callback(void* context) { /* Replace all the occurrences of character c1 with c2 in the specified * string. */ -void str_replace(char *buf, char c1, char c2) { - char *p = buf; +void str_replace(char* buf, char c1, char c2) { + char* p = buf; while(*p) { - if (*p == c1) *p = c2; + if(*p == c1) *p = c2; p++; } } /* Set a random filename the user can edit. */ -void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) { +void set_signal_random_filename(ProtoViewApp* app, char* buf, size_t buflen) { char suffix[6]; - set_random_name(suffix,sizeof(suffix)); - snprintf(buf,buflen,"%.10s-%s-%d",app->msg_info->decoder->name,suffix,rand()%1000); - str_replace(buf,' ','_'); - str_replace(buf,'-','_'); - str_replace(buf,'/','_'); + set_random_name(suffix, sizeof(suffix)); + snprintf(buf, buflen, "%.10s-%s-%d", app->msg_info->decoder->name, suffix, rand() % 1000); + str_replace(buf, ' ', '_'); + str_replace(buf, '-', '_'); + str_replace(buf, '/', '_'); } /* ========================== Signal transmission =========================== */ @@ -180,20 +185,20 @@ typedef enum { SendSignalEndTransmission } SendSignalState; -#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ -#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ typedef struct { - SendSignalState state; // Current state. - uint32_t curpos; // Current bit position of data to send. - ProtoViewApp *app; // App reference. + SendSignalState state; // Current state. + uint32_t curpos; // Current bit position of data to send. + ProtoViewApp* app; // App reference. uint32_t start_gap_dur; // Gap to send at the start. - uint32_t end_gap_dur; // Gap to send at the end. + uint32_t end_gap_dur; // Gap to send at the end. } SendSignalCtx; /* Setup the state context for the callback responsible to feed data * to the subghz async tx system. */ -static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { +static void send_signal_init(SendSignalCtx* ss, ProtoViewApp* app) { ss->state = SendSignalSendStartGap; ss->curpos = 0; ss->app = app; @@ -214,27 +219,26 @@ static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { * message we are, in ss->curoff. We also send a start and end gap in order * to make sure the transmission is clear. */ -LevelDuration radio_tx_feed_data(void *ctx) { - SendSignalCtx *ss = ctx; +LevelDuration radio_tx_feed_data(void* ctx) { + SendSignalCtx* ss = ctx; /* Send start gap. */ - if (ss->state == SendSignalSendStartGap) { + if(ss->state == SendSignalSendStartGap) { ss->state = SendSignalSendBits; - return level_duration_make(0,ss->start_gap_dur); + return level_duration_make(0, ss->start_gap_dur); } /* Send data. */ - if (ss->state == SendSignalSendBits) { + if(ss->state == SendSignalSendBits) { uint32_t dur = 0, j; uint32_t level = 0; /* Let's see how many consecutive bits we have with the same * level. */ - for (j = 0; ss->curpos+j < ss->app->msg_info->pulses_count; j++) { - uint32_t l = bitmap_get(ss->app->msg_info->bits, - ss->app->msg_info->bits_bytes, - ss->curpos+j); - if (j == 0) { + for(j = 0; ss->curpos + j < ss->app->msg_info->pulses_count; j++) { + uint32_t l = + bitmap_get(ss->app->msg_info->bits, ss->app->msg_info->bits_bytes, ss->curpos + j); + if(j == 0) { /* At the first bit of this sequence, we store the * level of the sequence. */ level = l; @@ -244,22 +248,21 @@ LevelDuration radio_tx_feed_data(void *ctx) { /* As long as the level is the same, we update the duration. * Otherwise stop the loop and return this sample. */ - if (l != level) break; + if(l != level) break; dur += ss->app->msg_info->short_pulse_dur; } ss->curpos += j; /* If this was the last set of bits, change the state to * send the final gap. */ - if (ss->curpos >= ss->app->msg_info->pulses_count) - ss->state = SendSignalSendEndGap; + if(ss->curpos >= ss->app->msg_info->pulses_count) ss->state = SendSignalSendEndGap; return level_duration_make(level, dur); } /* Send end gap. */ - if (ss->state == SendSignalSendEndGap) { + if(ss->state == SendSignalSendEndGap) { ss->state = SendSignalEndTransmission; - return level_duration_make(0,ss->end_gap_dur); + return level_duration_make(0, ss->end_gap_dur); } /* End transmission. Here state is guaranteed @@ -268,7 +271,7 @@ LevelDuration radio_tx_feed_data(void *ctx) { } /* Vibrate and produce a click sound when a signal is sent. */ -void notify_signal_sent(ProtoViewApp *app) { +void notify_signal_sent(ProtoViewApp* app) { static const NotificationSequence sent_seq = { &message_blue_255, &message_vibro_on, @@ -277,59 +280,53 @@ void notify_signal_sent(ProtoViewApp *app) { &message_sound_off, &message_vibro_off, &message_blue_0, - NULL - }; + NULL}; notification_message(app->notification, &sent_seq); } /* Handle input for the info view. */ -void process_input_info(ProtoViewApp *app, InputEvent input) { +void process_input_info(ProtoViewApp* app, InputEvent input) { /* If we don't have a decoded signal, we don't allow to go up/down * in the subviews: they are only useful when a loaded signal. */ - if (app->signal_decoded && - ui_process_subview_updown(app,input,SubViewInfoLast)) return; + if(app->signal_decoded && ui_process_subview_updown(app, input, SubViewInfoLast)) return; - InfoViewPrivData *privdata = app->view_privdata; + InfoViewPrivData* privdata = app->view_privdata; int subview = ui_get_current_subview(app); /* Main subview. */ - if (subview == SubViewInfoMain) { - if (input.type == InputTypeLong && input.key == InputKeyOk) { + if(subview == SubViewInfoMain) { + if(input.type == InputTypeLong && input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show next info page. */ privdata->cur_info_page++; } - } else if (subview == SubViewInfoSave) { - /* Save subview. */ - if (input.type == InputTypePress && input.key == InputKeyRight) { + } else if(subview == SubViewInfoSave) { + /* Save subview. */ + if(input.type == InputTypePress && input.key == InputKeyRight) { privdata->signal_display_start_row++; - } else if (input.type == InputTypePress && input.key == InputKeyLeft) { - if (privdata->signal_display_start_row != 0) - privdata->signal_display_start_row--; - } else if (input.type == InputTypeLong && input.key == InputKeyOk) - { + } else if(input.type == InputTypePress && input.key == InputKeyLeft) { + if(privdata->signal_display_start_row != 0) privdata->signal_display_start_row--; + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // We have have the buffer already allocated, in case the // user aborted with BACK a previous saving. - if (privdata->filename == NULL) - privdata->filename = malloc(SAVE_FILENAME_LEN); - set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN); - ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(privdata->filename == NULL) privdata->filename = malloc(SAVE_FILENAME_LEN); + set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN); + ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { SendSignalCtx send_state; - send_signal_init(&send_state,app); - radio_tx_signal(app,radio_tx_feed_data,&send_state); + send_signal_init(&send_state, app); + radio_tx_signal(app, radio_tx_feed_data, &send_state); notify_signal_sent(app); } } } /* Called on view exit. */ -void view_exit_info(ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +void view_exit_info(ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; // When the user aborts the keyboard input, we are left with the // filename buffer allocated. - if (privdata->filename) free(privdata->filename); + if(privdata->filename) free(privdata->filename); } diff --git a/applications/plugins/protoview/view_raw_signal.c b/applications/plugins/protoview/view_raw_signal.c index 023e986f9..38354bef9 100644 --- a/applications/plugins/protoview/view_raw_signal.c +++ b/applications/plugins/protoview/view_raw_signal.c @@ -12,7 +12,7 @@ * * The 'idx' argument is the first sample to render in the circular * buffer. */ -void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *buf, uint32_t idx) { +void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* buf, uint32_t idx) { canvas_set_color(canvas, ColorBlack); int rows = 8; @@ -20,31 +20,29 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu uint32_t start_idx = idx; bool level = 0; uint32_t dur = 0, sample_num = 0; - for (int row = 0; row < rows ; row++) { - for (int x = 0; x < 128; x++) { - int y = 3 + row*8; - if (dur < time_per_pixel/2) { + for(int row = 0; row < rows; row++) { + for(int x = 0; x < 128; x++) { + int y = 3 + row * 8; + if(dur < time_per_pixel / 2) { /* Get more data. */ raw_samples_get(buf, idx++, &level, &dur); sample_num++; } - canvas_draw_line(canvas, x,y,x,y-(level*3)); + canvas_draw_line(canvas, x, y, x, y - (level * 3)); /* Write a small triangle under the last sample detected. */ - if (app->signal_bestlen != 0 && - sample_num+start_idx == app->signal_bestlen+1) - { - canvas_draw_dot(canvas,x,y+2); - canvas_draw_dot(canvas,x-1,y+3); - canvas_draw_dot(canvas,x,y+3); - canvas_draw_dot(canvas,x+1,y+3); + if(app->signal_bestlen != 0 && sample_num + start_idx == app->signal_bestlen + 1) { + canvas_draw_dot(canvas, x, y + 2); + canvas_draw_dot(canvas, x - 1, y + 3); + canvas_draw_dot(canvas, x, y + 3); + canvas_draw_dot(canvas, x + 1, y + 3); sample_num++; /* Make sure we don't mark the next, too. */ } /* Remove from the current level duration the time we * just plot. */ - if (dur > time_per_pixel) + if(dur > time_per_pixel) dur -= time_per_pixel; else dur = 0; @@ -53,61 +51,63 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu } /* Raw pulses rendering. This is our default view. */ -void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) { +void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app) { /* Show signal. */ render_signal(app, canvas, DetectedSamples, app->signal_offset); /* Show signal information. */ char buf[64]; - snprintf(buf,sizeof(buf),"%luus", - (unsigned long)DetectedSamples->short_pulse_dur); + snprintf(buf, sizeof(buf), "%luus", (unsigned long)DetectedSamples->short_pulse_dur); canvas_set_font(canvas, FontSecondary); canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack); - if (app->signal_decoded) { + if(app->signal_decoded) { canvas_set_font(canvas, FontPrimary); - canvas_draw_str_with_border(canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack); + canvas_draw_str_with_border( + canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack); } } /* Handle input for the raw pulses view. */ -void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypeRepeat) { +void process_input_raw_pulses(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypeRepeat) { /* Handle panning of the signal window. Long pressing * right will show successive samples, long pressing left * previous samples. */ - if (input.key == InputKeyRight) app->signal_offset++; - else if (input.key == InputKeyLeft) app->signal_offset--; - } else if (input.type == InputTypeLong) { - if (input.key == InputKeyOk) { + if(input.key == InputKeyRight) + app->signal_offset++; + else if(input.key == InputKeyLeft) + app->signal_offset--; + } else if(input.type == InputTypeLong) { + if(input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); } - } else if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { + } else if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { app->signal_offset = 0; - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - } else if (input.key == InputKeyDown) { + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + } else if(input.key == InputKeyDown) { /* Rescaling. The set becomes finer under 50us per pixel. */ uint32_t scale_step = app->us_scale >= 50 ? 50 : 10; - if (app->us_scale < 500) app->us_scale += scale_step; - } else if (input.key == InputKeyUp) { + if(app->us_scale < 500) app->us_scale += scale_step; + } else if(input.key == InputKeyUp) { uint32_t scale_step = app->us_scale > 50 ? 50 : 10; - if (app->us_scale > 10) app->us_scale -= scale_step; + if(app->us_scale > 10) app->us_scale -= scale_step; } } } /* Adjust raw view scale depending on short pulse duration. */ -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur) { - if (short_pulse_dur == 0) +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur) { + if(short_pulse_dur == 0) app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; - else if (short_pulse_dur < 75) + else if(short_pulse_dur < 75) app->us_scale = 10; - else if (short_pulse_dur < 145) + else if(short_pulse_dur < 145) app->us_scale = 30; - else if (short_pulse_dur < 400) + else if(short_pulse_dur < 400) app->us_scale = 100; - else if (short_pulse_dur < 1000) + else if(short_pulse_dur < 1000) app->us_scale = 200; else app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c index 1e2dce226..09abf5a2a 100644 --- a/applications/plugins/protoview/view_settings.c +++ b/applications/plugins/protoview/view_settings.c @@ -6,30 +6,30 @@ /* Renders a single view with frequency and modulation setting. However * this are logically two different views, and only one of the settings * will be highlighted. */ -void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { +void render_view_settings(Canvas* const canvas, ProtoViewApp* app) { canvas_set_font(canvas, FontPrimary); - if (app->current_view == ViewFrequencySettings) - canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack); + if(app->current_view == ViewFrequencySettings) + canvas_draw_str_with_border(canvas, 1, 10, "Frequency", ColorWhite, ColorBlack); else - canvas_draw_str(canvas,1,10,"Frequency"); + canvas_draw_str(canvas, 1, 10, "Frequency"); - if (app->current_view == ViewModulationSettings) - canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack); + if(app->current_view == ViewModulationSettings) + canvas_draw_str_with_border(canvas, 70, 10, "Modulation", ColorWhite, ColorBlack); else - canvas_draw_str(canvas,70,10,"Modulation"); + canvas_draw_str(canvas, 70, 10, "Modulation"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas,10,61,"Use up and down to modify"); + canvas_draw_str(canvas, 10, 61, "Use up and down to modify"); - if (app->txrx->debug_timer_sampling) - canvas_draw_str(canvas,3,52,"(DEBUG timer sampling is ON)"); + if(app->txrx->debug_timer_sampling) + canvas_draw_str(canvas, 3, 52, "(DEBUG timer sampling is ON)"); /* Show frequency. We can use big numbers font since it's just a number. */ - if (app->current_view == ViewFrequencySettings) { + if(app->current_view == ViewFrequencySettings) { char buf[16]; - snprintf(buf,sizeof(buf),"%.2f",(double)app->frequency/1000000); + snprintf(buf, sizeof(buf), "%.2f", (double)app->frequency / 1000000); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 30, 40, buf); - } else if (app->current_view == ViewModulationSettings) { + } else if(app->current_view == ViewModulationSettings) { int current = app->modulation; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name); @@ -37,13 +37,13 @@ void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { } /* Handle input for the settings view. */ -void process_input_settings(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypeLong && input.key == InputKeyOk) { +void process_input_settings(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypeLong && input.key == InputKeyOk) { /* Long pressing to OK sets the default frequency and * modulation. */ app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; - } else if (0 && input.type == InputTypeLong && input.key == InputKeyDown) { + } else if(0 && input.type == InputTypeLong && input.key == InputKeyDown) { /* Long pressing to down switches between normal and debug * timer sampling mode. NOTE: this feature is disabled for users, * only useful for devs (if useful at all). */ @@ -55,42 +55,40 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) { app->txrx->debug_timer_sampling = !app->txrx->debug_timer_sampling; radio_begin(app); radio_rx(app); - } else if (input.type == InputTypePress && - (input.key != InputKeyDown || input.key != InputKeyUp)) - { + } else if(input.type == InputTypePress && (input.key != InputKeyDown || input.key != InputKeyUp)) { /* Handle up and down to change frequency or modulation. */ - if (app->current_view == ViewFrequencySettings) { + if(app->current_view == ViewFrequencySettings) { size_t curidx = 0, i; size_t count = subghz_setting_get_frequency_count(app->setting); /* Scan the list of frequencies to check for the index of the * currently set frequency. */ for(i = 0; i < count; i++) { - uint32_t freq = subghz_setting_get_frequency(app->setting,i); - if (freq == app->frequency) { + uint32_t freq = subghz_setting_get_frequency(app->setting, i); + if(freq == app->frequency) { curidx = i; break; } } - if (i == count) return; /* Should never happen. */ + if(i == count) return; /* Should never happen. */ - if (input.key == InputKeyUp) { - curidx = curidx == 0 ? count-1 : curidx-1; - } else if (input.key == InputKeyDown) { - curidx = (curidx+1) % count; + if(input.key == InputKeyUp) { + curidx = curidx == 0 ? count - 1 : curidx - 1; + } else if(input.key == InputKeyDown) { + curidx = (curidx + 1) % count; } else { return; } - app->frequency = subghz_setting_get_frequency(app->setting,curidx); - } else if (app->current_view == ViewModulationSettings) { + app->frequency = subghz_setting_get_frequency(app->setting, curidx); + } else if(app->current_view == ViewModulationSettings) { uint32_t count = 0; uint32_t modid = app->modulation; while(ProtoViewModulations[count].name != NULL) count++; - if (input.key == InputKeyUp) { - modid = modid == 0 ? count-1 : modid-1; - } else if (input.key == InputKeyDown) { - modid = (modid+1) % count; + if(input.key == InputKeyUp) { + modid = modid == 0 ? count - 1 : modid - 1; + } else if(input.key == InputKeyDown) { + modid = (modid + 1) % count; } else { return; } @@ -106,9 +104,13 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) { /* When the user switches to some other view, if they changed the parameters * we need to restart the radio with the right frequency and modulation. */ -void view_exit_settings(ProtoViewApp *app) { - if (app->txrx->freq_mod_changed) { - FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name); +void view_exit_settings(ProtoViewApp* app) { + if(app->txrx->freq_mod_changed) { + FURI_LOG_E( + TAG, + "Setting view, setting frequency/modulation to %lu %s", + app->frequency, + ProtoViewModulations[app->modulation].name); radio_rx_end(app); radio_begin(app); radio_rx(app); diff --git a/applications/plugins/tama_p1/hal.c b/applications/plugins/tama_p1/hal.c index 211457803..585cd88c4 100644 --- a/applications/plugins/tama_p1/hal.c +++ b/applications/plugins/tama_p1/hal.c @@ -40,7 +40,7 @@ static void tama_p1_hal_log(log_level_t level, char* buff, ...) { va_list args; va_start(args, buff); furi_string_cat_vprintf(string, buff, args); - va_end(args); + va_end(args); switch(level) { case LOG_ERROR: diff --git a/applications/plugins/tama_p1/tama.h b/applications/plugins/tama_p1/tama.h index e2a267443..e8eecc945 100644 --- a/applications/plugins/tama_p1/tama.h +++ b/applications/plugins/tama_p1/tama.h @@ -13,7 +13,6 @@ #define STATE_FILE_VERSION 2 #define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin") - typedef struct { FuriThread* thread; hal_t hal; diff --git a/applications/plugins/tama_p1/tama_p1.c b/applications/plugins/tama_p1/tama_p1.c index 7184638d7..0e7686a06 100644 --- a/applications/plugins/tama_p1/tama_p1.c +++ b/applications/plugins/tama_p1/tama_p1.c @@ -52,7 +52,6 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { uint16_t lcd_icon_lower_left = lcd_matrix_left; uint16_t lcd_icon_spacing_horiz = (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; - uint16_t y = lcd_matrix_top; for(uint8_t row = 0; row < 16; ++row) { @@ -71,7 +70,7 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { // Start drawing icons uint8_t lcd_icons = g_ctx->icons; - + // Draw top icons y = lcd_icon_upper_top; // y = 64 - TAMA_LCD_ICON_SIZE; @@ -114,135 +113,134 @@ static void tama_p1_update_timer_callback(FuriMessageQueue* event_queue) { TamaEvent event = {.type = EventTypeTick}; furi_message_queue_put(event_queue, &event, 0); } - -static void tama_p1_load_state() { - state_t *state; + +static void tama_p1_load_state() { + state_t* state; uint8_t buf[4]; - bool error = false; + bool error = false; state = tamalib_get_state(); - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - - storage_file_read(file, &buf, 4); - if (buf[0] != (uint8_t) STATE_FILE_MAGIC[0] || buf[1] != (uint8_t) STATE_FILE_MAGIC[1] || - buf[2] != (uint8_t) STATE_FILE_MAGIC[2] || buf[3] != (uint8_t) STATE_FILE_MAGIC[3]) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_read(file, &buf, 4); + if(buf[0] != (uint8_t)STATE_FILE_MAGIC[0] || buf[1] != (uint8_t)STATE_FILE_MAGIC[1] || + buf[2] != (uint8_t)STATE_FILE_MAGIC[2] || buf[3] != (uint8_t)STATE_FILE_MAGIC[3]) { FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH); error = true; } - storage_file_read(file, &buf, 1); - if (buf[0] != STATE_FILE_VERSION) { + storage_file_read(file, &buf, 1); + if(buf[0] != STATE_FILE_VERSION) { FURI_LOG_E(TAG, "FATAL: Unsupported version"); error = true; } - if (!error) { + if(!error) { FURI_LOG_D(TAG, "Reading save.bin"); - storage_file_read(file, &buf, 2); - *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8); - - storage_file_read(file, &buf, 2); - *(state->x) = buf[0] | ((buf[1] & 0xF) << 8); + storage_file_read(file, &buf, 2); + *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8); - storage_file_read(file, &buf, 2); - *(state->y) = buf[0] | ((buf[1] & 0xF) << 8); + storage_file_read(file, &buf, 2); + *(state->x) = buf[0] | ((buf[1] & 0xF) << 8); - storage_file_read(file, &buf, 1); - *(state->a) = buf[0] & 0xF; + storage_file_read(file, &buf, 2); + *(state->y) = buf[0] | ((buf[1] & 0xF) << 8); - storage_file_read(file, &buf, 1); - *(state->b) = buf[0] & 0xF; + storage_file_read(file, &buf, 1); + *(state->a) = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); + *(state->b) = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); *(state->np) = buf[0] & 0x1F; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->sp) = buf[0]; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->flags) = buf[0] & 0xF; - storage_file_read(file, &buf, 4); + storage_file_read(file, &buf, 4); *(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - storage_file_read(file, &buf, 4); - *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + storage_file_read(file, &buf, 4); + *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | + (buf[3] << 24); - storage_file_read(file, &buf, 4); - *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + storage_file_read(file, &buf, 4); + *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | + (buf[3] << 24); - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_enabled) = buf[0] & 0x1; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_data) = buf[0]; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_rld) = buf[0]; - storage_file_read(file, &buf, 4); + storage_file_read(file, &buf, 4); *(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); FURI_LOG_D(TAG, "Restoring Interupts"); - for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < INT_SLOT_NUM; i++) { + storage_file_read(file, &buf, 1); state->interrupts[i].factor_flag_reg = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); state->interrupts[i].mask_reg = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); state->interrupts[i].triggered = buf[0] & 0x1; } /* First 640 half bytes correspond to the RAM */ FURI_LOG_D(TAG, "Restoring RAM"); - for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + storage_file_read(file, &buf, 1); SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF); } /* I/Os are from 0xF00 to 0xF7F */ FURI_LOG_D(TAG, "Restoring I/O"); - for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < MEM_IO_SIZE; i++) { + storage_file_read(file, &buf, 1); SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF); - } - FURI_LOG_D(TAG, "Refreshing Hardware"); - tamalib_refresh_hw(); - } + } + FURI_LOG_D(TAG, "Refreshing Hardware"); + tamalib_refresh_hw(); + } } - + storage_file_close(file); storage_file_free(file); - furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_STORAGE); } - static void tama_p1_save_state() { - // Saving state FURI_LOG_D(TAG, "Saving Gamestate"); uint8_t buf[4]; - state_t *state; + state_t* state; uint32_t offset = 0; state = tamalib_get_state(); - - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - buf[0] = (uint8_t) STATE_FILE_MAGIC[0]; - buf[1] = (uint8_t) STATE_FILE_MAGIC[1]; - buf[2] = (uint8_t) STATE_FILE_MAGIC[2]; - buf[3] = (uint8_t) STATE_FILE_MAGIC[3]; + buf[0] = (uint8_t)STATE_FILE_MAGIC[0]; + buf[1] = (uint8_t)STATE_FILE_MAGIC[1]; + buf[2] = (uint8_t)STATE_FILE_MAGIC[2]; + buf[3] = (uint8_t)STATE_FILE_MAGIC[3]; offset += storage_file_write(file, &buf, sizeof(buf)); - + buf[0] = STATE_FILE_VERSION & 0xFF; offset += storage_file_write(file, &buf, 1); - + buf[0] = *(state->pc) & 0xFF; buf[1] = (*(state->pc) >> 8) & 0x1F; offset += storage_file_write(file, &buf, 2); @@ -303,7 +301,7 @@ static void tama_p1_save_state() { buf[3] = (*(state->call_depth) >> 24) & 0xFF; offset += storage_file_write(file, &buf, sizeof(buf)); - for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { + for(uint32_t i = 0; i < INT_SLOT_NUM; i++) { buf[0] = state->interrupts[i].factor_flag_reg & 0xF; offset += storage_file_write(file, &buf, 1); @@ -315,17 +313,17 @@ static void tama_p1_save_state() { } /* First 640 half bytes correspond to the RAM */ - for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) { buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF; offset += storage_file_write(file, &buf, 1); } /* I/Os are from 0xF00 to 0xF7F */ - for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { + for(uint32_t i = 0; i < MEM_IO_SIZE; i++) { buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF; offset += storage_file_write(file, &buf, 1); } - } + } storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -333,7 +331,6 @@ static void tama_p1_save_state() { FURI_LOG_D(TAG, "Finished Writing %lu", offset); } - static int32_t tama_p1_worker(void* context) { bool running = true; FuriMutex* mutex = context; @@ -357,8 +354,6 @@ static int32_t tama_p1_worker(void* context) { furi_mutex_release(mutex); return 0; } - - static void tama_p1_init(TamaApp* const ctx) { g_ctx = ctx; @@ -485,9 +480,9 @@ int32_t tama_p1_app(void* p) { tamalib_set_button(BTN_MIDDLE, tama_btn_state); } else if(event.input.key == InputKeyRight) { tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { + } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { // TODO: pause or fast-forward tamagotchi - tama_p1_save_state(); + tama_p1_save_state(); } else if(event.input.key == InputKeyUp) { // mute tamagotchi tamalib_set_button(BTN_LEFT, tama_btn_state); tamalib_set_button(BTN_RIGHT, tama_btn_state); @@ -500,7 +495,7 @@ int32_t tama_p1_app(void* p) { furi_timer_stop(timer); running = false; - tama_p1_save_state(); + tama_p1_save_state(); } } diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index c1eb50d59..5239d72d5 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -145,7 +145,8 @@ void animation_manager_check_blocking_process(AnimationManager* animation_manage const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(animation_manager->current_animation); - bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); + bool valid = animation_manager_is_valid_idle_animation( + manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); if(!valid) { animation_manager_start_new_idle(animation_manager); @@ -201,8 +202,10 @@ static void animation_manager_start_new_idle(AnimationManager* animation_manager animation_storage_get_bubble_animation(animation_manager->current_animation); animation_manager->state = AnimationManagerStateIdle; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims); - furi_timer_start(animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); + int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : + (xtreme_settings->cycle_anims); + furi_timer_start( + animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); } static bool animation_manager_check_blocking(AnimationManager* animation_manager) { @@ -355,7 +358,7 @@ static bool animation_manager_is_valid_idle_animation( result = (sd_status == FSE_NOT_READY); } - if (!unlock) { + if(!unlock) { if((stats->butthurt < info->min_butthurt) || (stats->butthurt > info->max_butthurt)) { result = false; } @@ -370,8 +373,9 @@ static bool animation_manager_is_valid_idle_animation( static StorageAnimation* animation_manager_select_idle_animation(AnimationManager* animation_manager) { const char* old_animation_name = NULL; - if (animation_manager->current_animation) { - old_animation_name = animation_storage_get_meta(animation_manager->current_animation)->name; + if(animation_manager->current_animation) { + old_animation_name = + animation_storage_get_meta(animation_manager->current_animation)->name; } StorageAnimationList_t animation_list; @@ -391,8 +395,8 @@ static StorageAnimation* animation_storage_get_meta(storage_animation); bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, unlock); - if (old_animation_name != NULL) { - if (strcmp(manifest_info->name, old_animation_name) == 0) { + if(old_animation_name != NULL) { + if(strcmp(manifest_info->name, old_animation_name) == 0) { valid = false; } } @@ -512,7 +516,8 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m furi_record_close(RECORD_DOLPHIN); const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(restore_animation); - bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); + bool valid = animation_manager_is_valid_idle_animation( + manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); if(valid) { animation_manager_replace_current_animation( animation_manager, restore_animation); @@ -523,12 +528,16 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m animation_manager->idle_animation_timer, animation_manager->freezed_animation_time_left); } else { - const BubbleAnimation* bubble_animation = animation_storage_get_bubble_animation( - animation_manager->current_animation); + const BubbleAnimation* bubble_animation = + animation_storage_get_bubble_animation( + animation_manager->current_animation); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims); + int32_t duration = (xtreme_settings->cycle_anims == 0) ? + (bubble_animation->duration) : + (xtreme_settings->cycle_anims); furi_timer_start( - animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); + animation_manager->idle_animation_timer, + (duration > 0) ? (duration * 1000) : 0); } } } else { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 28cdfe810..c717b0c36 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -35,18 +35,18 @@ void animation_handler_select_manifest() { FuriString* anim_dir = furi_string_alloc(); FuriString* manifest = furi_string_alloc(); bool use_asset_pack = xtreme_settings->asset_pack[0] != '\0'; - if (use_asset_pack) { + if(use_asset_pack) { furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack); furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); - if (storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { + if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { FURI_LOG_I(TAG, "Custom Manifest selected"); } else { use_asset_pack = false; } furi_record_close(RECORD_STORAGE); } - if (!use_asset_pack) { + if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); if(xtreme_settings->nsfw_mode) { furi_string_cat_str(anim_dir, "/nsfw"); @@ -58,7 +58,8 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); - strlcpy(ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE)); + strlcpy( + ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE)); furi_string_free(manifest); furi_string_free(anim_dir); } diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 4cdfa54da..594676c28 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -68,7 +68,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { str = "Set PIN + Off"; } } else if(i == DesktopLockMenuIndexXtremeSettings) { - str = "Xtreme Settings"; + str = "Xtreme Settings"; } if(str) //-V547 diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 72fa75ac0..419245a7c 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -15,9 +15,10 @@ #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 -const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, 1350, 1600, - 1850, 2100, 2400, 2700, 3000, 3350, 3700, 4050, 4450, 4850, - 5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9200}; +const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, + 1350, 1600, 1850, 2100, 2400, 2700, 3000, 3350, + 3700, 4050, 4450, 4850, 5250, 5700, 6150, 6600, + 7100, 7600, 8100, 8650, 9200}; #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index f0f7735fb..e52cb4e10 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -26,7 +26,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge); if((battery_style == BatteryStylePercent) && - (power->state != + (power->state != PowerStateCharging)) { //if display battery percentage, black background white text canvas_set_font(canvas, FontBatteryPercent); canvas_set_color(canvas, ColorBlack); @@ -36,7 +36,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } else if( (battery_style == BatteryStyleInvertedPercent) && (power->state != - PowerStateCharging)) { //if display inverted percentage, white background black text + PowerStateCharging)) { //if display inverted percentage, white background black text canvas_set_font(canvas, FontBatteryPercent); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); @@ -74,7 +74,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { (battery_style == BatteryStyleBarPercent) && (power->state != PowerStateCharging) && // Default bar display with percentage (power->info.voltage_battery_charging >= - 4.2)) { // not looking nice with low voltage indicator + 4.2)) { // not looking nice with low voltage indicator canvas_set_font(canvas, FontBatteryPercent); // align charge dispaly value with digits to draw @@ -145,8 +145,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); // TODO: replace -1 magic for uint8_t with re-framing - if(battery_style == BatteryStylePercent || - battery_style == BatteryStyleBarPercent) { + if(battery_style == BatteryStylePercent || battery_style == BatteryStyleBarPercent) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 42712bc9c..918083265 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -222,9 +222,9 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { snprintf(header, sizeof(header), "Charged!"); } - if (!strcmp(value, "")) { + if(!strcmp(value, "")) { canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, header); - } else if (!strcmp(header, "")) { + } else if(!strcmp(header, "")) { canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, value); } else { canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header); @@ -298,7 +298,6 @@ const AboutDialogScreen about_screens[] = { const int about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); - int32_t about_settings_app(void* p) { bool battery_info = false; if(p && strlen(p) && !strcmp(p, "batt")) { @@ -324,24 +323,19 @@ int32_t about_settings_app(void* p) { DialogMessageButton screen_result; // draw empty screen to prevent menu flickering - view_dispatcher_add_view( - view_dispatcher, battery_info_index, battery_view); + view_dispatcher_add_view(view_dispatcher, battery_info_index, battery_view); view_dispatcher_add_view( view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen)); view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); screen_index = -1 + !battery_info; while(screen_index > -2) { - - if (screen_index == -1) { - if (!battery_info) { + if(screen_index == -1) { + if(!battery_info) { break; } with_view_model( - battery_view, - PowerInfo * model, - { power_get_info(power, model); }, - true); + battery_view, PowerInfo * model, { power_get_info(power, model); }, true); view_dispatcher_switch_to_view(view_dispatcher, battery_info_index); furi_semaphore_acquire(semaphore, 2000); } else { @@ -360,7 +354,6 @@ int32_t about_settings_app(void* p) { screen_index = -2; } } - } dialog_message_free(message); diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 3bea705ac..f0430de5d 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -67,7 +67,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { uint32_t xp_need = dolphin_state_xp_to_levelup(stats->icounter); uint32_t xp_above_last_levelup = dolphin_state_xp_above_last_levelup(stats->icounter); uint32_t xp_levelup = 0; - if (ctx->progress_total) { + if(ctx->progress_total) { xp_levelup = xp_need + stats->icounter; } else { xp_levelup = xp_need + xp_above_last_levelup; diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 90bdde83f..53d679efb 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -15,8 +15,12 @@ static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1)); - strlcpy(XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), MAX_PACK_NAME_LEN); + variable_item_set_current_value_text( + item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1)); + strlcpy( + XTREME_SETTINGS()->asset_pack, + index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), + MAX_PACK_NAME_LEN); app->settings_changed = true; app->assets_changed = true; } @@ -33,8 +37,20 @@ static void xtreme_settings_scene_start_anim_speed_changed(VariableItem* item) { app->settings_changed = true; } -const char* const cycle_anims_names[] = - {"OFF", "Meta.txt", "30 S", "1 M", "5 M", "10 M", "15 M", "30 M", "1 H", "2 H", "6 H", "12 H", "24 H"}; +const char* const cycle_anims_names[] = { + "OFF", + "Meta.txt", + "30 S", + "1 M", + "5 M", + "10 M", + "15 M", + "30 M", + "1 H", + "2 H", + "6 H", + "12 H", + "24 H"}; const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; static void xtreme_settings_scene_start_cycle_anims_changed(VariableItem* item) { @@ -62,8 +78,7 @@ const int32_t battery_style_values[COUNT_OF(battery_style_names)] = { BatteryStyleInvertedPercent, BatteryStyleRetro3, BatteryStyleRetro5, - BatteryStyleBarPercent -}; + BatteryStyleBarPercent}; static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -112,7 +127,8 @@ void xtreme_settings_scene_start_on_enter(void* context) { app->subghz_extend = false; app->subghz_bypass = false; if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + flipper_format_read_bool( + subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); flipper_format_read_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); } flipper_format_free(subghz_range); @@ -123,14 +139,15 @@ void xtreme_settings_scene_start_on_enter(void* context) { FileInfo info; char* name = malloc(MAX_PACK_NAME_LEN); do { - if (!storage_dir_open(folder, PACKS_DIR)) break; + if(!storage_dir_open(folder, PACKS_DIR)) break; while(true) { - if (!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break; + if(!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break; if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); asset_packs_push_back(app->asset_packs, copy); - if (strcmp(name, xtreme_settings->asset_pack) == 0) current_pack = asset_packs_size(app->asset_packs); + if(strcmp(name, xtreme_settings->asset_pack) == 0) + current_pack = asset_packs_size(app->asset_packs); } } } while(false); @@ -139,11 +156,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { furi_record_close(RECORD_STORAGE); item = variable_item_list_add( - var_item_list, - "Base Graphics", - 2, - xtreme_settings_scene_start_base_graphics_changed, - app); + var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); @@ -154,7 +167,8 @@ void xtreme_settings_scene_start_on_enter(void* context) { xtreme_settings_scene_start_asset_pack_changed, app); variable_item_set_current_value_index(item, current_pack); - variable_item_set_current_value_text(item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1)); + variable_item_set_current_value_text( + item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1)); item = variable_item_list_add( var_item_list, @@ -179,11 +193,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, cycle_anims_names[value_index]); item = variable_item_list_add( - var_item_list, - "Unlock Anims", - 2, - xtreme_settings_scene_start_unlock_anims_changed, - app); + var_item_list, "Unlock Anims", 2, xtreme_settings_scene_start_unlock_anims_changed, app); variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); @@ -210,30 +220,18 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, level_str); item = variable_item_list_add( - var_item_list, - "SubGHz Extend", - 2, - xtreme_settings_scene_start_subghz_extend_changed, - app); + var_item_list, "SubGHz Extend", 2, xtreme_settings_scene_start_subghz_extend_changed, app); variable_item_set_current_value_index(item, app->subghz_extend); variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); item = variable_item_list_add( - var_item_list, - "SubGHz Bypass", - 2, - xtreme_settings_scene_start_subghz_bypass_changed, - app); + var_item_list, "SubGHz Bypass", 2, xtreme_settings_scene_start_subghz_bypass_changed, app); variable_item_set_current_value_index(item, app->subghz_bypass); variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - FuriString* version_tag = furi_string_alloc_printf("%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); - item = variable_item_list_add( - var_item_list, - furi_string_get_cstr(version_tag), - 0, - NULL, - app); + FuriString* version_tag = furi_string_alloc_printf( + "%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); + item = variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList); } @@ -248,7 +246,7 @@ bool xtreme_settings_scene_start_on_event(void* context, SceneManagerEvent event void xtreme_settings_scene_start_on_exit(void* context) { XtremeSettingsApp* app = context; asset_packs_it_t it; - for (asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { + for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { free(*asset_packs_cref(it)); } asset_packs_clear(app->asset_packs); diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 444b50951..0f6ab998d 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -5,94 +5,158 @@ XtremeAssets* xtreme_assets = NULL; XtremeAssets* XTREME_ASSETS() { - if (xtreme_assets == NULL) { + if(xtreme_assets == NULL) { XTREME_ASSETS_LOAD(); } return xtreme_assets; } void XTREME_ASSETS_LOAD() { - if (xtreme_assets != NULL) return; + if(xtreme_assets != NULL) return; xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - if (xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + if(xtreme_settings->nsfw_mode) { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_flipper; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_flipper; + xtreme_assets->I_passport_okay_46x49 = &I_flipper; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = + &I_iButtonDolphinVerySuccess_108x52_sfw; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; + xtreme_assets->I_passport_DB = &I_passport_DB_sfw; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; + xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; } - if (xtreme_settings->asset_pack[0] == '\0') return; + if(xtreme_settings->asset_pack[0] == '\0') return; FileInfo info; FuriString* path = furi_string_alloc(); const char* pack = xtreme_settings->asset_pack; furi_string_printf(path, PACKS_DIR "/%s", pack); Storage* storage = furi_record_open(RECORD_STORAGE); - if (storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - swap_bmx_icon(&xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinCommon_56x48, pack, "Dolphin/DolphinCommon_56x48.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinMafia_115x62, pack, "iButton/DolphinMafia_115x62.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_iButtonDolphinVerySuccess_108x52, pack, "iButton/iButtonDolphinVerySuccess_108x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinReadingSuccess_59x63, pack, "Infrared/DolphinReadingSuccess_59x63.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_NFC_dolphin_emulation_47x61, pack, "NFC/NFC_dolphin_emulation_47x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_bad_46x49, pack, "Passport/passport_bad_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_happy_46x49, pack, "Passport/passport_happy_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_okay_46x49, pack, "Passport/passport_okay_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinReceive_97x61, pack, "RFID/RFIDDolphinReceive_97x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSend_97x61, pack, "RFID/RFIDDolphinSend_97x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSuccess_108x57, pack, "RFID/RFIDDolphinSuccess_108x57.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinCommon_56x48, + pack, + "Dolphin/DolphinCommon_56x48.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinMafia_115x62, + pack, + "iButton/DolphinMafia_115x62.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, + pack, + "iButton/iButtonDolphinVerySuccess_108x52.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinReadingSuccess_59x63, + pack, + "Infrared/DolphinReadingSuccess_59x63.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_NFC_dolphin_emulation_47x61, + pack, + "NFC/NFC_dolphin_emulation_47x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_bad_46x49, + pack, + "Passport/passport_bad_46x49.bmx", + path, + file); + swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_passport_happy_46x49, + pack, + "Passport/passport_happy_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_okay_46x49, + pack, + "Passport/passport_okay_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinReceive_97x61, + pack, + "RFID/RFIDDolphinReceive_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSend_97x61, + pack, + "RFID/RFIDDolphinSend_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSuccess_108x57, + pack, + "RFID/RFIDDolphinSuccess_108x57.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); storage_file_free(file); } @@ -100,9 +164,14 @@ void XTREME_ASSETS_LOAD() { furi_string_free(path); } -void swap_bmx_icon(const Icon** replace, const char* pack, const char* name, FuriString* path, File* file) { +void swap_bmx_icon( + const Icon** replace, + const char* pack, + const char* name, + FuriString* path, + File* file) { furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); - if (storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; storage_file_read(file, &width, 4); diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index df618c47d..c49f5b590 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -34,6 +34,11 @@ XtremeAssets* XTREME_ASSETS(); void XTREME_ASSETS_LOAD(); -void swap_bmx_icon(const Icon** replace, const char* base, const char* name, FuriString* path, File* file); +void swap_bmx_icon( + const Icon** replace, + const char* base, + const char* name, + FuriString* path, + File* file); void free_bmx_icon(Icon* icon); diff --git a/applications/settings/xtreme_settings/xtreme_settings.c b/applications/settings/xtreme_settings/xtreme_settings.c index 84018bcf0..3db0a4c1c 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.c +++ b/applications/settings/xtreme_settings/xtreme_settings.c @@ -3,17 +3,21 @@ XtremeSettings* xtreme_settings = NULL; XtremeSettings* XTREME_SETTINGS() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } return xtreme_settings; } bool XTREME_SETTINGS_LOAD() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { xtreme_settings = malloc(sizeof(XtremeSettings)); bool loaded = saved_struct_load( - XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION); + XTREME_SETTINGS_PATH, + xtreme_settings, + sizeof(XtremeSettings), + XTREME_SETTINGS_MAGIC, + XTREME_SETTINGS_VERSION); if(!loaded) { memset(xtreme_settings, 0, sizeof(XtremeSettings)); loaded = XTREME_SETTINGS_SAVE(); @@ -24,9 +28,13 @@ bool XTREME_SETTINGS_LOAD() { } bool XTREME_SETTINGS_SAVE() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } return saved_struct_save( - XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION); + XTREME_SETTINGS_PATH, + xtreme_settings, + sizeof(XtremeSettings), + XTREME_SETTINGS_MAGIC, + XTREME_SETTINGS_VERSION); } diff --git a/applications/settings/xtreme_settings/xtreme_settings_app.c b/applications/settings/xtreme_settings/xtreme_settings_app.c index 780e76730..6b0a12f1c 100644 --- a/applications/settings/xtreme_settings/xtreme_settings_app.c +++ b/applications/settings/xtreme_settings/xtreme_settings_app.c @@ -15,10 +15,10 @@ static bool xtreme_settings_back_event_callback(void* context) { furi_assert(context); XtremeSettingsApp* app = context; - if (app->level_changed) { + if(app->level_changed) { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - if (app->dolphin_level != stats.level) { + if(app->dolphin_level != stats.level) { int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0; dolphin->state->data.icounter = xp + 1; dolphin->state->dirty = true; @@ -27,20 +27,22 @@ static bool xtreme_settings_back_event_callback(void* context) { furi_record_close(RECORD_DOLPHIN); } - if (app->subghz_changed) { + if(app->subghz_changed) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* subghz_range = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_insert_or_update_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); - flipper_format_insert_or_update_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); + flipper_format_insert_or_update_bool( + subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + flipper_format_insert_or_update_bool( + subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); } flipper_format_free(subghz_range); furi_record_close(RECORD_STORAGE); } - if (app->settings_changed) { + if(app->settings_changed) { XTREME_SETTINGS_SAVE(); - if (app->assets_changed) { + if(app->assets_changed) { popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter); popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter); popup_set_callback(app->popup, xtreme_settings_reboot); @@ -81,9 +83,7 @@ XtremeSettingsApp* xtreme_settings_app_alloc() { app->popup = popup_alloc(); view_dispatcher_add_view( - app->view_dispatcher, - XtremeSettingsAppViewPopup, - popup_get_view(app->popup)); + app->view_dispatcher, XtremeSettingsAppViewPopup, popup_get_view(app->popup)); // Set first scene scene_manager_next_scene(app->scene_manager, XtremeSettingsAppSceneStart); From d833d6dbb24daf6ba1a3962f815c93741d602713 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 27 Jan 2023 02:11:53 +0000 Subject: [PATCH 025/231] Update build.yml --- .github/workflows/build.yml | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99f80fefa..773fe8d1d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,12 +80,46 @@ jobs: run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Archive artifacts' + - name: 'Firmware artifact' uses: actions/upload-artifact@v3 with: name: firmware path: | artifacts + + - name: 'Updater artifact' + uses: actions/upload-artifact@v3 + with: + name: updater + path: | + artifacts/f7-* + + - name: Artifact info + id: artifact-info + uses: dawidd6/action-download-artifact@v2 + with: + name: updater + dry_run: true + + - name: 'Find Previous Comment' + if: ${{ github.event.pull_request }} + uses: peter-evans/find-comment@v1 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'Compiled firmware for commit' + + - name: 'Create or update comment' + if: ${{ github.event.pull_request}} + uses: peter-evans/create-or-update-comment@v1 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) + edit-mode: replace compact: if: ${{ !startsWith(github.ref, 'refs/tags') }} From c382f9d110b3176dd4ffa8edec4a84fef2fb2512 Mon Sep 17 00:00:00 2001 From: WillyJL Date: Fri, 27 Jan 2023 02:25:27 +0000 Subject: [PATCH 026/231] Update build.yml --- .github/workflows/build.yml | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 773fe8d1d..5b19d123b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -80,13 +80,6 @@ jobs: run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - - name: 'Firmware artifact' - uses: actions/upload-artifact@v3 - with: - name: firmware - path: | - artifacts - - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: @@ -94,12 +87,12 @@ jobs: path: | artifacts/f7-* - - name: Artifact info - id: artifact-info - uses: dawidd6/action-download-artifact@v2 + - name: 'Firmware artifact' + uses: actions/upload-artifact@v3 with: - name: updater - dry_run: true + name: firmware + path: | + artifacts - name: 'Find Previous Comment' if: ${{ github.event.pull_request }} @@ -109,6 +102,12 @@ jobs: issue-number: ${{ github.event.pull_request.number }} comment-author: 'github-actions[bot]' body-includes: 'Compiled firmware for commit' + + - name: Artifact info + id: artifact-info + uses: dawidd6/action-download-artifact@v2 + with: + dry_run: true - name: 'Create or update comment' if: ${{ github.event.pull_request}} From 8fc834090d289ad75de8261ad69ec310a39b5834 Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Fri, 27 Jan 2023 06:00:25 +0100 Subject: [PATCH 027/231] nfc: Fix sector reads when one block is unreadable for MIFARE Classic (#2296) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix sector reads when one block is unreadable * Auth on the correct block instead of first * Fix in sector reader as well * Apply patch by @gornekich Co-authored-by: gornekich Co-authored-by: あく --- lib/nfc/protocols/mifare_classic.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index c91e9c605..d1aadf06f 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -595,6 +595,14 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { mf_classic_set_block_read(data, i, &block_tmp); blocks_read++; + } else if(i > start_block) { + // Try to re-auth to read block in case prevous block was protected from read + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyA, &crypto, false, 0)) break; + if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mf_classic_set_block_read(data, i, &block_tmp); + blocks_read++; + } } } else { blocks_read++; @@ -607,13 +615,20 @@ void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, u if(!key_b_found) break; FURI_LOG_D(TAG, "Try to read blocks with key B"); key = nfc_util_bytes2num(sec_tr->key_b, sizeof(sec_tr->key_b)); - furi_hal_nfc_sleep(); if(!mf_classic_auth(tx_rx, start_block, key, MfClassicKeyB, &crypto, false, 0)) break; for(size_t i = start_block; i < start_block + total_blocks; i++) { if(!mf_classic_is_block_read(data, i)) { if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { mf_classic_set_block_read(data, i, &block_tmp); blocks_read++; + } else if(i > start_block) { + // Try to re-auth to read block in case prevous block was protected from read + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, i, key, MfClassicKeyB, &crypto, false, 0)) break; + if(mf_classic_read_block(tx_rx, &crypto, i, &block_tmp)) { + mf_classic_set_block_read(data, i, &block_tmp); + blocks_read++; + } } } else { blocks_read++; @@ -665,6 +680,11 @@ static bool mf_classic_read_sector_with_reader( // Read blocks for(uint8_t i = 0; i < sector->total_blocks; i++) { + if(mf_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i])) continue; + if(i == 0) continue; + // Try to auth to read next block in case previous is locked + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, first_block + i, key, key_type, crypto, false, 0)) continue; mf_classic_read_block(tx_rx, crypto, first_block + i, §or->block[i]); } // Save sector keys in last block From 4dc4d34d04c4d998ba5e05a2bca8a6d17aeefd01 Mon Sep 17 00:00:00 2001 From: Emily Trau Date: Fri, 27 Jan 2023 16:10:08 +1100 Subject: [PATCH 028/231] emv: parse track1&2 equivalent data (#2332) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * emv: parse track1&2 equivalent data * emv: alternate expiry parser * nfc: log EMV track1&2 data to trace output Co-authored-by: gornekich Co-authored-by: あく --- lib/nfc/protocols/emv.c | 35 +++++++++++++++++++++++++++++------ lib/nfc/protocols/emv.h | 3 ++- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/lib/nfc/protocols/emv.c b/lib/nfc/protocols/emv.c index e00d09e70..4c4ac856b 100644 --- a/lib/nfc/protocols/emv.c +++ b/lib/nfc/protocols/emv.c @@ -142,21 +142,44 @@ static bool emv_decode_response(uint8_t* buff, uint16_t len, EmvApplication* app success = true; FURI_LOG_T(TAG, "found EMV_TAG_AFL %x (len=%d)", tag, tlen); break; - case EMV_TAG_CARD_NUM: // Track 2 Equivalent Data. 0xD0 delimits PAN from expiry (YYMM) + case EMV_TAG_TRACK_1_EQUIV: { + char track_1_equiv[80]; + memcpy(track_1_equiv, &buff[i], tlen); + track_1_equiv[tlen] = '\0'; + success = true; + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_1_EQUIV %x : %s", tag, track_1_equiv); + break; + } + case EMV_TAG_TRACK_2_EQUIV: { + // 0xD0 delimits PAN from expiry (YYMM) for(int x = 1; x < tlen; x++) { if(buff[i + x + 1] > 0xD0) { memcpy(app->card_number, &buff[i], x + 1); app->card_number_len = x + 1; + app->exp_year = (buff[i + x + 1] << 4) | (buff[i + x + 2] >> 4); + app->exp_month = (buff[i + x + 2] << 4) | (buff[i + x + 3] >> 4); break; } } + + // Convert 4-bit to ASCII representation + char track_2_equiv[41]; + uint8_t track_2_equiv_len = 0; + for(int x = 0; x < tlen; x++) { + char top = (buff[i + x] >> 4) + '0'; + char bottom = (buff[i + x] & 0x0F) + '0'; + track_2_equiv[x * 2] = top; + track_2_equiv_len++; + if(top == '?') break; + track_2_equiv[x * 2 + 1] = bottom; + track_2_equiv_len++; + if(bottom == '?') break; + } + track_2_equiv[track_2_equiv_len] = '\0'; success = true; - FURI_LOG_T( - TAG, - "found EMV_TAG_CARD_NUM %x (len=%d)", - EMV_TAG_CARD_NUM, - app->card_number_len); + FURI_LOG_T(TAG, "found EMV_TAG_TRACK_2_EQUIV %x : %s", tag, track_2_equiv); break; + } case EMV_TAG_PAN: memcpy(app->card_number, &buff[i], tlen); app->card_number_len = tlen; diff --git a/lib/nfc/protocols/emv.h b/lib/nfc/protocols/emv.h index 0ccf7c3e0..c5b089fdf 100644 --- a/lib/nfc/protocols/emv.h +++ b/lib/nfc/protocols/emv.h @@ -11,7 +11,8 @@ #define EMV_TAG_CARD_NAME 0x50 #define EMV_TAG_FCI 0xBF0C #define EMV_TAG_LOG_CTRL 0x9F4D -#define EMV_TAG_CARD_NUM 0x57 +#define EMV_TAG_TRACK_1_EQUIV 0x56 +#define EMV_TAG_TRACK_2_EQUIV 0x57 #define EMV_TAG_PAN 0x5A #define EMV_TAG_AFL 0x94 #define EMV_TAG_EXP_DATE 0x5F24 From eee5c3540060abb4c7e48dbdbbbc045f9cc1b1ed Mon Sep 17 00:00:00 2001 From: Giacomo Ferretti Date: Fri, 27 Jan 2023 06:51:47 +0100 Subject: [PATCH 029/231] NFC: add MIFARE MINI support (#2307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * NFC: add MIFARE MINI support * Move new value to end of enum * nfc: added missing unit test Co-authored-by: gornekich Co-authored-by: あく --- applications/debug/unit_tests/nfc/nfc_test.c | 5 +++++ lib/nfc/helpers/nfc_generators.c | 22 ++++++++++++++++++++ lib/nfc/nfc_device.c | 18 ++++++++++++---- lib/nfc/nfc_types.c | 4 +++- lib/nfc/protocols/mifare_classic.c | 20 +++++++++++++----- lib/nfc/protocols/mifare_classic.h | 2 ++ 6 files changed, 61 insertions(+), 10 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index e9e7b35f6..d613be2b9 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -466,6 +466,10 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { nfc_device_free(nfc_keys); } +MU_TEST(mf_mini_file_test) { + mf_classic_generator_test(4, MfClassicTypeMini); +} + MU_TEST(mf_classic_1k_4b_file_test) { mf_classic_generator_test(4, MfClassicType1k); } @@ -486,6 +490,7 @@ MU_TEST_SUITE(nfc) { nfc_test_alloc(); MU_RUN_TEST(nfca_file_test); + MU_RUN_TEST(mf_mini_file_test); MU_RUN_TEST(mf_classic_1k_4b_file_test); MU_RUN_TEST(mf_classic_4k_4b_file_test); MU_RUN_TEST(mf_classic_1k_7b_file_test); diff --git a/lib/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c index 769b9c7b6..590ff4d50 100644 --- a/lib/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -352,11 +352,27 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType } // Set SAK to 08 data->nfc_data.sak = 0x08; + } else if(type == MfClassicTypeMini) { + // Set every block to 0xFF + for(uint16_t i = 1; i < MF_MINI_TOTAL_SECTORS_NUM * 4; i += 1) { + if(mf_classic_is_sector_trailer(i)) { + nfc_generate_mf_classic_sector_trailer(mfc, i); + } else { + memset(&mfc->block[i].value, 0xFF, 16); + } + mf_classic_set_block_read(mfc, i, &mfc->block[i]); + } + // Set SAK to 09 + data->nfc_data.sak = 0x09; } mfc->type = type; } +static void nfc_generate_mf_mini(NfcDeviceData* data) { + nfc_generate_mf_classic(data, 4, MfClassicTypeMini); +} + static void nfc_generate_mf_classic_1k_4b_uid(NfcDeviceData* data) { nfc_generate_mf_classic(data, 4, MfClassicType1k); } @@ -438,6 +454,11 @@ static const NfcGenerator ntag_i2c_plus_2k_generator = { .generator_func = nfc_generate_ntag_i2c_plus_2k, }; +static const NfcGenerator mifare_mini_generator = { + .name = "Mifare Mini", + .generator_func = nfc_generate_mf_mini, +}; + static const NfcGenerator mifare_classic_1k_4b_uid_generator = { .name = "Mifare Classic 1k 4byte UID", .generator_func = nfc_generate_mf_classic_1k_4b_uid, @@ -472,6 +493,7 @@ const NfcGenerator* const nfc_generators[] = { &ntag_i2c_2k_generator, &ntag_i2c_plus_1k_generator, &ntag_i2c_plus_2k_generator, + &mifare_mini_generator, &mifare_classic_1k_4b_uid_generator, &mifare_classic_1k_7b_uid_generator, &mifare_classic_4k_4b_uid_generator, diff --git a/lib/nfc/nfc_device.c b/lib/nfc/nfc_device.c index d10eaa0e5..517913070 100644 --- a/lib/nfc/nfc_device.c +++ b/lib/nfc/nfc_device.c @@ -745,7 +745,10 @@ static bool nfc_device_save_mifare_classic_data(FlipperFormat* file, NfcDevice* do { if(!flipper_format_write_comment_cstr(file, "Mifare Classic specific data")) break; - if(data->type == MfClassicType1k) { + if(data->type == MfClassicTypeMini) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; + blocks = 20; + } else if(data->type == MfClassicType1k) { if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; blocks = 64; } else if(data->type == MfClassicType4k) { @@ -843,7 +846,10 @@ static bool nfc_device_load_mifare_classic_data(FlipperFormat* file, NfcDevice* do { // Read Mifare Classic type if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!furi_string_cmp(temp_str, "1K")) { + if(!furi_string_cmp(temp_str, "MINI")) { + data->type = MfClassicTypeMini; + data_blocks = 20; + } else if(!furi_string_cmp(temp_str, "1K")) { data->type = MfClassicType1k; data_blocks = 64; } else if(!furi_string_cmp(temp_str, "4K")) { @@ -918,7 +924,9 @@ static bool nfc_device_save_mifare_classic_keys(NfcDevice* dev) { if(!flipper_format_file_open_always(file, furi_string_get_cstr(temp_str))) break; if(!flipper_format_write_header_cstr(file, nfc_keys_file_header, nfc_keys_file_version)) break; - if(data->type == MfClassicType1k) { + if(data->type == MfClassicTypeMini) { + if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "MINI")) break; + } else if(data->type == MfClassicType1k) { if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "1K")) break; } else if(data->type == MfClassicType4k) { if(!flipper_format_write_string_cstr(file, "Mifare Classic type", "4K")) break; @@ -968,7 +976,9 @@ bool nfc_device_load_key_cache(NfcDevice* dev) { if(furi_string_cmp_str(temp_str, nfc_keys_file_header)) break; if(version != nfc_keys_file_version) break; if(!flipper_format_read_string(file, "Mifare Classic type", temp_str)) break; - if(!furi_string_cmp(temp_str, "1K")) { + if(!furi_string_cmp(temp_str, "MINI")) { + data->type = MfClassicTypeMini; + } else if(!furi_string_cmp(temp_str, "1K")) { data->type = MfClassicType1k; } else if(!furi_string_cmp(temp_str, "4K")) { data->type = MfClassicType4k; diff --git a/lib/nfc/nfc_types.c b/lib/nfc/nfc_types.c index 427628769..02ca85580 100644 --- a/lib/nfc/nfc_types.c +++ b/lib/nfc/nfc_types.c @@ -55,7 +55,9 @@ const char* nfc_mf_ul_type(MfUltralightType type, bool full_name) { } const char* nfc_mf_classic_type(MfClassicType type) { - if(type == MfClassicType1k) { + if(type == MfClassicTypeMini) { + return "Mifare Mini 0.3K"; + } else if(type == MfClassicType1k) { return "Mifare Classic 1K"; } else if(type == MfClassicType4k) { return "Mifare Classic 4K"; diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index d1aadf06f..2f340a5ce 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -13,7 +13,9 @@ #define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) const char* mf_classic_get_type_str(MfClassicType type) { - if(type == MfClassicType1k) { + if(type == MfClassicTypeMini) { + return "MIFARE Mini 0.3K"; + } else if(type == MfClassicType1k) { return "MIFARE Classic 1K"; } else if(type == MfClassicType4k) { return "MIFARE Classic 4K"; @@ -73,7 +75,9 @@ MfClassicSectorTrailer* } uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { - if(type == MfClassicType1k) { + if(type == MfClassicTypeMini) { + return MF_MINI_TOTAL_SECTORS_NUM; + } else if(type == MfClassicType1k) { return MF_CLASSIC_1K_TOTAL_SECTORS_NUM; } else if(type == MfClassicType4k) { return MF_CLASSIC_4K_TOTAL_SECTORS_NUM; @@ -83,7 +87,9 @@ uint8_t mf_classic_get_total_sectors_num(MfClassicType type) { } uint16_t mf_classic_get_total_block_num(MfClassicType type) { - if(type == MfClassicType1k) { + if(type == MfClassicTypeMini) { + return 20; + } else if(type == MfClassicType1k) { return 64; } else if(type == MfClassicType4k) { return 256; @@ -363,8 +369,12 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); - if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { - return MfClassicType1k; + if((ATQA0 == 0x44 || ATQA0 == 0x04)) { + if((SAK == 0x08 || SAK == 0x88)) { + return MfClassicType1k; + } else if(SAK == 0x09) { + return MfClassicTypeMini; + } } else if((ATQA0 == 0x01) && (ATQA1 == 0x0F) && (SAK == 0x01)) { //skylanders support return MfClassicType1k; diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 74beceb62..0346da0f7 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -6,6 +6,7 @@ #define MF_CLASSIC_BLOCK_SIZE (16) #define MF_CLASSIC_TOTAL_BLOCKS_MAX (256) +#define MF_MINI_TOTAL_SECTORS_NUM (5) #define MF_CLASSIC_1K_TOTAL_SECTORS_NUM (16) #define MF_CLASSIC_4K_TOTAL_SECTORS_NUM (40) @@ -20,6 +21,7 @@ typedef enum { MfClassicType1k, MfClassicType4k, + MfClassicTypeMini, } MfClassicType; typedef enum { From 126a9efd091ea949cb9a26a01b0ee7f8c22a4a68 Mon Sep 17 00:00:00 2001 From: Giacomo Ferretti Date: Fri, 27 Jan 2023 09:21:52 +0100 Subject: [PATCH 030/231] NFC: change from int8_t to uint8_t (#2302) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: gornekich Co-authored-by: あく --- firmware/targets/f7/api_symbols.csv | 2 +- lib/nfc/protocols/mifare_classic.c | 2 +- lib/nfc/protocols/mifare_classic.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3d7bd7cd2..0e4af3c1d 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1880,7 +1880,7 @@ Function,-,mf_classic_dict_is_key_present,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_is_key_present_str,_Bool,"MfClassicDict*, FuriString*" Function,-,mf_classic_dict_rewind,_Bool,MfClassicDict* Function,-,mf_classic_emulator,_Bool,"MfClassicEmulator*, FuriHalNfcTxRxContext*" -Function,-,mf_classic_get_classic_type,MfClassicType,"int8_t, uint8_t, uint8_t" +Function,-,mf_classic_get_classic_type,MfClassicType,"uint8_t, uint8_t, uint8_t" Function,-,mf_classic_get_read_sectors_and_keys,void,"MfClassicData*, uint8_t*, uint8_t*" Function,-,mf_classic_get_sector_by_block,uint8_t,uint8_t Function,-,mf_classic_get_sector_trailer_block_num_by_sector,uint8_t,uint8_t diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 2f340a5ce..5887ab4c1 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -367,7 +367,7 @@ bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { } } -MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { +MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04)) { if((SAK == 0x08 || SAK == 0x88)) { diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 0346da0f7..321ab5f03 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -96,7 +96,7 @@ const char* mf_classic_get_type_str(MfClassicType type); bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); -MfClassicType mf_classic_get_classic_type(int8_t ATQA0, uint8_t ATQA1, uint8_t SAK); +MfClassicType mf_classic_get_classic_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK); uint8_t mf_classic_get_total_sectors_num(MfClassicType type); From 24a23e5dc7b1719874f075abbf428bd5dd59e5e6 Mon Sep 17 00:00:00 2001 From: Emily Trau Date: Fri, 27 Jan 2023 20:41:55 +1100 Subject: [PATCH 031/231] debug apps: made runnable as .faps; sdk: resolved additional APIs in use by faps (#2333) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * sdk: resolve additional APIs in use by faps * debug tools: fixed battery_test, bt_debug, display_test, rpc_debug Co-authored-by: hedger Co-authored-by: hedger Co-authored-by: あく --- .../debug/battery_test_app/application.fam | 1 + .../battery_test_app/views/battery_info.c | 148 ++++++++++++++++++ .../battery_test_app/views/battery_info.h | 23 +++ .../debug/bt_debug_app/bt_debug_app.c | 7 +- .../debug/bt_debug_app/bt_debug_app.h | 5 +- .../debug/display_test/application.fam | 1 + .../debug/display_test/display_test.c | 1 - .../rpc_debug_app_scene_input_error_code.c | 6 +- applications/services/bt/bt_settings.h | 8 + firmware/targets/f7/api_symbols.csv | 8 +- lib/toolbox/SConscript | 1 + 11 files changed, 198 insertions(+), 11 deletions(-) create mode 100644 applications/debug/battery_test_app/views/battery_info.c create mode 100644 applications/debug/battery_test_app/views/battery_info.h diff --git a/applications/debug/battery_test_app/application.fam b/applications/debug/battery_test_app/application.fam index b388445cc..f97d10279 100644 --- a/applications/debug/battery_test_app/application.fam +++ b/applications/debug/battery_test_app/application.fam @@ -11,4 +11,5 @@ App( stack_size=1 * 1024, order=130, fap_category="Debug", + fap_libs=["assets"], ) diff --git a/applications/debug/battery_test_app/views/battery_info.c b/applications/debug/battery_test_app/views/battery_info.c new file mode 100644 index 000000000..5353a2e2a --- /dev/null +++ b/applications/debug/battery_test_app/views/battery_info.c @@ -0,0 +1,148 @@ +#include "battery_info.h" +#include +#include +#include + +#define LOW_CHARGE_THRESHOLD 10 +#define HIGH_DRAIN_CURRENT_THRESHOLD 100 + +struct BatteryInfo { + View* view; +}; + +static void draw_stat(Canvas* canvas, int x, int y, const Icon* icon, char* val) { + canvas_draw_frame(canvas, x - 7, y + 7, 30, 13); + canvas_draw_icon(canvas, x, y, icon); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box(canvas, x - 4, y + 16, 24, 6); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x + 8, y + 22, AlignCenter, AlignBottom, val); +}; + +static void draw_battery(Canvas* canvas, BatteryInfoModel* data, int x, int y) { + char emote[20] = {}; + char header[20] = {}; + char value[20] = {}; + + int32_t drain_current = data->gauge_current * (-1000); + uint32_t charge_current = data->gauge_current * 1000; + + // Draw battery + canvas_draw_icon(canvas, x, y, &I_BatteryBody_52x28); + if(charge_current > 0) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceCharging_29x14); + } else if(drain_current > HIGH_DRAIN_CURRENT_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceConfused_29x14); + } else if(data->charge < LOW_CHARGE_THRESHOLD) { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNopower_29x14); + } else { + canvas_draw_icon(canvas, x + 16, y + 7, &I_FaceNormal_29x14); + } + + // Draw bubble + elements_bubble(canvas, 53, 0, 71, 39); + + // Set text + if(charge_current > 0) { + snprintf(emote, sizeof(emote), "%s", "Yummy!"); + snprintf(header, sizeof(header), "%s", "Charging at"); + snprintf( + value, + sizeof(value), + "%lu.%luV %lumA", + (uint32_t)(data->vbus_voltage), + (uint32_t)(data->vbus_voltage * 10) % 10, + charge_current); + } else if(drain_current > 0) { + snprintf( + emote, + sizeof(emote), + "%s", + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "Oh no!" : "Om-nom-nom!"); + snprintf(header, sizeof(header), "%s", "Consumption is"); + snprintf( + value, + sizeof(value), + "%ld %s", + drain_current, + drain_current > HIGH_DRAIN_CURRENT_THRESHOLD ? "mA!" : "mA"); + } else if(drain_current != 0) { + snprintf(header, 20, "..."); + } else if(data->charging_voltage < 4.2) { + // Non-default battery charging limit, mention it + snprintf(emote, sizeof(emote), "Charged!"); + snprintf(header, sizeof(header), "Limited to"); + snprintf( + value, + sizeof(value), + "%lu.%luV", + (uint32_t)(data->charging_voltage), + (uint32_t)(data->charging_voltage * 10) % 10); + } else { + snprintf(header, sizeof(header), "Charged!"); + } + + canvas_draw_str_aligned(canvas, 92, y + 3, AlignCenter, AlignCenter, emote); + canvas_draw_str_aligned(canvas, 92, y + 15, AlignCenter, AlignCenter, header); + canvas_draw_str_aligned(canvas, 92, y + 27, AlignCenter, AlignCenter, value); +}; + +static void battery_info_draw_callback(Canvas* canvas, void* context) { + furi_assert(context); + BatteryInfoModel* model = context; + + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + draw_battery(canvas, model, 0, 5); + + char batt_level[10]; + char temperature[10]; + char voltage[10]; + char health[10]; + + snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + snprintf( + voltage, + sizeof(voltage), + "%lu.%01lu V", + (uint32_t)model->gauge_voltage, + (uint32_t)(model->gauge_voltage * 10) % 10UL); + snprintf(health, sizeof(health), "%d%%", model->health); + + draw_stat(canvas, 8, 42, &I_Battery_16x16, batt_level); + draw_stat(canvas, 40, 42, &I_Temperature_16x16, temperature); + draw_stat(canvas, 72, 42, &I_Voltage_16x16, voltage); + draw_stat(canvas, 104, 42, &I_Health_16x16, health); +} + +BatteryInfo* battery_info_alloc() { + BatteryInfo* battery_info = malloc(sizeof(BatteryInfo)); + battery_info->view = view_alloc(); + view_set_context(battery_info->view, battery_info); + view_allocate_model(battery_info->view, ViewModelTypeLocking, sizeof(BatteryInfoModel)); + view_set_draw_callback(battery_info->view, battery_info_draw_callback); + + return battery_info; +} + +void battery_info_free(BatteryInfo* battery_info) { + furi_assert(battery_info); + view_free(battery_info->view); + free(battery_info); +} + +View* battery_info_get_view(BatteryInfo* battery_info) { + furi_assert(battery_info); + return battery_info->view; +} + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data) { + furi_assert(battery_info); + furi_assert(data); + with_view_model( + battery_info->view, + BatteryInfoModel * model, + { memcpy(model, data, sizeof(BatteryInfoModel)); }, + true); +} diff --git a/applications/debug/battery_test_app/views/battery_info.h b/applications/debug/battery_test_app/views/battery_info.h new file mode 100644 index 000000000..7bfacf69e --- /dev/null +++ b/applications/debug/battery_test_app/views/battery_info.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +typedef struct BatteryInfo BatteryInfo; + +typedef struct { + float vbus_voltage; + float gauge_voltage; + float gauge_current; + float gauge_temperature; + float charging_voltage; + uint8_t charge; + uint8_t health; +} BatteryInfoModel; + +BatteryInfo* battery_info_alloc(); + +void battery_info_free(BatteryInfo* battery_info); + +View* battery_info_get_view(BatteryInfo* battery_info); + +void battery_info_set_data(BatteryInfo* battery_info, BatteryInfoModel* data); diff --git a/applications/debug/bt_debug_app/bt_debug_app.c b/applications/debug/bt_debug_app/bt_debug_app.c index 405051a4a..bf13f6570 100644 --- a/applications/debug/bt_debug_app/bt_debug_app.c +++ b/applications/debug/bt_debug_app/bt_debug_app.c @@ -31,9 +31,6 @@ uint32_t bt_debug_start_view(void* context) { BtDebugApp* bt_debug_app_alloc() { BtDebugApp* app = malloc(sizeof(BtDebugApp)); - // Load settings - bt_settings_load(&app->settings); - // Gui app->gui = furi_record_open(RECORD_GUI); @@ -105,13 +102,15 @@ int32_t bt_debug_app(void* p) { } BtDebugApp* app = bt_debug_app_alloc(); + // Was bt active? + const bool was_active = furi_hal_bt_is_active(); // Stop advertising furi_hal_bt_stop_advertising(); view_dispatcher_run(app->view_dispatcher); // Restart advertising - if(app->settings.enabled) { + if(was_active) { furi_hal_bt_start_advertising(); } bt_debug_app_free(app); diff --git a/applications/debug/bt_debug_app/bt_debug_app.h b/applications/debug/bt_debug_app/bt_debug_app.h index cd59e4d00..0ad94d7dd 100644 --- a/applications/debug/bt_debug_app/bt_debug_app.h +++ b/applications/debug/bt_debug_app/bt_debug_app.h @@ -4,15 +4,14 @@ #include #include #include +#include + #include -#include #include "views/bt_carrier_test.h" #include "views/bt_packet_test.h" -#include typedef struct { - BtSettings settings; Gui* gui; ViewDispatcher* view_dispatcher; Submenu* submenu; diff --git a/applications/debug/display_test/application.fam b/applications/debug/display_test/application.fam index 4b40322fb..e8a00d2ae 100644 --- a/applications/debug/display_test/application.fam +++ b/applications/debug/display_test/application.fam @@ -5,6 +5,7 @@ App( entry_point="display_test_app", cdefines=["APP_DISPLAY_TEST"], requires=["gui"], + fap_libs=["misc"], stack_size=1 * 1024, order=120, fap_category="Debug", diff --git a/applications/debug/display_test/display_test.c b/applications/debug/display_test/display_test.c index 5b46d2b41..8065a23a1 100644 --- a/applications/debug/display_test/display_test.c +++ b/applications/debug/display_test/display_test.c @@ -91,7 +91,6 @@ static void display_test_reload_config(DisplayTest* instance) { instance->config_contrast, instance->config_regulation_ratio, instance->config_bias); - gui_update(instance->gui); } static void display_config_set_bias(VariableItem* item) { diff --git a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c index eae12e6ee..367ca7a4f 100644 --- a/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c +++ b/applications/debug/rpc_debug_app/scenes/rpc_debug_app_scene_input_error_code.c @@ -44,7 +44,11 @@ bool rpc_debug_app_scene_input_error_code_on_event(void* context, SceneManagerEv if(event.type == SceneManagerEventTypeCustom) { if(event.event == RpcDebugAppCustomEventInputErrorCode) { - rpc_system_app_set_error_code(app->rpc, (uint32_t)atol(app->text_store)); + char* end; + int error_code = strtol(app->text_store, &end, 10); + if(!*end) { + rpc_system_app_set_error_code(app->rpc, error_code); + } scene_manager_previous_scene(app->scene_manager); consumed = true; } diff --git a/applications/services/bt/bt_settings.h b/applications/services/bt/bt_settings.h index 260d9c0e0..9ed8be89c 100644 --- a/applications/services/bt/bt_settings.h +++ b/applications/services/bt/bt_settings.h @@ -5,6 +5,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + typedef struct { bool enabled; } BtSettings; @@ -12,3 +16,7 @@ typedef struct { bool bt_settings_load(BtSettings* bt_settings); bool bt_settings_save(BtSettings* bt_settings); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0e4af3c1d..0e027a6a2 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.7,, +Version,+,11.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -188,6 +188,7 @@ Header,+,lib/toolbox/stream/file_stream.h,, Header,+,lib/toolbox/stream/stream.h,, Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, +Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" @@ -2066,7 +2067,7 @@ Function,-,posix_memalign,int,"void**, size_t, size_t" Function,-,pow,double,"double, double" Function,-,pow10,double,double Function,-,pow10f,float,float -Function,-,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" Function,+,power_get_info,void,"Power*, PowerInfo*" Function,+,power_get_pubsub,FuriPubSub*,Power* Function,+,power_is_battery_healthy,_Bool,Power* @@ -2804,6 +2805,9 @@ Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" diff --git a/lib/toolbox/SConscript b/lib/toolbox/SConscript index 724d25afa..8ce45d25f 100644 --- a/lib/toolbox/SConscript +++ b/lib/toolbox/SConscript @@ -20,6 +20,7 @@ env.Append( File("saved_struct.h"), File("version.h"), File("float_tools.h"), + File("value_index.h"), File("tar/tar_archive.h"), File("stream/stream.h"), File("stream/file_stream.h"), From a6daa2a20d7c3b96d27b965714a4086fae9753ab Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 27 Jan 2023 11:56:29 +0100 Subject: [PATCH 032/231] drop useless file and some useless line --- applications/main/bad_ble/bad_ble_app.c | 7 +- applications/main/bad_ble/bad_ble_app.h | 2 +- applications/main/bad_ble/bad_ble_app_i.h | 9 +- applications/main/bad_ble/bad_ble_script.c | 54 +- applications/main/bad_ble/bad_ble_script.h | 2 +- .../bad_ble/scenes/bad_ble_scene_config.c | 4 +- .../scenes/bad_ble_scene_config_name.c | 3 +- .../main/gpio/scenes/gpio_scene_start.c | 3 +- applications/plugins/dap_link/dap_link.c | 3 +- applications/plugins/dice/dice.c | 1 - .../plugins/orgasmotron/orgasmotron.c | 37 +- applications/plugins/protoview/app.c | 174 +- applications/plugins/protoview/app.h | 266 +- applications/plugins/protoview/app_subghz.c | 68 +- applications/plugins/protoview/crc.c | 9 +- .../plugins/protoview/custom_presets.h | 31 +- applications/plugins/protoview/data_feed.c | 17 +- applications/plugins/protoview/fields.c | 348 +- .../plugins/protoview/protocols/b4b1.c | 67 +- .../plugins/protoview/protocols/keeloq.c | 99 +- .../plugins/protoview/protocols/oregon2.c | 90 +- .../protoview/protocols/tpms/citroen.c | 46 +- .../plugins/protoview/protocols/tpms/ford.c | 49 +- .../protoview/protocols/tpms/renault.c | 104 +- .../protoview/protocols/tpms/schrader.c | 52 +- .../protocols/tpms/schrader_eg53ma4.c | 47 +- .../plugins/protoview/protocols/tpms/toyota.c | 57 +- applications/plugins/protoview/raw_samples.c | 47 +- applications/plugins/protoview/raw_samples.h | 27 +- applications/plugins/protoview/signal.c | 348 +- applications/plugins/protoview/signal_file.c | 96 +- applications/plugins/protoview/ui.c | 99 +- applications/plugins/protoview/view_build.c | 211 +- .../plugins/protoview/view_direct_sampling.c | 45 +- applications/plugins/protoview/view_info.c | 211 +- .../plugins/protoview/view_raw_signal.c | 76 +- .../plugins/protoview/view_settings.c | 74 +- applications/plugins/tama_p1/hal.c | 2 +- applications/plugins/tama_p1/tama.h | 1 - applications/plugins/tama_p1/tama_p1.c | 145 +- applications/services/bt/bt_service/bt.c | 27 +- applications/services/bt/bt_service/bt.h | 13 +- .../desktop/animations/animation_manager.c | 35 +- .../desktop/animations/animation_storage.c | 9 +- .../desktop/views/desktop_view_lock_menu.c | 2 +- .../services/dolphin/helpers/dolphin_state.c | 7 +- .../services/power/power_service/power.c | 9 +- applications/settings/about/about.c | 19 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 76 +- .../settings/xtreme_settings/xtreme_assets.c | 207 +- .../settings/xtreme_settings/xtreme_assets.h | 7 +- .../xtreme_settings/xtreme_settings.c | 18 +- .../xtreme_settings/xtreme_settings_app.c | 20 +- firmware/targets/f7/ble_glue/gap.c | 28 +- firmware/targets/f7/ble_glue/gap.h | 2 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 14 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 3 +- .../targets/furi_hal_include/furi_hal_bt.h | 14 +- flipper.log | 3146 ----------------- 60 files changed, 1822 insertions(+), 4867 deletions(-) delete mode 100644 flipper.log diff --git a/applications/main/bad_ble/bad_ble_app.c b/applications/main/bad_ble/bad_ble_app.c index ef1a400d6..c5a7e72d0 100644 --- a/applications/main/bad_ble/bad_ble_app.c +++ b/applications/main/bad_ble/bad_ble_app.c @@ -95,14 +95,13 @@ BadBleApp* bad_ble_app_alloc(char* arg) { view_dispatcher_set_navigation_event_callback( app->view_dispatcher, bad_ble_app_back_event_callback); - Bt* bt = furi_record_open(RECORD_BT); app->bt = bt; - const char *adv_name = bt_get_profile_adv_name(bt); + const char* adv_name = bt_get_profile_adv_name(bt); memcpy(app->name, adv_name, BAD_BLE_ADV_NAME_MAX_LEN); - const uint8_t *mac_addr = bt_get_profile_mac_address(bt); - memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN); + const uint8_t* mac_addr = bt_get_profile_mac_address(bt); + memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN); // Custom Widget app->widget = widget_alloc(); diff --git a/applications/main/bad_ble/bad_ble_app.h b/applications/main/bad_ble/bad_ble_app.h index 35da3ad2c..ff681e849 100644 --- a/applications/main/bad_ble/bad_ble_app.h +++ b/applications/main/bad_ble/bad_ble_app.h @@ -6,7 +6,7 @@ extern "C" { typedef struct BadBleApp BadBleApp; -void bad_ble_set_name(BadBleApp *app, const char* fmt, ...); +void bad_ble_set_name(BadBleApp* app, const char* fmt, ...); #ifdef __cplusplus } diff --git a/applications/main/bad_ble/bad_ble_app_i.h b/applications/main/bad_ble/bad_ble_app_i.h index 940706c85..fd2405c51 100644 --- a/applications/main/bad_ble/bad_ble_app_i.h +++ b/applications/main/bad_ble/bad_ble_app_i.h @@ -23,8 +23,8 @@ #define BAD_BLE_APP_SCRIPT_EXTENSION ".txt" #define BAD_BLE_APP_LAYOUT_EXTENSION ".kl" -#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro -#define BAD_BLE_ADV_NAME_MAX_LEN 18 +#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro +#define BAD_BLE_ADV_NAME_MAX_LEN 18 typedef enum { BadBleAppErrorNoFiles, @@ -40,12 +40,11 @@ struct BadBleApp { Widget* widget; Submenu* submenu; - TextInput *text_input; - ByteInput *byte_input; + TextInput* text_input; + ByteInput* byte_input; uint8_t mac[BAD_BLE_MAC_ADDRESS_LEN]; char name[BAD_BLE_ADV_NAME_MAX_LEN + 1]; - BadBleAppError error; FuriString* file_path; FuriString* keyboard_layout; diff --git a/applications/main/bad_ble/bad_ble_script.c b/applications/main/bad_ble/bad_ble_script.c index 73f4dd6c6..7a14c4498 100644 --- a/applications/main/bad_ble/bad_ble_script.c +++ b/applications/main/bad_ble/bad_ble_script.c @@ -30,7 +30,6 @@ typedef enum { WorkerEvtDisconnect = (1 << 3), } WorkerEvtFlags; - typedef enum { LevelRssi122_100, LevelRssi99_80, @@ -41,12 +40,15 @@ typedef enum { LevelRssiError = 0xFF, } LevelRssiRange; +/** + * Delays for waiting between HID key press and key release +*/ const uint8_t bt_hid_delays[LevelRssiNum] = { - 30, // LevelRssi122_100 - 25, // LevelRssi99_80 - 20, // LevelRssi79_60 - 17, // LevelRssi59_40 - 14, // LevelRssi39_0 + 30, // LevelRssi122_100 + 25, // LevelRssi99_80 + 20, // LevelRssi79_60 + 17, // LevelRssi59_40 + 14, // LevelRssi39_0 }; struct BadBleScript { @@ -161,31 +163,28 @@ static const uint8_t numpad_keys[10] = { uint8_t bt_timeout = 0; -static LevelRssiRange bt_remote_rssi_range(Bt *bt) { +static LevelRssiRange bt_remote_rssi_range(Bt* bt) { + BtRssi rssi_data = {0}; - BtRssi rssi_data = { 0 }; + if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; - if (!bt_remote_rssi(bt, &rssi_data)) - return LevelRssiError; - - if (rssi_data.rssi <= 39) + if(rssi_data.rssi <= 39) return LevelRssi39_0; - else if (rssi_data.rssi <= 59) + else if(rssi_data.rssi <= 59) return LevelRssi59_40; - else if (rssi_data.rssi <= 79) + else if(rssi_data.rssi <= 79) return LevelRssi79_60; - else if (rssi_data.rssi <= 99) + else if(rssi_data.rssi <= 99) return LevelRssi99_80; - else if (rssi_data.rssi <= 122) + else if(rssi_data.rssi <= 122) return LevelRssi122_100; - + return LevelRssiError; } -static inline void update_bt_timeout(Bt *bt) { - +static inline void update_bt_timeout(Bt* bt) { LevelRssiRange r = bt_remote_rssi_range(bt); - if (r < LevelRssiNum) { + if(r < LevelRssiNum) { bt_timeout = bt_hid_delays[r]; } } @@ -297,7 +296,7 @@ static bool ducky_string(BadBleScript* bad_ble, const char* param) { if(keycode != HID_KEYBOARD_NONE) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(keycode); - + furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(keycode); } @@ -330,8 +329,6 @@ static int32_t return SCRIPT_STATE_NEXT_LINE; // Skip empty lines } - FURI_LOG_D(WORKER_TAG, "line:%s", line_tmp); - // General commands if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { // REM - comment line @@ -426,7 +423,6 @@ static int32_t line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; key |= ducky_get_keycode(bad_ble, line_tmp, true); } - FURI_LOG_I(WORKER_TAG, "Special key pressed %x\r\n", key); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); @@ -545,7 +541,7 @@ static void bad_ble_hid_state_callback(BtStatus status, void* context) { if(state == true) { LevelRssiRange r = bt_remote_rssi_range(bad_ble->bt); - if (r != LevelRssiError) { + if(r != LevelRssiError) { bt_timeout = bt_hid_delays[r]; } furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect); @@ -559,9 +555,9 @@ static int32_t bad_ble_worker(void* context) { BadBleWorkerState worker_state = BadBleStateInit; int32_t delay_val = 0; + // BLE HID init bt_timeout = bt_hid_delays[LevelRssi39_0]; - // init ble hid bt_disconnect(bad_ble->bt); // Wait 2nd core to update nvm storage @@ -569,10 +565,6 @@ static int32_t bad_ble_worker(void* context) { bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH); - bt_set_profile_adv_name(bad_ble->bt, "Keyboard K99"); - - //furi_hal_bt_set_profile_adv_name("Keyboard K99", FuriHalBtProfileHidKeyboard); - if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); return -1; @@ -727,7 +719,7 @@ static int32_t bad_ble_worker(void* context) { } update_bt_timeout(bad_ble->bt); - FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } // release all keys diff --git a/applications/main/bad_ble/bad_ble_script.h b/applications/main/bad_ble/bad_ble_script.h index f1553e1db..5445c45ed 100644 --- a/applications/main/bad_ble/bad_ble_script.h +++ b/applications/main/bad_ble/bad_ble_script.h @@ -30,7 +30,7 @@ typedef struct { char error[64]; } BadBleState; -BadBleScript* bad_ble_script_open(FuriString* file_path, Bt *bt); +BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt); void bad_ble_script_close(BadBleScript* bad_ble); diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config.c b/applications/main/bad_ble/scenes/bad_ble_scene_config.c index b601a80af..0987b153b 100644 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config.c +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config.c @@ -52,9 +52,9 @@ bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) { consumed = true; if(event.event == SubmenuIndexKeyboardLayout) { scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout); - } else if (event.event == SubmenuIndexAdvertisementName) { + } else if(event.event == SubmenuIndexAdvertisementName) { scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigName); - } else if (event.event == SubmenuIndexMacAddress) { + } else if(event.event == SubmenuIndexMacAddress) { scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigMac); } else { furi_crash("Unknown key type"); diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c index 9fdeebe1b..ae9d16ba4 100644 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c +++ b/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c @@ -33,8 +33,7 @@ bool bad_ble_scene_config_name_on_event(void* context, SceneManagerEvent event) if(event.event == BadBleAppCustomEventTextEditResult) { bt_set_profile_adv_name(bad_ble->bt, bad_ble->name); } - scene_manager_previous_scene( - bad_ble->scene_manager); + scene_manager_previous_scene(bad_ble->scene_manager); } return consumed; } diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index c06d7b5c6..c43858a0a 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -38,8 +38,7 @@ static void gpio_scene_start_var_list_enter_callback(void* context, uint32_t ind } } -static void -gpio_scene_start_var_list_change_callback(VariableItem* item) { +static void gpio_scene_start_var_list_change_callback(VariableItem* item) { GpioApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); diff --git a/applications/plugins/dap_link/dap_link.c b/applications/plugins/dap_link/dap_link.c index c46c68788..dd684810a 100644 --- a/applications/plugins/dap_link/dap_link.c +++ b/applications/plugins/dap_link/dap_link.c @@ -486,8 +486,7 @@ int32_t dap_link_app(void* p) { if(furi_hal_usb_is_locked()) { DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); DialogMessage* message = dialog_message_alloc(); - dialog_message_set_header( - message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); + dialog_message_set_header(message, "Connection\nis active!", 3, 2, AlignLeft, AlignTop); dialog_message_set_text( message, "Disconnect from\nPC or phone to\nuse this function.", diff --git a/applications/plugins/dice/dice.c b/applications/plugins/dice/dice.c index 61aa7b4f5..dc748b68f 100644 --- a/applications/plugins/dice/dice.c +++ b/applications/plugins/dice/dice.c @@ -467,7 +467,6 @@ int32_t dice_app(void* p) { return 255; } - ViewPort* view_port = view_port_alloc(); view_port_draw_callback_set(view_port, dice_render_callback, plugin_state); view_port_input_callback_set(view_port, dice_input_callback, plugin_state->event_queue); diff --git a/applications/plugins/orgasmotron/orgasmotron.c b/applications/plugins/orgasmotron/orgasmotron.c index b28f392f5..684fc3d95 100644 --- a/applications/plugins/orgasmotron/orgasmotron.c +++ b/applications/plugins/orgasmotron/orgasmotron.c @@ -40,7 +40,7 @@ int32_t orgasmotron_app(void* p) { PluginState* plugin_state = malloc(sizeof(PluginState)); ValueMutex state_mutex; - if (!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { + if(!init_mutex(&state_mutex, plugin_state, sizeof(PluginState))) { FURI_LOG_E("Orgasmatron", "cannot create mutex\r\n"); free(plugin_state); return 255; @@ -61,10 +61,10 @@ int32_t orgasmotron_app(void* p) { //int mode = 0; bool processing = true; //while(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk) { - while (processing) { + while(processing) { FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100); PluginState* plugin_state = (PluginState*)acquire_mutex_block(&state_mutex); - if (event_status == FuriStatusOk) { + if(event_status == FuriStatusOk) { if(event.key == InputKeyBack && event.type == InputTypeShort) { //Exit Application notification_message(notification, &sequence_reset_vibro); @@ -73,53 +73,58 @@ int32_t orgasmotron_app(void* p) { processing = false; //break; } - if(event.key == InputKeyOk && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyOk && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 0; } - if(event.key == InputKeyLeft && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyLeft && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 1; } - if(event.key == InputKeyRight && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyRight && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 3; } - if(event.key == InputKeyUp && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyUp && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 2; } - if(event.key == InputKeyDown && (event.type == InputTypePress || event.type == InputTypeRelease)) { + if(event.key == InputKeyDown && + (event.type == InputTypePress || event.type == InputTypeRelease)) { plugin_state->mode = 4; } } - - if (plugin_state->mode == 0) { + + if(plugin_state->mode == 0) { //Stop Vibration notification_message(notification, &sequence_reset_vibro); notification_message(notification, &sequence_reset_green); - } else if (plugin_state->mode == 1) { + } else if(plugin_state->mode == 1) { //Full power notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); - } else if (plugin_state->mode == 2) { + } else if(plugin_state->mode == 2) { //Pulsed Vibration notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(100); notification_message(notification, &sequence_reset_vibro); - } else if (plugin_state->mode == 3) { + } else if(plugin_state->mode == 3) { //Soft power notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(50); notification_message(notification, &sequence_reset_vibro); - } else if (plugin_state->mode == 4) { + } else if(plugin_state->mode == 4) { //Special Sequence - for (int i = 0;i < 15;i++) { + for(int i = 0; i < 15; i++) { notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(50); notification_message(notification, &sequence_reset_vibro); delay(50); } - for (int i = 0;i < 2;i++) { + for(int i = 0; i < 2; i++) { notification_message(notification, &sequence_set_vibro_on); notification_message(notification, &sequence_set_green_255); delay(400); diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index f16457e55..d060e2242 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -40,8 +40,8 @@ extern const SubGhzProtocolRegistry protoview_protocol_registry; /* The callback actually just passes the control to the actual active * view callback, after setting up basic stuff like cleaning the screen * and setting color to black. */ -static void render_callback(Canvas *const canvas, void *ctx) { - ProtoViewApp *app = ctx; +static void render_callback(Canvas* const canvas, void* ctx) { + ProtoViewApp* app = ctx; /* Clear screen. */ canvas_set_color(canvas, ColorWhite); @@ -51,14 +51,25 @@ static void render_callback(Canvas *const canvas, void *ctx) { /* Call who is in charge right now. */ switch(app->current_view) { - case ViewRawPulses: render_view_raw_pulses(canvas,app); break; - case ViewInfo: render_view_info(canvas,app); break; + case ViewRawPulses: + render_view_raw_pulses(canvas, app); + break; + case ViewInfo: + render_view_info(canvas, app); + break; case ViewFrequencySettings: case ViewModulationSettings: - render_view_settings(canvas,app); break; - case ViewDirectSampling: render_view_direct_sampling(canvas,app); break; - case ViewBuildMessage: render_view_build_message(canvas,app); break; - default: furi_crash(TAG "Invalid view selected"); break; + render_view_settings(canvas, app); + break; + case ViewDirectSampling: + render_view_direct_sampling(canvas, app); + break; + case ViewBuildMessage: + render_view_build_message(canvas, app); + break; + default: + furi_crash(TAG "Invalid view selected"); + break; } /* Draw the alert box if set. */ @@ -67,10 +78,9 @@ static void render_callback(Canvas *const canvas, void *ctx) { /* Here all we do is putting the events into the queue that will be handled * in the while() loop of the app entry point function. */ -static void input_callback(InputEvent* input_event, void* ctx) -{ - ProtoViewApp *app = ctx; - furi_message_queue_put(app->event_queue,input_event,FuriWaitForever); +static void input_callback(InputEvent* input_event, void* ctx) { + ProtoViewApp* app = ctx; + furi_message_queue_put(app->event_queue, input_event, FuriWaitForever); } /* Called to switch view (when left/right is pressed). Handles @@ -80,15 +90,15 @@ static void input_callback(InputEvent* input_event, void* ctx) * The 'switchto' parameter can be the identifier of a view, or the * special views ViewGoNext and ViewGoPrev in order to move to * the logical next/prev view. */ -static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { +static void app_switch_view(ProtoViewApp* app, ProtoViewCurrentView switchto) { /* Switch to the specified view. */ ProtoViewCurrentView old = app->current_view; - if (switchto == ViewGoNext) { + if(switchto == ViewGoNext) { app->current_view++; - if (app->current_view == ViewLast) app->current_view = 0; - } else if (switchto == ViewGoPrev) { - if (app->current_view == 0) - app->current_view = ViewLast-1; + if(app->current_view == ViewLast) app->current_view = 0; + } else if(switchto == ViewGoPrev) { + if(app->current_view == 0) + app->current_view = ViewLast - 1; else app->current_view--; } else { @@ -103,20 +113,20 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { /* Reset the view private data each time, before calling the enter/exit * callbacks that may want to setup some state. */ - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); /* Call the enter/exit view callbacks if needed. */ - if (old == ViewDirectSampling) view_exit_direct_sampling(app); - if (new == ViewDirectSampling) view_enter_direct_sampling(app); - if (old == ViewBuildMessage) view_exit_build_message(app); - if (new == ViewBuildMessage) view_enter_build_message(app); - if (old == ViewInfo) view_exit_info(app); + if(old == ViewDirectSampling) view_exit_direct_sampling(app); + if(new == ViewDirectSampling) view_enter_direct_sampling(app); + if(old == ViewBuildMessage) view_exit_build_message(app); + if(new == ViewBuildMessage) view_enter_build_message(app); + if(old == ViewInfo) view_exit_info(app); /* The frequency/modulation settings are actually a single view: * as long as the user stays between the two modes of this view we * don't need to call the exit-view callback. */ - if ((old == ViewFrequencySettings && new != ViewModulationSettings) || - (old == ViewModulationSettings && new != ViewFrequencySettings)) + if((old == ViewFrequencySettings && new != ViewModulationSettings) || + (old == ViewModulationSettings && new != ViewFrequencySettings)) view_exit_settings(app); ui_dismiss_alert(app); @@ -125,7 +135,7 @@ static void app_switch_view(ProtoViewApp *app, ProtoViewCurrentView switchto) { /* Allocate the application state and initialize a number of stuff. * This is called in the entry point to create the application state. */ ProtoViewApp* protoview_app_alloc() { - ProtoViewApp *app = malloc(sizeof(ProtoViewApp)); + ProtoViewApp* app = malloc(sizeof(ProtoViewApp)); // Init shared data structures RawSamples = raw_samples_alloc(); @@ -148,10 +158,10 @@ ProtoViewApp* protoview_app_alloc() { app->show_text_input = false; app->alert_dismiss_time = 0; app->current_view = ViewRawPulses; - for (int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; + for(int j = 0; j < ViewLast; j++) app->current_subview[j] = 0; app->direct_sampling_enabled = false; app->view_privdata = malloc(PROTOVIEW_VIEW_PRIVDATA_LEN); - memset(app->view_privdata,0,PROTOVIEW_VIEW_PRIVDATA_LEN); + memset(app->view_privdata, 0, PROTOVIEW_VIEW_PRIVDATA_LEN); // Signal found and visualization defaults app->signal_bestlen = 0; @@ -176,17 +186,14 @@ ProtoViewApp* protoview_app_alloc() { app->txrx->environment = subghz_environment_alloc(); subghz_environment_set_protocol_registry( app->txrx->environment, (void*)&protoview_protocol_registry); - app->txrx->receiver = - subghz_receiver_alloc_init(app->txrx->environment); - subghz_receiver_set_filter(app->txrx->receiver, - SubGhzProtocolFlag_Decodable); + app->txrx->receiver = subghz_receiver_alloc_init(app->txrx->environment); + subghz_receiver_set_filter(app->txrx->receiver, SubGhzProtocolFlag_Decodable); subghz_worker_set_overrun_callback( - app->txrx->worker, - (SubGhzWorkerOverrunCallback)subghz_receiver_reset); + app->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); subghz_worker_set_pair_callback( app->txrx->worker, (SubGhzWorkerPairCallback)subghz_receiver_decode); subghz_worker_set_context(app->txrx->worker, app->txrx->receiver); - + app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; /* Defaults to ProtoViewModulations[0]. */ @@ -199,7 +206,7 @@ ProtoViewApp* protoview_app_alloc() { /* Free what the application allocated. It is not clear to me if the * Flipper OS, once the application exits, will be able to reclaim space * even if we forget to free something here. */ -void protoview_app_free(ProtoViewApp *app) { +void protoview_app_free(ProtoViewApp* app) { furi_assert(app); // Put CC1101 on sleep, this also restores charging. @@ -218,7 +225,7 @@ void protoview_app_free(ProtoViewApp *app) { subghz_setting_free(app->setting); // Worker stuff. - if (!app->txrx->debug_timer_sampling) { + if(!app->txrx->debug_timer_sampling) { subghz_receiver_free(app->txrx->receiver); subghz_environment_free(app->txrx->environment); subghz_worker_free(app->txrx->worker); @@ -236,8 +243,8 @@ void protoview_app_free(ProtoViewApp *app) { /* Called periodically. Do signal processing here. Data we process here * will be later displayed by the render callback. The side effect of this * function is to scan for signals and set DetectedSamples. */ -static void timer_callback(void *ctx) { - ProtoViewApp *app = ctx; +static void timer_callback(void* ctx) { + ProtoViewApp* app = ctx; uint32_t delta, lastidx = app->signal_last_scan_idx; /* scan_for_signal(), called by this function, deals with a @@ -245,14 +252,14 @@ static void timer_callback(void *ctx) { * cross-boundaries, it is enough if we scan each time the buffer fills * for 50% more compared to the last scan. Thanks to this check we * can avoid scanning too many times to just find the same data. */ - if (lastidx < RawSamples->idx) { + if(lastidx < RawSamples->idx) { delta = RawSamples->idx - lastidx; } else { delta = RawSamples->total - lastidx + RawSamples->idx; } - if (delta < RawSamples->total/2) return; + if(delta < RawSamples->total / 2) return; app->signal_last_scan_idx = RawSamples->idx; - scan_for_signal(app,RawSamples); + scan_for_signal(app, RawSamples); } /* This is the navigation callback we use in the view dispatcher used @@ -265,7 +272,7 @@ static void timer_callback(void *ctx) { * We just need a dummy callback returning false. We believe the * implementation should be changed and if no callback is set, it should be * the same as returning false. */ -static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { +static bool keyboard_view_dispatcher_navigation_callback(void* ctx) { UNUSED(ctx); return false; } @@ -273,10 +280,10 @@ static bool keyboard_view_dispatcher_navigation_callback(void *ctx) { /* App entry point, as specified in application.fam. */ int32_t protoview_app_entry(void* p) { UNUSED(p); - ProtoViewApp *app = protoview_app_alloc(); + ProtoViewApp* app = protoview_app_alloc(); /* Create a timer. We do data analysis in the callback. */ - FuriTimer *timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); + FuriTimer* timer = furi_timer_alloc(timer_callback, FuriTimerTypePeriodic, app); furi_timer_start(timer, furi_kernel_get_tick_frequency() / 8); /* Start listening to signals immediately. */ @@ -291,71 +298,68 @@ int32_t protoview_app_entry(void* p) { InputEvent input; while(app->running) { FuriStatus qstat = furi_message_queue_get(app->event_queue, &input, 100); - if (qstat == FuriStatusOk) { - if (DEBUG_MSG) FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", - input.type, input.key); + if(qstat == FuriStatusOk) { + if(DEBUG_MSG) + FURI_LOG_E(TAG, "Main Loop - Input: type %d key %u", input.type, input.key); /* Handle navigation here. Then handle view-specific inputs * in the view specific handling function. */ - if (input.type == InputTypeShort && - input.key == InputKeyBack) - { - if (app->current_view != ViewRawPulses) { + if(input.type == InputTypeShort && input.key == InputKeyBack) { + if(app->current_view != ViewRawPulses) { /* If this is not the main app view, go there. */ - app_switch_view(app,ViewRawPulses); + app_switch_view(app, ViewRawPulses); } else { /* If we are in the main app view, warn the user * they needs to long press to really quit. */ - ui_show_alert(app,"Long press to exit",1000); + ui_show_alert(app, "Long press to exit", 1000); } - } else if (input.type == InputTypeLong && - input.key == InputKeyBack) - { + } else if(input.type == InputTypeLong && input.key == InputKeyBack) { app->running = 0; - } else if (input.type == InputTypeShort && - input.key == InputKeyRight && - ui_get_current_subview(app) == 0) - { + } else if( + input.type == InputTypeShort && input.key == InputKeyRight && + ui_get_current_subview(app) == 0) { /* Go to the next view. */ - app_switch_view(app,ViewGoNext); - } else if (input.type == InputTypeShort && - input.key == InputKeyLeft && - ui_get_current_subview(app) == 0) - { + app_switch_view(app, ViewGoNext); + } else if( + input.type == InputTypeShort && input.key == InputKeyLeft && + ui_get_current_subview(app) == 0) { /* Go to the previous view. */ - app_switch_view(app,ViewGoPrev); + app_switch_view(app, ViewGoPrev); } else { /* This is where we pass the control to the currently * active view input processing. */ switch(app->current_view) { case ViewRawPulses: - process_input_raw_pulses(app,input); + process_input_raw_pulses(app, input); break; case ViewInfo: - process_input_info(app,input); + process_input_info(app, input); break; case ViewFrequencySettings: case ViewModulationSettings: - process_input_settings(app,input); + process_input_settings(app, input); break; case ViewDirectSampling: - process_input_direct_sampling(app,input); + process_input_direct_sampling(app, input); break; case ViewBuildMessage: - process_input_build_message(app,input); + process_input_build_message(app, input); + break; + default: + furi_crash(TAG "Invalid view selected"); break; - default: furi_crash(TAG "Invalid view selected"); break; } } } else { /* Useful to understand if the app is still alive when it * does not respond because of bugs. */ - if (DEBUG_MSG) { - static int c = 0; c++; - if (!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); + if(DEBUG_MSG) { + static int c = 0; + c++; + if(!(c % 20)) FURI_LOG_E(TAG, "Loop timeout"); } } - if (app->show_text_input) { + if(app->show_text_input) { /* Remove our viewport: we need to use a view dispatcher * in order to show the standard Flipper keyboard. */ gui_remove_view_port(app->gui, app->view_port); @@ -368,11 +372,11 @@ int32_t protoview_app_entry(void* p) { * otherwise when the user presses back on the keyboard to * abort, the dispatcher will not stop. */ view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, - keyboard_view_dispatcher_navigation_callback); + app->view_dispatcher, keyboard_view_dispatcher_navigation_callback); app->text_input = text_input_alloc(); - view_dispatcher_set_event_callback_context(app->view_dispatcher,app); - view_dispatcher_add_view(app->view_dispatcher, 0, text_input_get_view(app->text_input)); + view_dispatcher_set_event_callback_context(app->view_dispatcher, app); + view_dispatcher_add_view( + app->view_dispatcher, 0, text_input_get_view(app->text_input)); view_dispatcher_switch_to_view(app->view_dispatcher, 0); /* Setup the text input view. The different parameters are set @@ -388,7 +392,8 @@ int32_t protoview_app_entry(void* p) { false); /* Run the dispatcher with the keyboard. */ - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_attach_to_gui( + app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); view_dispatcher_run(app->view_dispatcher); /* Undo all it: remove the view from the dispatcher, free it @@ -406,7 +411,7 @@ int32_t protoview_app_entry(void* p) { } /* App no longer running. Shut down and free. */ - if (app->txrx->txrx_state == TxRxStateRx) { + if(app->txrx->txrx_state == TxRxStateRx) { FURI_LOG_E(TAG, "Putting CC1101 to sleep before exiting."); radio_rx_end(app); radio_sleep(app); @@ -416,4 +421,3 @@ int32_t protoview_app_entry(void* p) { protoview_app_free(app); return 0; } - diff --git a/applications/plugins/protoview/app.h b/applications/plugins/protoview/app.h index 33bd85eb4..85007e345 100644 --- a/applications/plugins/protoview/app.h +++ b/applications/plugins/protoview/app.h @@ -66,11 +66,11 @@ typedef enum { /* ================================== RX/TX ================================= */ typedef struct { - const char *name; // Name to show to the user. - const char *id; // Identifier in the Flipper API/file. - FuriHalSubGhzPreset preset; // The preset ID. - uint8_t *custom; // If not null, a set of registers for - // the CC1101, specifying a custom preset. + const char* name; // Name to show to the user. + const char* id; // Identifier in the Flipper API/file. + FuriHalSubGhzPreset preset; // The preset ID. + uint8_t* custom; // If not null, a set of registers for + // the CC1101, specifying a custom preset. } ProtoViewModulation; extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ @@ -79,19 +79,19 @@ extern ProtoViewModulation ProtoViewModulations[]; /* In app_subghz.c */ * It receives data and we get our protocol "feed" callback called * with the level (1 or 0) and duration. */ struct ProtoViewTxRx { - bool freq_mod_changed; /* The user changed frequency and/or modulation + bool freq_mod_changed; /* The user changed frequency and/or modulation from the interface. There is to restart the radio with the right parameters. */ - SubGhzWorker* worker; /* Our background worker. */ + SubGhzWorker* worker; /* Our background worker. */ SubGhzEnvironment* environment; SubGhzReceiver* receiver; TxRxState txrx_state; /* Receiving, idle or sleeping? */ /* Timer sampling mode state. */ - bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only + bool debug_timer_sampling; /* Read data from GDO0 in a busy loop. Only for testing. */ uint32_t last_g0_change_time; /* Last high->low (or reverse) switch. */ - bool last_g0_value; /* Current value (high or low): we are + bool last_g0_value; /* Current value (high or low): we are checking the duration in the timer handler. */ }; @@ -103,44 +103,44 @@ typedef struct ProtoViewTxRx ProtoViewTxRx; #define ALERT_MAX_LEN 32 struct ProtoViewApp { /* GUI */ - Gui *gui; - NotificationApp *notification; - ViewPort *view_port; /* We just use a raw viewport and we render + Gui* gui; + NotificationApp* notification; + ViewPort* view_port; /* We just use a raw viewport and we render everything into the low level canvas. */ - ProtoViewCurrentView current_view; /* Active left-right view ID. */ - int current_subview[ViewLast]; /* Active up-down subview ID. */ - FuriMessageQueue *event_queue; /* Keypress events go here. */ + ProtoViewCurrentView current_view; /* Active left-right view ID. */ + int current_subview[ViewLast]; /* Active up-down subview ID. */ + FuriMessageQueue* event_queue; /* Keypress events go here. */ /* Input text state. */ - ViewDispatcher *view_dispatcher; /* Used only when we want to show + ViewDispatcher* view_dispatcher; /* Used only when we want to show the text_input view for a moment. Otherwise it is set to null. */ - TextInput *text_input; + TextInput* text_input; bool show_text_input; - char *text_input_buffer; + char* text_input_buffer; uint32_t text_input_buffer_len; void (*text_input_done_callback)(void*); /* Alert state. */ - uint32_t alert_dismiss_time; /* Millisecond when the alert will be + uint32_t alert_dismiss_time; /* Millisecond when the alert will be no longer shown. Or zero if the alert is currently not set at all. */ char alert_text[ALERT_MAX_LEN]; /* Alert content. */ /* Radio related. */ - ProtoViewTxRx *txrx; /* Radio state. */ - SubGhzSetting *setting; /* A list of valid frequencies. */ + ProtoViewTxRx* txrx; /* Radio state. */ + SubGhzSetting* setting; /* A list of valid frequencies. */ /* Generic app state. */ - int running; /* Once false exists the app. */ + int running; /* Once false exists the app. */ uint32_t signal_bestlen; /* Longest coherent signal observed so far. */ uint32_t signal_last_scan_idx; /* Index of the buffer last time we performed the scan. */ - bool signal_decoded; /* Was the current signal decoded? */ - ProtoViewMsgInfo *msg_info; /* Decoded message info if not NULL. */ + bool signal_decoded; /* Was the current signal decoded? */ + ProtoViewMsgInfo* msg_info; /* Decoded message info if not NULL. */ bool direct_sampling_enabled; /* This special view needs an explicit acknowledge to work. */ - void *view_privdata; /* This is a piece of memory of total size + void* view_privdata; /* This is a piece of memory of total size PROTOVIEW_VIEW_PRIVDATA_LEN that it is initialized to zero when we switch to a a new view. While the view we are using @@ -149,12 +149,12 @@ struct ProtoViewApp { the pointer to a few specific-data structure. */ /* Raw view apps state. */ - uint32_t us_scale; /* microseconds per pixel. */ - uint32_t signal_offset; /* Long press left/right panning in raw view. */ + uint32_t us_scale; /* microseconds per pixel. */ + uint32_t signal_offset; /* Long press left/right panning in raw view. */ /* Configuration view app state. */ - uint32_t frequency; /* Current frequency. */ - uint8_t modulation; /* Current modulation ID, array index in the + uint32_t frequency; /* Current frequency. */ + uint8_t modulation; /* Current modulation ID, array index in the ProtoViewModulations table. */ }; @@ -165,18 +165,18 @@ struct ProtoViewApp { * in the message info view. */ #define PROTOVIEW_MSG_STR_LEN 32 typedef struct ProtoViewMsgInfo { - ProtoViewDecoder *decoder; /* The decoder that decoded the message. */ - ProtoViewFieldSet *fieldset; /* Decoded fields. */ + ProtoViewDecoder* decoder; /* The decoder that decoded the message. */ + ProtoViewFieldSet* fieldset; /* Decoded fields. */ /* Low level information of the detected signal: the following are filled * by the protocol decoding function: */ - uint32_t start_off; /* Pulses start offset in the bitmap. */ - uint32_t pulses_count; /* Number of pulses of the full message. */ + uint32_t start_off; /* Pulses start offset in the bitmap. */ + uint32_t pulses_count; /* Number of pulses of the full message. */ /* The following are passed already filled to the decoder. */ - uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ + uint32_t short_pulse_dur; /* Microseconds duration of the short pulse. */ /* The following are filled by ProtoView core after the decoder returned * success. */ - uint8_t *bits; /* Bitmap with the signal. */ - uint32_t bits_bytes; /* Number of full bytes in the bitmap, that + uint8_t* bits; /* Bitmap with the signal. */ + uint32_t bits_bytes; /* Number of full bytes in the bitmap, that is 'pulses_count/8' rounded to the next integer. */ } ProtoViewMsgInfo; @@ -196,28 +196,28 @@ typedef enum { typedef struct { ProtoViewFieldType type; - uint32_t len; // Depends on type: - // Bits for integers (signed,unsigned,binary,hex). - // Number of characters for strings. - // Number of nibbles for bytes (1 for each 4 bits). - // Number of digits after dot for floats. - char *name; // Field name. + uint32_t len; // Depends on type: + // Bits for integers (signed,unsigned,binary,hex). + // Number of characters for strings. + // Number of nibbles for bytes (1 for each 4 bits). + // Number of digits after dot for floats. + char* name; // Field name. union { - char *str; // String type. - int64_t value; // Signed integer type. - uint64_t uvalue; // Unsigned integer type. - uint8_t *bytes; // Raw bytes type. - float fvalue; // Float type. + char* str; // String type. + int64_t value; // Signed integer type. + uint64_t uvalue; // Unsigned integer type. + uint8_t* bytes; // Raw bytes type. + float fvalue; // Float type. }; } ProtoViewField; typedef struct ProtoViewFieldSet { - ProtoViewField **fields; + ProtoViewField** fields; uint32_t numfields; } ProtoViewFieldSet; typedef struct ProtoViewDecoder { - const char *name; /* Protocol name. */ + const char* name; /* Protocol name. */ /* The decode function takes a buffer that is actually a bitmap, with * high and low levels represented as 0 and 1. The number of high/low * pulses represented by the bitmap is passed as the 'numbits' argument, @@ -225,15 +225,15 @@ typedef struct ProtoViewDecoder { * 'bits'. So 'numbytes' is mainly useful to pass as argument to other * functions that perform bit extraction with bound checking, such as * bitmap_get() and so forth. */ - bool (*decode)(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info); + bool (*decode)(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info); /* This method is used by the decoder to return the fields it needs * in order to build a new message. This way the message builder view * can ask the user to fill the right set of fields of the specified * type. */ - void (*get_fields)(ProtoViewFieldSet *fields); + void (*get_fields)(ProtoViewFieldSet* fields); /* This method takes the fields supported by the decoder, and * renders a message in 'samples'. */ - void (*build_message)(RawSamplesBuffer *samples, ProtoViewFieldSet *fields); + void (*build_message)(RawSamplesBuffer* samples, ProtoViewFieldSet* fields); } ProtoViewDecoder; extern RawSamplesBuffer *RawSamples, *DetectedSamples; @@ -244,76 +244,118 @@ uint32_t radio_rx(ProtoViewApp* app); void radio_idle(ProtoViewApp* app); void radio_rx_end(ProtoViewApp* app); void radio_sleep(ProtoViewApp* app); -void raw_sampling_worker_start(ProtoViewApp *app); -void raw_sampling_worker_stop(ProtoViewApp *app); -void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx); +void raw_sampling_worker_start(ProtoViewApp* app); +void raw_sampling_worker_stop(ProtoViewApp* app); +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx); /* signal.c */ uint32_t duration_delta(uint32_t a, uint32_t b); -void reset_current_signal(ProtoViewApp *app); -void scan_for_signal(ProtoViewApp *app,RawSamplesBuffer *source); -bool bitmap_get(uint8_t *b, uint32_t blen, uint32_t bitpos); -void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val); -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, uint8_t *s, uint32_t slen, uint32_t soff, uint32_t count); -void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat); -void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len); -bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits); -uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits); -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t offset, const char *zero_pattern, const char *one_pattern); -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous); -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app); -void free_msg_info(ProtoViewMsgInfo *i); +void reset_current_signal(ProtoViewApp* app); +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source); +bool bitmap_get(uint8_t* b, uint32_t blen, uint32_t bitpos); +void bitmap_set(uint8_t* b, uint32_t blen, uint32_t bitpos, bool val); +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count); +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat); +void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len); +bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits); +uint32_t bitmap_seek_bits( + uint8_t* b, + uint32_t blen, + uint32_t startpos, + uint32_t maxbits, + const char* bits); +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t offset, + const char* zero_pattern, + const char* one_pattern); +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous); +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app); +void free_msg_info(ProtoViewMsgInfo* i); /* signal_file.c */ -bool save_signal(ProtoViewApp *app, const char *filename); +bool save_signal(ProtoViewApp* app, const char* filename); /* view_*.c */ -void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app); -void process_input_raw_pulses(ProtoViewApp *app, InputEvent input); -void render_view_settings(Canvas *const canvas, ProtoViewApp *app); -void process_input_settings(ProtoViewApp *app, InputEvent input); -void render_view_info(Canvas *const canvas, ProtoViewApp *app); -void process_input_info(ProtoViewApp *app, InputEvent input); -void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app); -void process_input_direct_sampling(ProtoViewApp *app, InputEvent input); -void render_view_build_message(Canvas *const canvas, ProtoViewApp *app); -void process_input_build_message(ProtoViewApp *app, InputEvent input); -void view_enter_build_message(ProtoViewApp *app); -void view_exit_build_message(ProtoViewApp *app); -void view_enter_direct_sampling(ProtoViewApp *app); -void view_exit_direct_sampling(ProtoViewApp *app); -void view_exit_settings(ProtoViewApp *app); -void view_exit_info(ProtoViewApp *app); -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur); +void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app); +void process_input_raw_pulses(ProtoViewApp* app, InputEvent input); +void render_view_settings(Canvas* const canvas, ProtoViewApp* app); +void process_input_settings(ProtoViewApp* app, InputEvent input); +void render_view_info(Canvas* const canvas, ProtoViewApp* app); +void process_input_info(ProtoViewApp* app, InputEvent input); +void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app); +void process_input_direct_sampling(ProtoViewApp* app, InputEvent input); +void render_view_build_message(Canvas* const canvas, ProtoViewApp* app); +void process_input_build_message(ProtoViewApp* app, InputEvent input); +void view_enter_build_message(ProtoViewApp* app); +void view_exit_build_message(ProtoViewApp* app); +void view_enter_direct_sampling(ProtoViewApp* app); +void view_exit_direct_sampling(ProtoViewApp* app); +void view_exit_settings(ProtoViewApp* app); +void view_exit_info(ProtoViewApp* app); +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur); /* ui.c */ -int ui_get_current_subview(ProtoViewApp *app); -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, int last_subview); -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview); -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)); -void ui_dismiss_keyboard(ProtoViewApp *app); -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl); -void ui_dismiss_alert(ProtoViewApp *app); -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app); -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color); +int ui_get_current_subview(ProtoViewApp* app); +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview); +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview); +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)); +void ui_dismiss_keyboard(ProtoViewApp* app); +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl); +void ui_dismiss_alert(ProtoViewApp* app); +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app); +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color); /* fields.c */ -void fieldset_free(ProtoViewFieldSet *fs); -ProtoViewFieldSet *fieldset_new(void); -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits); -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits); -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s); -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count); -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot); -const char *field_get_type_name(ProtoViewField *f); -int field_to_string(char *buf, size_t len, ProtoViewField *f); -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len); -bool field_incr_value(ProtoViewField *f, int incr); -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, ProtoViewFieldSet *src); -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src); +void fieldset_free(ProtoViewFieldSet* fs); +ProtoViewFieldSet* fieldset_new(void); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot); +const char* field_get_type_name(ProtoViewField* f); +int field_to_string(char* buf, size_t len, ProtoViewField* f); +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len); +bool field_incr_value(ProtoViewField* f, int incr); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src); +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src); /* crc.c */ -uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly); +uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly); diff --git a/applications/plugins/protoview/app_subghz.c b/applications/plugins/protoview/app_subghz.c index 55905e8a3..73e0e16ae 100644 --- a/applications/plugins/protoview/app_subghz.c +++ b/applications/plugins/protoview/app_subghz.c @@ -9,18 +9,20 @@ #include #include -void raw_sampling_worker_start(ProtoViewApp *app); -void raw_sampling_worker_stop(ProtoViewApp *app); +void raw_sampling_worker_start(ProtoViewApp* app); +void raw_sampling_worker_stop(ProtoViewApp* app); ProtoViewModulation ProtoViewModulations[] = { - {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", - FuriHalSubGhzPresetOok650Async, NULL}, - {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", - FuriHalSubGhzPresetOok270Async, NULL}, - {"2FSK 2.38Khz", "FuriHalSubGhzPreset2FSKDev238Async", - FuriHalSubGhzPreset2FSKDev238Async, NULL}, - {"2FSK 47.6Khz", "FuriHalSubGhzPreset2FSKDev476Async", - FuriHalSubGhzPreset2FSKDev476Async, NULL}, + {"OOK 650Khz", "FuriHalSubGhzPresetOok650Async", FuriHalSubGhzPresetOok650Async, NULL}, + {"OOK 270Khz", "FuriHalSubGhzPresetOok270Async", FuriHalSubGhzPresetOok270Async, NULL}, + {"2FSK 2.38Khz", + "FuriHalSubGhzPreset2FSKDev238Async", + FuriHalSubGhzPreset2FSKDev238Async, + NULL}, + {"2FSK 47.6Khz", + "FuriHalSubGhzPreset2FSKDev476Async", + FuriHalSubGhzPreset2FSKDev476Async, + NULL}, {"TPMS 1 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms1_fsk_async_regs}, {"TPMS 2 (OOK)", NULL, 0, (uint8_t*)protoview_subghz_tpms2_ook_async_regs}, {"TPMS 3 (FSK)", NULL, 0, (uint8_t*)protoview_subghz_tpms3_fsk_async_regs}, @@ -44,12 +46,10 @@ void radio_begin(ProtoViewApp* app) { /* The CC1101 preset can be either one of the standard presets, if * the modulation "custom" field is NULL, or a custom preset we * defined in custom_presets.h. */ - if (ProtoViewModulations[app->modulation].custom == NULL) { - furi_hal_subghz_load_preset( - ProtoViewModulations[app->modulation].preset); + if(ProtoViewModulations[app->modulation].custom == NULL) { + furi_hal_subghz_load_preset(ProtoViewModulations[app->modulation].preset); } else { - furi_hal_subghz_load_custom_preset( - ProtoViewModulations[app->modulation].custom); + furi_hal_subghz_load_custom_preset(ProtoViewModulations[app->modulation].custom); } furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); app->txrx->txrx_state = TxRxStateIDLE; @@ -61,10 +61,10 @@ void radio_begin(ProtoViewApp* app) { uint32_t radio_rx(ProtoViewApp* app) { furi_assert(app); if(!furi_hal_subghz_is_frequency_valid(app->frequency)) { - furi_crash(TAG" Incorrect RX frequency."); + furi_crash(TAG " Incorrect RX frequency."); } - if (app->txrx->txrx_state == TxRxStateRx) return app->frequency; + if(app->txrx->txrx_state == TxRxStateRx) return app->frequency; furi_hal_subghz_idle(); /* Put it into idle state in case it is sleeping. */ uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency); @@ -72,10 +72,8 @@ uint32_t radio_rx(ProtoViewApp* app) { furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); furi_hal_subghz_rx(); - if (!app->txrx->debug_timer_sampling) { - - furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, - app->txrx->worker); + if(!app->txrx->debug_timer_sampling) { + furi_hal_subghz_start_async_rx(subghz_worker_rx_callback, app->txrx->worker); subghz_worker_start(app->txrx->worker); } else { raw_sampling_worker_start(app); @@ -88,8 +86,8 @@ uint32_t radio_rx(ProtoViewApp* app) { void radio_rx_end(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { - if (!app->txrx->debug_timer_sampling) { + if(app->txrx->txrx_state == TxRxStateRx) { + if(!app->txrx->debug_timer_sampling) { if(subghz_worker_is_running(app->txrx->worker)) { subghz_worker_stop(app->txrx->worker); furi_hal_subghz_stop_async_rx(); @@ -105,7 +103,7 @@ void radio_rx_end(ProtoViewApp* app) { /* Put radio on sleep. */ void radio_sleep(ProtoViewApp* app) { furi_assert(app); - if (app->txrx->txrx_state == TxRxStateRx) { + if(app->txrx->txrx_state == TxRxStateRx) { /* We can't go from having an active RX worker to sleeping. * Stop the RX subsystems first. */ radio_rx_end(app); @@ -120,10 +118,10 @@ void radio_sleep(ProtoViewApp* app) { /* This function suspends the current RX state, switches to TX mode, * transmits the signal provided by the callback data_feeder, and later * restores the RX state if there was one. */ -void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx) { +void radio_tx_signal(ProtoViewApp* app, FuriHalSubGhzAsyncTxCallback data_feeder, void* ctx) { TxRxState oldstate = app->txrx->txrx_state; - if (oldstate == TxRxStateRx) radio_rx_end(app); + if(oldstate == TxRxStateRx) radio_rx_end(app); radio_begin(app); furi_hal_subghz_idle(); @@ -138,7 +136,7 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder furi_hal_subghz_idle(); radio_begin(app); - if (oldstate == TxRxStateRx) radio_rx(app); + if(oldstate == TxRxStateRx) radio_rx(app); } /* ============================= Raw sampling mode ============================= @@ -148,15 +146,15 @@ void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder * Flipper system. * ===========================================================================*/ -void protoview_timer_isr(void *ctx) { - ProtoViewApp *app = ctx; +void protoview_timer_isr(void* ctx) { + ProtoViewApp* app = ctx; bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if (app->txrx->last_g0_value != level) { + if(app->txrx->last_g0_value != level) { uint32_t now = DWT->CYCCNT; uint32_t dur = now - app->txrx->last_g0_change_time; dur /= furi_hal_cortex_instructions_per_microsecond(); - if (dur > 15000) dur = 15000; + if(dur > 15000) dur = 15000; raw_samples_add(RawSamples, app->txrx->last_g0_value, dur); app->txrx->last_g0_value = level; app->txrx->last_g0_change_time = now; @@ -164,13 +162,13 @@ void protoview_timer_isr(void *ctx) { LL_TIM_ClearFlag_UPDATE(TIM2); } -void raw_sampling_worker_start(ProtoViewApp *app) { +void raw_sampling_worker_start(ProtoViewApp* app) { UNUSED(app); LL_TIM_InitTypeDef tim_init = { - .Prescaler = 63, /* CPU frequency is ~64Mhz. */ + .Prescaler = 63, /* CPU frequency is ~64Mhz. */ .CounterMode = LL_TIM_COUNTERMODE_UP, - .Autoreload = 5, /* Sample every 5 us */ + .Autoreload = 5, /* Sample every 5 us */ }; LL_TIM_Init(TIM2, &tim_init); @@ -183,7 +181,7 @@ void raw_sampling_worker_start(ProtoViewApp *app) { FURI_LOG_E(TAG, "Timer enabled"); } -void raw_sampling_worker_stop(ProtoViewApp *app) { +void raw_sampling_worker_stop(ProtoViewApp* app) { UNUSED(app); FURI_CRITICAL_ENTER(); LL_TIM_DisableCounter(TIM2); diff --git a/applications/plugins/protoview/crc.c b/applications/plugins/protoview/crc.c index 38a809e10..94d482972 100644 --- a/applications/plugins/protoview/crc.c +++ b/applications/plugins/protoview/crc.c @@ -3,14 +3,13 @@ /* CRC8 with the specified initialization value 'init' and * polynomial 'poly'. */ -uint8_t crc8(const uint8_t *data, size_t len, uint8_t init, uint8_t poly) -{ +uint8_t crc8(const uint8_t* data, size_t len, uint8_t init, uint8_t poly) { uint8_t crc = init; size_t i, j; - for (i = 0; i < len; i++) { + for(i = 0; i < len; i++) { crc ^= data[i]; - for (j = 0; j < 8; j++) { - if ((crc & 0x80) != 0) + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) crc = (uint8_t)((crc << 1) ^ poly); else crc <<= 1; diff --git a/applications/plugins/protoview/custom_presets.h b/applications/plugins/protoview/custom_presets.h index cb9a421c6..00aa49945 100644 --- a/applications/plugins/protoview/custom_presets.h +++ b/applications/plugins/protoview/custom_presets.h @@ -76,7 +76,8 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 20kBaud {CC1101_MDMCFG4, 0x59}, // Rx bandwidth filter is 325 kHz {CC1101_DEVIATN, 0x41}, // Deviation 28.56 kHz @@ -106,8 +107,10 @@ static uint8_t protoview_subghz_tpms1_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* This is like the default Flipper OOK 640Khz bandwidth preset, but * the bandwidth is changed to 10kBaud to accomodate TPMS frequency. */ @@ -156,8 +159,10 @@ static const uint8_t protoview_subghz_tpms2_ook_async_regs[][2] = { {0, 0}, /* CC1101 OOK PATABLE. */ - {0, 0xC0}, {0,0}, {0,0}, {0,0} -}; + {0, 0xC0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* 40 KBaud, 2FSK, 28 kHz deviation, 270 Khz bandwidth filter. */ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { @@ -174,7 +179,8 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { // // Modem Configuration {CC1101_MDMCFG0, 0x00}, {CC1101_MDMCFG1, 0x02}, - {CC1101_MDMCFG2, 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. + {CC1101_MDMCFG2, + 0x04}, // Format 2-FSK/FM, No preamble/sync, Disable (current optimized). Other code reading TPMS uses GFSK, but should be the same when in RX mode. {CC1101_MDMCFG3, 0x93}, // Data rate is 40kBaud {CC1101_MDMCFG4, 0x6A}, // 6 = BW filter 270kHz, A = Data rate exp {CC1101_DEVIATN, 0x41}, // Deviation 28kHz @@ -204,8 +210,10 @@ static uint8_t protoview_subghz_tpms3_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; /* FSK 19k dev, 325 Khz filter, 20kBaud. Works well with Toyota. */ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { @@ -250,6 +258,7 @@ static uint8_t protoview_subghz_tpms4_fsk_async_regs[][2] = { {0, 0}, /* CC1101 2FSK PATABLE. */ - {0xC0, 0}, {0,0}, {0,0}, {0,0} -}; - + {0xC0, 0}, + {0, 0}, + {0, 0}, + {0, 0}}; diff --git a/applications/plugins/protoview/data_feed.c b/applications/plugins/protoview/data_feed.c index a3bed238e..81d1a8020 100644 --- a/applications/plugins/protoview/data_feed.c +++ b/applications/plugins/protoview/data_feed.c @@ -14,7 +14,7 @@ const SubGhzProtocol subghz_protocol_protoview; /* The feed() method puts data in the RawSamples global (protected by * a mutex). */ -extern RawSamplesBuffer *RawSamples; +extern RawSamplesBuffer* RawSamples; /* This is totally dummy: we just define the decoder base for the async * system to work but we don't really use it if not to collect raw @@ -26,8 +26,7 @@ typedef struct SubGhzProtocolDecoderprotoview { void* subghz_protocol_decoder_protoview_alloc(SubGhzEnvironment* environment) { UNUSED(environment); - SubGhzProtocolDecoderprotoview* instance = - malloc(sizeof(SubGhzProtocolDecoderprotoview)); + SubGhzProtocolDecoderprotoview* instance = malloc(sizeof(SubGhzProtocolDecoderprotoview)); instance->base.protocol = &subghz_protocol_protoview; return instance; } @@ -66,8 +65,7 @@ uint8_t subghz_protocol_decoder_protoview_get_hash_data(void* context) { bool subghz_protocol_decoder_protoview_serialize( void* context, FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) -{ + SubGhzRadioPreset* preset) { UNUSED(context); UNUSED(flipper_format); UNUSED(preset); @@ -75,15 +73,13 @@ bool subghz_protocol_decoder_protoview_serialize( } /* Not used. */ -bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) -{ +bool subghz_protocol_decoder_protoview_deserialize(void* context, FlipperFormat* flipper_format) { UNUSED(context); UNUSED(flipper_format); return false; } -void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) -{ +void subhz_protocol_decoder_protoview_get_string(void* context, FuriString* output) { furi_assert(context); furi_string_cat_printf(output, "Protoview"); } @@ -116,5 +112,4 @@ const SubGhzProtocol* protoview_protocol_registry_items[] = { const SubGhzProtocolRegistry protoview_protocol_registry = { .items = protoview_protocol_registry_items, - .size = COUNT_OF(protoview_protocol_registry_items) -}; + .size = COUNT_OF(protoview_protocol_registry_items)}; diff --git a/applications/plugins/protoview/fields.c b/applications/plugins/protoview/fields.c index bc62cda54..47d573f4f 100644 --- a/applications/plugins/protoview/fields.c +++ b/applications/plugins/protoview/fields.c @@ -7,8 +7,8 @@ /* Create a new field of the specified type. Without populating its * type-specific value. */ -static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { - ProtoViewField *f = malloc(sizeof(*f)); +static ProtoViewField* field_new(ProtoViewFieldType type, const char* name) { + ProtoViewField* f = malloc(sizeof(*f)); f->type = type; f->name = strdup(name); return f; @@ -16,72 +16,80 @@ static ProtoViewField *field_new(ProtoViewFieldType type, const char *name) { /* Free only the auxiliary data of a field, used to represent the * current type. Name and type are not touched. */ -static void field_free_aux_data(ProtoViewField *f) { +static void field_free_aux_data(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: free(f->str); break; - case FieldTypeBytes: free(f->bytes); break; - default: break; // Nothing to free for other types. + case FieldTypeStr: + free(f->str); + break; + case FieldTypeBytes: + free(f->bytes); + break; + default: + break; // Nothing to free for other types. } } /* Free a field an associated data. */ -static void field_free(ProtoViewField *f) { +static void field_free(ProtoViewField* f) { field_free_aux_data(f); free(f->name); free(f); } /* Return the type of the field as string. */ -const char *field_get_type_name(ProtoViewField *f) { +const char* field_get_type_name(ProtoViewField* f) { switch(f->type) { - case FieldTypeStr: return "str"; - case FieldTypeSignedInt: return "int"; - case FieldTypeUnsignedInt: return "uint"; - case FieldTypeBinary: return "bin"; - case FieldTypeHex: return "hex"; - case FieldTypeBytes: return "bytes"; - case FieldTypeFloat: return "float"; + case FieldTypeStr: + return "str"; + case FieldTypeSignedInt: + return "int"; + case FieldTypeUnsignedInt: + return "uint"; + case FieldTypeBinary: + return "bin"; + case FieldTypeHex: + return "hex"; + case FieldTypeBytes: + return "bytes"; + case FieldTypeFloat: + return "float"; } return "unknown"; } /* Set a string representation of the specified field in buf. */ -int field_to_string(char *buf, size_t len, ProtoViewField *f) { +int field_to_string(char* buf, size_t len, ProtoViewField* f) { switch(f->type) { case FieldTypeStr: - return snprintf(buf,len,"%s", f->str); + return snprintf(buf, len, "%s", f->str); case FieldTypeSignedInt: - return snprintf(buf,len,"%lld", (long long) f->value); + return snprintf(buf, len, "%lld", (long long)f->value); case FieldTypeUnsignedInt: - return snprintf(buf,len,"%llu", (unsigned long long) f->uvalue); - case FieldTypeBinary: - { - uint64_t test_bit = (1 << (f->len-1)); - uint64_t idx = 0; - while(idx < len-1 && test_bit) { - buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; - test_bit >>= 1; - } - buf[idx] = 0; - return idx; + return snprintf(buf, len, "%llu", (unsigned long long)f->uvalue); + case FieldTypeBinary: { + uint64_t test_bit = (1 << (f->len - 1)); + uint64_t idx = 0; + while(idx < len - 1 && test_bit) { + buf[idx++] = (f->uvalue & test_bit) ? '1' : '0'; + test_bit >>= 1; } + buf[idx] = 0; + return idx; + } case FieldTypeHex: - return snprintf(buf, len, "%*llX", (int)(f->len+7)/8, f->uvalue); + return snprintf(buf, len, "%*llX", (int)(f->len + 7) / 8, f->uvalue); case FieldTypeFloat: return snprintf(buf, len, "%.*f", (int)f->len, (double)f->fvalue); - case FieldTypeBytes: - { - uint64_t idx = 0; - while(idx < len-1 && idx < f->len) { - const char *charset = "0123456789ABCDEF"; - uint32_t nibble = idx & 1 ? - (f->bytes[idx/2] & 0xf) : - (f->bytes[idx/2] >> 4); - buf[idx++] = charset[nibble]; - } - buf[idx] = 0; - return idx; + case FieldTypeBytes: { + uint64_t idx = 0; + while(idx < len - 1 && idx < f->len) { + const char* charset = "0123456789ABCDEF"; + uint32_t nibble = idx & 1 ? (f->bytes[idx / 2] & 0xf) : (f->bytes[idx / 2] >> 4); + buf[idx++] = charset[nibble]; } + buf[idx] = 0; + return idx; + } } return 0; } @@ -96,7 +104,7 @@ int field_to_string(char *buf, size_t len, ProtoViewField *f) { * The function returns true if the filed was successfully set to the * new value, otherwise if the specified value is invalid for the * field type, false is returned. */ -bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { +bool field_set_from_string(ProtoViewField* f, char* buf, size_t len) { // Initialize values to zero since the Flipper sscanf() implementation // is fuzzy... may populate only part of the value. long long val = 0; @@ -107,80 +115,78 @@ bool field_set_from_string(ProtoViewField *f, char *buf, size_t len) { case FieldTypeStr: free(f->str); f->len = len; - f->str = malloc(len+1); - memcpy(f->str,buf,len+1); + f->str = malloc(len + 1); + memcpy(f->str, buf, len + 1); break; case FieldTypeSignedInt: - if (!sscanf(buf,"%lld",&val)) return false; + if(!sscanf(buf, "%lld", &val)) return false; f->value = val; break; case FieldTypeUnsignedInt: - if (!sscanf(buf,"%llu",&uval)) return false; + if(!sscanf(buf, "%llu", &uval)) return false; f->uvalue = uval; break; - case FieldTypeBinary: - { - uint64_t bit_to_set = (1 << (len-1)); - uint64_t idx = 0; - uval = 0; - while(buf[idx]) { - if (buf[idx] == '1') uval |= bit_to_set; - else if (buf[idx] != '0') return false; - bit_to_set >>= 1; - idx++; - } - f->uvalue = uval; + case FieldTypeBinary: { + uint64_t bit_to_set = (1 << (len - 1)); + uint64_t idx = 0; + uval = 0; + while(buf[idx]) { + if(buf[idx] == '1') + uval |= bit_to_set; + else if(buf[idx] != '0') + return false; + bit_to_set >>= 1; + idx++; } - break; + f->uvalue = uval; + } break; case FieldTypeHex: - if (!sscanf(buf,"%llx",&uval) && - !sscanf(buf,"%llX",&uval)) return false; + if(!sscanf(buf, "%llx", &uval) && !sscanf(buf, "%llX", &uval)) return false; f->uvalue = uval; break; case FieldTypeFloat: - if (!sscanf(buf,"%f",&fval)) return false; + if(!sscanf(buf, "%f", &fval)) return false; f->fvalue = fval; break; - case FieldTypeBytes: - { - if (len > f->len) return false; - uint64_t idx = 0; - while(buf[idx]) { - uint8_t nibble = 0; - char c = toupper(buf[idx]); - if (c >= '0' && c <= '9') nibble = c-'0'; - else if (c >= 'A' && c <= 'F') nibble = 10+(c-'A'); - else return false; + case FieldTypeBytes: { + if(len > f->len) return false; + uint64_t idx = 0; + while(buf[idx]) { + uint8_t nibble = 0; + char c = toupper(buf[idx]); + if(c >= '0' && c <= '9') + nibble = c - '0'; + else if(c >= 'A' && c <= 'F') + nibble = 10 + (c - 'A'); + else + return false; - if (idx & 1) { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0xF0) | nibble; - } else { - f->bytes[idx/2] = - (f->bytes[idx/2] & 0x0F) | (nibble<<4); - } - idx++; + if(idx & 1) { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0xF0) | nibble; + } else { + f->bytes[idx / 2] = (f->bytes[idx / 2] & 0x0F) | (nibble << 4); } - buf[idx] = 0; + idx++; } - break; + buf[idx] = 0; + } break; } return true; } /* Set the 'dst' field to contain a copy of the value of the 'src' * field. The field name is not modified. */ -void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { +void field_set_from_field(ProtoViewField* dst, ProtoViewField* src) { field_free_aux_data(dst); dst->type = src->type; dst->len = src->len; - switch(src->type) { + switch(src->type) { case FieldTypeStr: dst->str = strdup(src->str); break; case FieldTypeBytes: dst->bytes = malloc(src->len); - memcpy(dst->bytes,src->bytes,dst->len); + memcpy(dst->bytes, src->bytes, dst->len); break; case FieldTypeSignedInt: dst->value = src->value; @@ -199,159 +205,159 @@ void field_set_from_field(ProtoViewField *dst, ProtoViewField *src) { /* Increment the specified field value of 'incr'. If the field type * does not support increments false is returned, otherwise the * action is performed. */ -bool field_incr_value(ProtoViewField *f, int incr) { +bool field_incr_value(ProtoViewField* f, int incr) { switch(f->type) { - case FieldTypeStr: return false; - case FieldTypeSignedInt: { - /* Wrap around depending on the number of bits (f->len) + case FieldTypeStr: + return false; + case FieldTypeSignedInt: { + /* Wrap around depending on the number of bits (f->len) * the integer was declared to have. */ - int64_t max = (1ULL << (f->len-1))-1; - int64_t min = -max-1; - int64_t v = (int64_t)f->value + incr; - if (v > max) v = min+(v-max-1); - if (v < min) v = max+(v-min+1); - f->value = v; - break; - } - case FieldTypeBinary: - case FieldTypeHex: - case FieldTypeUnsignedInt: { - /* Wrap around like for the unsigned case, but here + int64_t max = (1ULL << (f->len - 1)) - 1; + int64_t min = -max - 1; + int64_t v = (int64_t)f->value + incr; + if(v > max) v = min + (v - max - 1); + if(v < min) v = max + (v - min + 1); + f->value = v; + break; + } + case FieldTypeBinary: + case FieldTypeHex: + case FieldTypeUnsignedInt: { + /* Wrap around like for the unsigned case, but here * is simpler. */ - uint64_t max = (1ULL << f->len)-1; // Broken for 64 bits. - uint64_t uv = (uint64_t)f->value + incr; - if (uv > max) uv = uv & max; - f->uvalue = uv; - break; - } - case FieldTypeFloat: - f->fvalue += incr; - break; - case FieldTypeBytes: { - // For bytes we only support single unit increments. - if (incr != -1 && incr != 1) return false; - for (int j = f->len-1; j >= 0; j--) { - uint8_t nibble = (j&1) ? (f->bytes[j/2] & 0x0F) : - ((f->bytes[j/2] & 0xF0) >> 4); + uint64_t max = (1ULL << f->len) - 1; // Broken for 64 bits. + uint64_t uv = (uint64_t)f->value + incr; + if(uv > max) uv = uv & max; + f->uvalue = uv; + break; + } + case FieldTypeFloat: + f->fvalue += incr; + break; + case FieldTypeBytes: { + // For bytes we only support single unit increments. + if(incr != -1 && incr != 1) return false; + for(int j = f->len - 1; j >= 0; j--) { + uint8_t nibble = (j & 1) ? (f->bytes[j / 2] & 0x0F) : ((f->bytes[j / 2] & 0xF0) >> 4); - nibble += incr; - nibble &= 0x0F; + nibble += incr; + nibble &= 0x0F; - f->bytes[j/2] = (j&1) ? ((f->bytes[j/2] & 0xF0) | nibble) : - ((f->bytes[j/2] & 0x0F) | (nibble<<4)); + f->bytes[j / 2] = (j & 1) ? ((f->bytes[j / 2] & 0xF0) | nibble) : + ((f->bytes[j / 2] & 0x0F) | (nibble << 4)); - /* Propagate the operation on overflow of this nibble. */ - if ((incr == 1 && nibble == 0) || - (incr == -1 && nibble == 0xf)) - { - continue; - } - break; // Otherwise stop the loop here. + /* Propagate the operation on overflow of this nibble. */ + if((incr == 1 && nibble == 0) || (incr == -1 && nibble == 0xf)) { + continue; } - break; + break; // Otherwise stop the loop here. } + break; + } } return true; } - /* Free a field set and its contained fields. */ -void fieldset_free(ProtoViewFieldSet *fs) { - for (uint32_t j = 0; j < fs->numfields; j++) - field_free(fs->fields[j]); +void fieldset_free(ProtoViewFieldSet* fs) { + for(uint32_t j = 0; j < fs->numfields; j++) field_free(fs->fields[j]); free(fs->fields); free(fs); } /* Allocate and init an empty field set. */ -ProtoViewFieldSet *fieldset_new(void) { - ProtoViewFieldSet *fs = malloc(sizeof(*fs)); +ProtoViewFieldSet* fieldset_new(void) { + ProtoViewFieldSet* fs = malloc(sizeof(*fs)); fs->numfields = 0; fs->fields = NULL; return fs; } /* Append an already allocated field at the end of the specified field set. */ -static void fieldset_add_field(ProtoViewFieldSet *fs, ProtoViewField *field) { +static void fieldset_add_field(ProtoViewFieldSet* fs, ProtoViewField* field) { fs->numfields++; - fs->fields = realloc(fs->fields,sizeof(ProtoViewField*)*fs->numfields); - fs->fields[fs->numfields-1] = field; + fs->fields = realloc(fs->fields, sizeof(ProtoViewField*) * fs->numfields); + fs->fields[fs->numfields - 1] = field; } /* Allocate and append an integer field. */ -void fieldset_add_int(ProtoViewFieldSet *fs, const char *name, int64_t val, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeSignedInt,name); +void fieldset_add_int(ProtoViewFieldSet* fs, const char* name, int64_t val, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeSignedInt, name); f->value = val; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append an unsigned field. */ -void fieldset_add_uint(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeUnsignedInt,name); +void fieldset_add_uint(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeUnsignedInt, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a hex field. This is an unsigned number but * with an hex representation. */ -void fieldset_add_hex(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeHex,name); +void fieldset_add_hex(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeHex, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bin field. This is an unsigned number but * with a binary representation. */ -void fieldset_add_bin(ProtoViewFieldSet *fs, const char *name, uint64_t uval, uint8_t bits) { - ProtoViewField *f = field_new(FieldTypeBinary,name); +void fieldset_add_bin(ProtoViewFieldSet* fs, const char* name, uint64_t uval, uint8_t bits) { + ProtoViewField* f = field_new(FieldTypeBinary, name); f->uvalue = uval; f->len = bits; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a string field. */ -void fieldset_add_str(ProtoViewFieldSet *fs, const char *name, const char *s) { - ProtoViewField *f = field_new(FieldTypeStr,name); +void fieldset_add_str(ProtoViewFieldSet* fs, const char* name, const char* s) { + ProtoViewField* f = field_new(FieldTypeStr, name); f->str = strdup(s); f->len = strlen(s); - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a bytes field. Note that 'count' is specified in * nibbles (bytes*2). */ -void fieldset_add_bytes(ProtoViewFieldSet *fs, const char *name, const uint8_t *bytes, uint32_t count_nibbles) { - uint32_t numbytes = (count_nibbles+count_nibbles%2)/2; - ProtoViewField *f = field_new(FieldTypeBytes,name); +void fieldset_add_bytes( + ProtoViewFieldSet* fs, + const char* name, + const uint8_t* bytes, + uint32_t count_nibbles) { + uint32_t numbytes = (count_nibbles + count_nibbles % 2) / 2; + ProtoViewField* f = field_new(FieldTypeBytes, name); f->bytes = malloc(numbytes); - memcpy(f->bytes,bytes,numbytes); + memcpy(f->bytes, bytes, numbytes); f->len = count_nibbles; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* Allocate and append a float field. */ -void fieldset_add_float(ProtoViewFieldSet *fs, const char *name, float val, uint32_t digits_after_dot) { - ProtoViewField *f = field_new(FieldTypeFloat,name); +void fieldset_add_float( + ProtoViewFieldSet* fs, + const char* name, + float val, + uint32_t digits_after_dot) { + ProtoViewField* f = field_new(FieldTypeFloat, name); f->fvalue = val; f->len = digits_after_dot; - fieldset_add_field(fs,f); + fieldset_add_field(fs, f); } /* For each field of the destination filedset 'dst', look for a matching * field name/type in the source fieldset 'src', and if one is found copy * its value into the 'dst' field. */ -void fieldset_copy_matching_fields(ProtoViewFieldSet *dst, - ProtoViewFieldSet *src) -{ - for (uint32_t j = 0; j < dst->numfields; j++) { - for (uint32_t i = 0; i < src->numfields; i++) { - if (dst->fields[j]->type == src->fields[i]->type && - !strcmp(dst->fields[j]->name,src->fields[i]->name)) - { - field_set_from_field(dst->fields[j], - src->fields[i]); +void fieldset_copy_matching_fields(ProtoViewFieldSet* dst, ProtoViewFieldSet* src) { + for(uint32_t j = 0; j < dst->numfields; j++) { + for(uint32_t i = 0; i < src->numfields; i++) { + if(dst->fields[j]->type == src->fields[i]->type && + !strcmp(dst->fields[j]->name, src->fields[i]->name)) { + field_set_from_field(dst->fields[j], src->fields[i]); } } } diff --git a/applications/plugins/protoview/protocols/b4b1.c b/applications/plugins/protoview/protocols/b4b1.c index 7308d1211..52c59d24b 100644 --- a/applications/plugins/protoview/protocols/b4b1.c +++ b/applications/plugins/protoview/protocols/b4b1.c @@ -9,9 +9,9 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - if (numbits < 30) return false; - const char *sync_patterns[3] = { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits < 30) return false; + const char* sync_patterns[3] = { "10000000000000000000000000000001", /* 30 zero bits. */ "100000000000000000000000000000001", /* 31 zero bits. */ "1000000000000000000000000000000001", /* 32 zero bits. */ @@ -19,70 +19,67 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint32_t off; int j; - for (j = 0; j < 3; j++) { - off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_patterns[j]); - if (off != BITMAP_SEEK_NOT_FOUND) break; + for(j = 0; j < 3; j++) { + off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_patterns[j]); + if(off != BITMAP_SEEK_NOT_FOUND) break; } - if (off == BITMAP_SEEK_NOT_FOUND) return false; - if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu",off); + if(off == BITMAP_SEEK_NOT_FOUND) return false; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 preamble at: %lu", off); info->start_off = off; // Seek data setction. Why -1? Last bit is data. - off += strlen(sync_patterns[j])-1; + off += strlen(sync_patterns[j]) - 1; uint8_t d[3]; /* 24 bits of data. */ - uint32_t decoded = - convert_from_line_code(d,sizeof(d),bits,numbytes,off,"1000","1110"); + uint32_t decoded = convert_from_line_code(d, sizeof(d), bits, numbytes, off, "1000", "1110"); - if (DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu",decoded); - if (decoded < 24) return false; + if(DEBUG_MSG) FURI_LOG_E(TAG, "B4B1 decoded: %lu", decoded); + if(decoded < 24) return false; - off += 24*4; // seek to end symbol offset to calculate the length. + off += 24 * 4; // seek to end symbol offset to calculate the length. off++; // In this protocol there is a final pulse as terminator. info->pulses_count = off - info->start_off; - fieldset_add_bytes(info->fieldset,"id",d,5); - fieldset_add_uint(info->fieldset,"button",d[2]&0xf,4); + fieldset_add_bytes(info->fieldset, "id", d, 5); + fieldset_add_uint(info->fieldset, "button", d[2] & 0xf, 4); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xE0}; - fieldset_add_bytes(fieldset,"id",default_id,5); - fieldset_add_uint(fieldset,"button",1,4); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xE0}; + fieldset_add_bytes(fieldset, "id", default_id, 5); + fieldset_add_uint(fieldset, "button", 1, 4); } /* Create a signal. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fs) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fs) { uint32_t te = 334; // Short pulse duration in microseconds. // Sync: 1 te pulse, 31 te gap. - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*31); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 31); // ID + button state uint8_t data[3]; - memcpy(data,fs->fields[0]->bytes,3); - data[2] = (data[2]&0xF0) | (fs->fields[1]->uvalue & 0xF); - for (uint32_t j = 0; j < 24; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add(samples,true,te*3); - raw_samples_add(samples,false,te); + memcpy(data, fs->fields[0]->bytes, 3); + data[2] = (data[2] & 0xF0) | (fs->fields[1]->uvalue & 0xF); + for(uint32_t j = 0; j < 24; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add(samples, true, te * 3); + raw_samples_add(samples, false, te); } else { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*3); + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 3); } } // Signal terminator. Just a single short pulse. - raw_samples_add(samples,true,te); + raw_samples_add(samples, true, te); } ProtoViewDecoder B4B1Decoder = { .name = "PT/SC remote", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/keeloq.c b/applications/plugins/protoview/protocols/keeloq.c index 0741eac47..298c690d4 100644 --- a/applications/plugins/protoview/protocols/keeloq.c +++ b/applications/plugins/protoview/protocols/keeloq.c @@ -24,16 +24,16 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { /* In the sync pattern, we require the 12 high/low pulses and at least * half the gap we expect (5 pulses times, one is the final zero in the * 24 symbols high/low sequence, then other 4). */ - const char *sync_pattern = "101010101010101010101010" "0000"; - uint8_t sync_len = 24+4; - if (numbits-sync_len+sync_len < 3*66) return false; - uint32_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "101010101010101010101010" + "0000"; + uint8_t sync_len = 24 + 4; + if(numbits - sync_len + sync_len < 3 * 66) return false; + uint32_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; info->start_off = off; off += sync_len; // Seek start of message. @@ -42,84 +42,77 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * symbols of gap, to avoid missing the signal for a matter of wrong * timing. */ uint8_t gap_len = 0; - while(gap_len <= 7 && bitmap_get(bits,numbytes,off+gap_len) == 0) - gap_len++; - if (gap_len < 3 || gap_len > 7) return false; + while(gap_len <= 7 && bitmap_get(bits, numbytes, off + gap_len) == 0) gap_len++; + if(gap_len < 3 || gap_len > 7) return false; off += gap_len; FURI_LOG_E(TAG, "Keeloq preamble+sync found"); uint8_t raw[9] = {0}; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "110","100"); /* Pulse width modulation. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "110", "100"); /* Pulse width modulation. */ FURI_LOG_E(TAG, "Keeloq decoded bits: %lu", decoded); - if (decoded < 66) return false; /* Require the full 66 bits. */ + if(decoded < 66) return false; /* Require the full 66 bits. */ - info->pulses_count = (off+66*3) - info->start_off; + info->pulses_count = (off + 66 * 3) - info->start_off; - bitmap_reverse_bytes_bits(raw,sizeof(raw)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(raw, sizeof(raw)); /* Keeloq is LSB first. */ - int buttons = raw[7]>>4; - int lowbat = (raw[8]&0x1) == 0; // Actual bit meaning: good battery level - int alwaysone = (raw[8]&0x2) != 0; + int buttons = raw[7] >> 4; + int lowbat = (raw[8] & 0x1) == 0; // Actual bit meaning: good battery level + int alwaysone = (raw[8] & 0x2) != 0; - fieldset_add_bytes(info->fieldset,"encr",raw,8); - raw[7] = raw[7]<<4; // Make ID bits contiguous - fieldset_add_bytes(info->fieldset,"id",raw+4,7); // 28 bits, 7 nibbles - fieldset_add_bin(info->fieldset,"s[2,1,0,3]",buttons,4); - fieldset_add_bin(info->fieldset,"low battery",lowbat,1); - fieldset_add_bin(info->fieldset,"always one",alwaysone,1); + fieldset_add_bytes(info->fieldset, "encr", raw, 8); + raw[7] = raw[7] << 4; // Make ID bits contiguous + fieldset_add_bytes(info->fieldset, "id", raw + 4, 7); // 28 bits, 7 nibbles + fieldset_add_bin(info->fieldset, "s[2,1,0,3]", buttons, 4); + fieldset_add_bin(info->fieldset, "low battery", lowbat, 1); + fieldset_add_bin(info->fieldset, "always one", alwaysone, 1); return true; } -static void get_fields(ProtoViewFieldSet *fieldset) { +static void get_fields(ProtoViewFieldSet* fieldset) { uint8_t remote_id[4] = {0xab, 0xcd, 0xef, 0xa0}; uint8_t encr[4] = {0xab, 0xab, 0xab, 0xab}; - fieldset_add_bytes(fieldset,"encr",encr,8); - fieldset_add_bytes(fieldset,"id",remote_id,7); - fieldset_add_bin(fieldset,"s[2,1,0,3]",2,4); - fieldset_add_bin(fieldset,"low battery",0,1); - fieldset_add_bin(fieldset,"always one",1,1); + fieldset_add_bytes(fieldset, "encr", encr, 8); + fieldset_add_bytes(fieldset, "id", remote_id, 7); + fieldset_add_bin(fieldset, "s[2,1,0,3]", 2, 4); + fieldset_add_bin(fieldset, "low battery", 0, 1); + fieldset_add_bin(fieldset, "always one", 1, 1); } -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 380; // Short pulse duration in microseconds. // Sync: 12 pairs of pulse/gap + 9 times gap - for (int j = 0; j < 12; j++) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te); + for(int j = 0; j < 12; j++) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te); } - raw_samples_add(samples,false,te*9); + raw_samples_add(samples, false, te * 9); // Data, 66 bits. uint8_t data[9] = {0}; - memcpy(data,fieldset->fields[0]->bytes,4); // Encrypted part. - memcpy(data+4,fieldset->fields[1]->bytes,4); // ID. - data[7] = data[7]>>4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] + memcpy(data, fieldset->fields[0]->bytes, 4); // Encrypted part. + memcpy(data + 4, fieldset->fields[1]->bytes, 4); // ID. + data[7] = data[7] >> 4 | fieldset->fields[2]->uvalue << 4; // s[2,1,0,3] int low_battery = fieldset->fields[3] != 0; int always_one = fieldset->fields[4] != 0; low_battery = !low_battery; // Bit real meaning is good battery level. data[8] |= low_battery; data[8] |= (always_one << 1); - bitmap_reverse_bytes_bits(data,sizeof(data)); /* Keeloq is LSB first. */ + bitmap_reverse_bytes_bits(data, sizeof(data)); /* Keeloq is LSB first. */ - for (int j = 0; j < 66; j++) { - if (bitmap_get(data,9,j)) { - raw_samples_add(samples,true,te); - raw_samples_add(samples,false,te*2); + for(int j = 0; j < 66; j++) { + if(bitmap_get(data, 9, j)) { + raw_samples_add(samples, true, te); + raw_samples_add(samples, false, te * 2); } else { - raw_samples_add(samples,true,te*2); - raw_samples_add(samples,false,te); + raw_samples_add(samples, true, te * 2); + raw_samples_add(samples, false, te); } } } -ProtoViewDecoder KeeloqDecoder = { - .name = "Keeloq", - .decode = decode, - .get_fields = get_fields, - .build_message = build_message -}; +ProtoViewDecoder KeeloqDecoder = + {.name = "Keeloq", .decode = decode, .get_fields = get_fields, .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/oregon2.c b/applications/plugins/protoview/protocols/oregon2.c index 1d909a504..f67e85a2d 100644 --- a/applications/plugins/protoview/protocols/oregon2.c +++ b/applications/plugins/protoview/protocols/oregon2.c @@ -6,11 +6,14 @@ #include "../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - if (numbits < 32) return false; - const char *sync_pattern = "01100110" "01100110" "10010110" "10010110"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits < 32) return false; + const char* sync_pattern = "01100110" + "01100110" + "10010110" + "10010110"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Oregon2 preamble+sync found"); info->start_off = off; @@ -18,50 +21,61 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t buffer[8], raw[8] = {0}; uint32_t decoded = - convert_from_line_code(buffer,sizeof(buffer),bits,numbytes,off,"1001","0110"); + convert_from_line_code(buffer, sizeof(buffer), bits, numbytes, off, "1001", "0110"); FURI_LOG_E(TAG, "Oregon2 decoded bits: %lu", decoded); - if (decoded < 11*4) return false; /* Minimum len to extract some data. */ - info->pulses_count = (off+11*4*4) - info->start_off; + if(decoded < 11 * 4) return false; /* Minimum len to extract some data. */ + info->pulses_count = (off + 11 * 4 * 4) - info->start_off; char temp[3] = {0}, hum[2] = {0}; uint8_t deviceid[2]; - for (int j = 0; j < 64; j += 4) { + for(int j = 0; j < 64; j += 4) { uint8_t nib[1]; - nib[0] = (bitmap_get(buffer,8,j+0) | - bitmap_get(buffer,8,j+1) << 1 | - bitmap_get(buffer,8,j+2) << 2 | - bitmap_get(buffer,8,j+3) << 3); - if (DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j/4, (unsigned int)nib[0]); - raw[j/8] |= nib[0] << (4-(j%4)); - switch(j/4) { - case 1: deviceid[0] |= nib[0]; break; - case 0: deviceid[0] |= nib[0] << 4; break; - case 3: deviceid[1] |= nib[0]; break; - case 2: deviceid[1] |= nib[0] << 4; break; - case 10: temp[0] = nib[0]; break; + nib[0] = + (bitmap_get(buffer, 8, j + 0) | bitmap_get(buffer, 8, j + 1) << 1 | + bitmap_get(buffer, 8, j + 2) << 2 | bitmap_get(buffer, 8, j + 3) << 3); + if(DEBUG_MSG) FURI_LOG_E(TAG, "Not inverted nibble[%d]: %x", j / 4, (unsigned int)nib[0]); + raw[j / 8] |= nib[0] << (4 - (j % 4)); + switch(j / 4) { + case 1: + deviceid[0] |= nib[0]; + break; + case 0: + deviceid[0] |= nib[0] << 4; + break; + case 3: + deviceid[1] |= nib[0]; + break; + case 2: + deviceid[1] |= nib[0] << 4; + break; + case 10: + temp[0] = nib[0]; + break; /* Fixme: take the temperature sign from nibble 11. */ - case 9: temp[1] = nib[0]; break; - case 8: temp[2] = nib[0]; break; - case 13: hum[0] = nib[0]; break; - case 12: hum[1] = nib[0]; break; + case 9: + temp[1] = nib[0]; + break; + case 8: + temp[2] = nib[0]; + break; + case 13: + hum[0] = nib[0]; + break; + case 12: + hum[1] = nib[0]; + break; } } - float tempval = ((temp[0]-'0')*10) + - (temp[1]-'0') + - ((float)(temp[2]-'0')*0.1); - int humval = (hum[0]-'0')*10 + (hum[1]-'0'); + float tempval = ((temp[0] - '0') * 10) + (temp[1] - '0') + ((float)(temp[2] - '0') * 0.1); + int humval = (hum[0] - '0') * 10 + (hum[1] - '0'); - fieldset_add_bytes(info->fieldset,"Sensor ID",deviceid,4); - fieldset_add_float(info->fieldset,"Temperature",tempval,1); - fieldset_add_uint(info->fieldset,"Humidity",humval,7); + fieldset_add_bytes(info->fieldset, "Sensor ID", deviceid, 4); + fieldset_add_float(info->fieldset, "Temperature", tempval, 1); + fieldset_add_uint(info->fieldset, "Humidity", humval, 7); return true; } -ProtoViewDecoder Oregon2Decoder = { - .name = "Oregon2", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder Oregon2Decoder = + {.name = "Oregon2", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/citroen.c b/applications/plugins/protoview/protocols/tpms/citroen.c index d8a1681e4..ecd8fb983 100644 --- a/applications/plugins/protoview/protocols/tpms/citroen.c +++ b/applications/plugins/protoview/protocols/tpms/citroen.c @@ -7,55 +7,49 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { /* We consider a preamble of 17 symbols. They are more, but the decoding * is more likely to happen if we don't pretend to receive from the * very start of the message. */ uint32_t sync_len = 17; - const char *sync_pattern = "10101010101010110"; - if (numbits-sync_len < 8*10) return false; /* Expect 10 bytes. */ + const char* sync_pattern = "10101010101010110"; + if(numbits - sync_len < 8 * 10) return false; /* Expect 10 bytes. */ - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); info->start_off = off; off += sync_len; /* Skip preamble + sync. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Citroen TPMS decoded bits: %lu", decoded); - if (decoded < 8*10) return false; /* Require the full 10 bytes. */ + if(decoded < 8 * 10) return false; /* Require the full 10 bytes. */ /* Check the CRC. It's a simple XOR of bytes 1-9, the first byte * is not included. The meaning of the first byte is unknown and * we don't display it. */ uint8_t crc = 0; - for (int j = 1; j < 10; j++) crc ^= raw[j]; - if (crc != 0) return false; /* Require sane checksum. */ + for(int j = 1; j < 10; j++) crc ^= raw[j]; + if(crc != 0) return false; /* Require sane checksum. */ - info->pulses_count = (off+8*10*2) - info->start_off; + info->pulses_count = (off + 8 * 10 * 2) - info->start_off; int repeat = raw[5] & 0xf; - float kpa = (float)raw[6]*1.364; - int temp = raw[7]-50; + float kpa = (float)raw[6] * 1.364; + int temp = raw[7] - 50; int battery = raw[8]; /* This may be the battery. It's not clear. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+1,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_uint(info->fieldset,"Repeat",repeat,4); - fieldset_add_uint(info->fieldset,"Battery",battery,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 1, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_uint(info->fieldset, "Repeat", repeat, 4); + fieldset_add_uint(info->fieldset, "Battery", battery, 8); return true; } -ProtoViewDecoder CitroenTPMSDecoder = { - .name = "Citroen TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder CitroenTPMSDecoder = + {.name = "Citroen TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/ford.c b/applications/plugins/protoview/protocols/tpms/ford.c index abdb692ee..3816e72f9 100644 --- a/applications/plugins/protoview/protocols/tpms/ford.c +++ b/applications/plugins/protoview/protocols/tpms/ford.c @@ -10,54 +10,49 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "010101010101" + "0110"; + uint8_t sync_len = 12 + 4; /* We just use 12 preamble symbols + sync. */ + if(numbits - sync_len < 8 * 8) return false; - const char *sync_pattern = "010101010101" "0110"; - uint8_t sync_len = 12+4; /* We just use 12 preamble symbols + sync. */ - if (numbits-sync_len < 8*8) return false; - - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Fort TPMS preamble+sync found"); info->start_off = off; off += sync_len; /* Skip preamble and sync. */ uint8_t raw[8]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Ford TPMS decoded bits: %lu", decoded); - if (decoded < 8*8) return false; /* Require the full 8 bytes. */ + if(decoded < 8 * 8) return false; /* Require the full 8 bytes. */ /* CRC is just the sum of the first 7 bytes MOD 256. */ uint8_t crc = 0; - for (int j = 0; j < 7; j++) crc += raw[j]; - if (crc != raw[7]) return false; /* Require sane CRC. */ + for(int j = 0; j < 7; j++) crc += raw[j]; + if(crc != raw[7]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float psi = 0.25 * (((raw[6]&0x20)<<3)|raw[4]); + float psi = 0.25 * (((raw[6] & 0x20) << 3) | raw[4]); /* Temperature apperas to be valid only if the most significant * bit of the value is not set. Otherwise its meaning is unknown. * Likely useful to alternatively send temperature or other info. */ - int temp = raw[5] & 0x80 ? 0 : raw[5]-56; + int temp = raw[5] & 0x80 ? 0 : raw[5] - 56; int flags = raw[5] & 0x7f; int car_moving = (raw[6] & 0x44) == 0x44; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,7); - fieldset_add_uint(info->fieldset,"Moving",car_moving,1); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 7); + fieldset_add_uint(info->fieldset, "Moving", car_moving, 1); return true; } -ProtoViewDecoder FordTPMSDecoder = { - .name = "Ford TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder FordTPMSDecoder = + {.name = "Ford TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/renault.c b/applications/plugins/protoview/protocols/tpms/renault.c index 09de77d17..3d8fc43d5 100644 --- a/applications/plugins/protoview/protocols/tpms/renault.c +++ b/applications/plugins/protoview/protocols/tpms/renault.c @@ -6,85 +6,82 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = +static const char* test_vector = "...01010101010101010110" // Preamble + sync /* The following is Marshal encoded, so each two characters are * actaully one bit. 01 = 0, 10 = 1. */ "010110010110" // Flags. "10011001101010011001" // Pressure, multiply by 0.75 to obtain kpa. - // 244 kpa here. - "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. + // 244 kpa here. + "1010010110011010" // Temperature, subtract 30 to obtain celsius. 22C here. "1001010101101001" "0101100110010101" - "1001010101100110" // Tire ID. 0x7AD779 here. + "1001010101100110" // Tire ID. 0x7AD779 here. "0101010101010101" - "0101010101010101" // Two FF bytes (usually). Unknown. + "0101010101010101" // Two FF bytes (usually). Unknown. "0110010101010101"; // CRC8 with (poly 7, initialization 0). -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ - bitmap_set_pattern(bits,numbytes,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits-12 < 9*8) return false; + if(numbits - 12 < 9 * 8) return false; - const char *sync_pattern = "01010101010101010110"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "01010101010101010110"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Renault TPMS preamble+sync found"); info->start_off = off; off += 20; /* Skip preamble. */ uint8_t raw[9]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester. */ FURI_LOG_E(TAG, "Renault TPMS decoded bits: %lu", decoded); - if (decoded < 8*9) return false; /* Require the full 9 bytes. */ - if (crc8(raw,8,0,7) != raw[8]) return false; /* Require sane CRC. */ + if(decoded < 8 * 9) return false; /* Require the full 9 bytes. */ + if(crc8(raw, 8, 0, 7) != raw[8]) return false; /* Require sane CRC. */ - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - uint8_t flags = raw[0]>>2; - float kpa = 0.75 * ((uint32_t)((raw[0]&3)<<8) | raw[1]); - int temp = raw[2]-30; + uint8_t flags = raw[0] >> 2; + float kpa = 0.75 * ((uint32_t)((raw[0] & 3) << 8) | raw[1]); + int temp = raw[2] - 30; - fieldset_add_bytes(info->fieldset,"Tire ID",raw+3,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); - fieldset_add_hex(info->fieldset,"Flags",flags,6); - fieldset_add_bytes(info->fieldset,"Unknown1",raw+6,2); - fieldset_add_bytes(info->fieldset,"Unknown2",raw+7,2); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 3, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); + fieldset_add_hex(info->fieldset, "Flags", flags, 6); + fieldset_add_bytes(info->fieldset, "Unknown1", raw + 6, 2); + fieldset_add_bytes(info->fieldset, "Unknown2", raw + 7, 2); return true; } /* Give fields and defaults for the signal creator. */ -static void get_fields(ProtoViewFieldSet *fieldset) { - uint8_t default_id[3]= {0xAB, 0xCD, 0xEF}; - fieldset_add_bytes(fieldset,"Tire ID",default_id,3*2); - fieldset_add_float(fieldset,"Pressure kpa",123,2); - fieldset_add_int(fieldset,"Temperature C",20,8); +static void get_fields(ProtoViewFieldSet* fieldset) { + uint8_t default_id[3] = {0xAB, 0xCD, 0xEF}; + fieldset_add_bytes(fieldset, "Tire ID", default_id, 3 * 2); + fieldset_add_float(fieldset, "Pressure kpa", 123, 2); + fieldset_add_int(fieldset, "Temperature C", 20, 8); // We don't know what flags are, but 1B is a common value. - fieldset_add_hex(fieldset,"Flags",0x1b,6); - fieldset_add_bytes(fieldset,"Unknown1",(uint8_t*)"\xff",2); - fieldset_add_bytes(fieldset,"Unknown2",(uint8_t*)"\xff",2); + fieldset_add_hex(fieldset, "Flags", 0x1b, 6); + fieldset_add_bytes(fieldset, "Unknown1", (uint8_t*)"\xff", 2); + fieldset_add_bytes(fieldset, "Unknown2", (uint8_t*)"\xff", 2); } /* Create a Renault TPMS signal, according to the fields provided. */ -static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset) -{ +static void build_message(RawSamplesBuffer* samples, ProtoViewFieldSet* fieldset) { uint32_t te = 50; // Short pulse duration in microseconds. // Preamble + sync - const char *psync = "01010101010101010101010101010110"; - const char *p = psync; + const char* psync = "01010101010101010101010101010110"; + const char* p = psync; while(*p) { - raw_samples_add_or_update(samples,*p == '1',te); + raw_samples_add_or_update(samples, *p == '1', te); p++; } @@ -93,21 +90,21 @@ static void build_message(RawSamplesBuffer *samples, ProtoViewFieldSet *fieldset unsigned int raw_pressure = fieldset->fields[1]->fvalue * 4 / 3; data[0] = fieldset->fields[3]->uvalue << 2; // Flags data[0] |= (raw_pressure >> 8) & 3; // Pressure kpa high 2 bits - data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits + data[1] = raw_pressure & 0xff; // Pressure kpa low 8 bits data[2] = fieldset->fields[2]->value + 30; // Temperature C - memcpy(data+3,fieldset->fields[0]->bytes,6); // ID, 24 bits. - data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 - data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 - data[8] = crc8(data,8,0,7); + memcpy(data + 3, fieldset->fields[0]->bytes, 6); // ID, 24 bits. + data[6] = fieldset->fields[4]->bytes[0]; // Unknown 1 + data[7] = fieldset->fields[5]->bytes[0]; // Unknown 2 + data[8] = crc8(data, 8, 0, 7); // Generate Manchester code for each bit - for (uint32_t j = 0; j < 9*8; j++) { - if (bitmap_get(data,sizeof(data),j)) { - raw_samples_add_or_update(samples,true,te); - raw_samples_add_or_update(samples,false,te); + for(uint32_t j = 0; j < 9 * 8; j++) { + if(bitmap_get(data, sizeof(data), j)) { + raw_samples_add_or_update(samples, true, te); + raw_samples_add_or_update(samples, false, te); } else { - raw_samples_add_or_update(samples,false,te); - raw_samples_add_or_update(samples,true,te); + raw_samples_add_or_update(samples, false, te); + raw_samples_add_or_update(samples, true, te); } } } @@ -116,5 +113,4 @@ ProtoViewDecoder RenaultTPMSDecoder = { .name = "Renault TPMS", .decode = decode, .get_fields = get_fields, - .build_message = build_message -}; + .build_message = build_message}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader.c b/applications/plugins/protoview/protocols/tpms/schrader.c index ae25e39bb..7dc85a2cb 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader.c +++ b/applications/plugins/protoview/protocols/tpms/schrader.c @@ -11,20 +11,21 @@ #include "../../app.h" #define USE_TEST_VECTOR 0 -static const char *test_vector = "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; +static const char* test_vector = + "000000111101010101011010010110010110101001010110100110011001100101010101011010100110100110011010101010101010101010101010101010101010101010101010"; -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ - bitmap_set_pattern(bits,numbytes,0,test_vector); +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(USE_TEST_VECTOR) { /* Test vector to check that decoding works. */ + bitmap_set_pattern(bits, numbytes, 0, test_vector); numbits = strlen(test_vector); } - if (numbits < 64) return false; /* Preamble + data. */ + if(numbits < 64) return false; /* Preamble + data. */ - const char *sync_pattern = "1111010101" "01011010"; - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + const char* sync_pattern = "1111010101" + "01011010"; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Schrader TPMS gap+preamble found"); info->start_off = off; @@ -34,38 +35,33 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView uint8_t raw[8]; uint8_t id[4]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader TPMS decoded bits: %lu", decoded); - if (decoded < 64) return false; /* Require the full 8 bytes. */ + if(decoded < 64) return false; /* Require the full 8 bytes. */ raw[0] |= 0xf0; // Fix the preamble nibble for checksum computation. - uint8_t cksum = crc8(raw,sizeof(raw)-1,0xf0,0x7); - if (cksum != raw[7]) { + uint8_t cksum = crc8(raw, sizeof(raw) - 1, 0xf0, 0x7); + if(cksum != raw[7]) { FURI_LOG_E(TAG, "Schrader TPMS checksum mismatch"); return false; } - info->pulses_count = (off+8*8*2) - info->start_off; + info->pulses_count = (off + 8 * 8 * 2) - info->start_off; - float kpa = (float)raw[5]*2.5; - int temp = raw[6]-50; - id[0] = raw[1]&7; + float kpa = (float)raw[5] * 2.5; + int temp = raw[6] - 50; + id[0] = raw[1] & 7; id[1] = raw[2]; id[2] = raw[3]; id[3] = raw[4]; - fieldset_add_bytes(info->fieldset,"Tire ID",id,4*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", id, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder SchraderTPMSDecoder = { - .name = "Schrader TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderTPMSDecoder = + {.name = "Schrader TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c index 0105010bd..45accf1a1 100644 --- a/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c +++ b/applications/plugins/protoview/protocols/tpms/schrader_eg53ma4.c @@ -15,50 +15,45 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + const char* sync_pattern = "010101010101" + "01100101"; + uint8_t sync_len = 12 + 8; /* We just use 12 preamble symbols + sync. */ + if(numbits - sync_len + 8 < 8 * 10) return false; - const char *sync_pattern = "010101010101" "01100101"; - uint8_t sync_len = 12+8; /* We just use 12 preamble symbols + sync. */ - if (numbits-sync_len+8 < 8*10) return false; - - uint64_t off = bitmap_seek_bits(bits,numbytes,0,numbits,sync_pattern); - if (off == BITMAP_SEEK_NOT_FOUND) return false; + uint64_t off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync_pattern); + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS preamble+sync found"); info->start_off = off; - off += sync_len-8; /* Skip preamble, not sync that is part of the data. */ + off += sync_len - 8; /* Skip preamble, not sync that is part of the data. */ uint8_t raw[10]; - uint32_t decoded = - convert_from_line_code(raw,sizeof(raw),bits,numbytes,off, - "01","10"); /* Manchester code. */ + uint32_t decoded = convert_from_line_code( + raw, sizeof(raw), bits, numbytes, off, "01", "10"); /* Manchester code. */ FURI_LOG_E(TAG, "Schrader EG53MA4 TPMS decoded bits: %lu", decoded); - if (decoded < 10*8) return false; /* Require the full 10 bytes. */ + if(decoded < 10 * 8) return false; /* Require the full 10 bytes. */ /* CRC is just all bytes added mod 256. */ uint8_t crc = 0; - for (int j = 0; j < 9; j++) crc += raw[j]; - if (crc != raw[9]) return false; /* Require sane CRC. */ + for(int j = 0; j < 9; j++) crc += raw[j]; + if(crc != raw[9]) return false; /* Require sane CRC. */ - info->pulses_count = (off+10*8*2) - info->start_off; + info->pulses_count = (off + 10 * 8 * 2) - info->start_off; /* To convert the raw pressure to kPa, RTL433 uses 2.5, but is likely * wrong. Searching on Google for users experimenting with the value * reported, the value appears to be 2.75. */ - float kpa = (float)raw[7]*2.75; + float kpa = (float)raw[7] * 2.75; int temp_f = raw[8]; - int temp_c = (temp_f-32)*5/9; /* Convert Fahrenheit to Celsius. */ + int temp_c = (temp_f - 32) * 5 / 9; /* Convert Fahrenheit to Celsius. */ - fieldset_add_bytes(info->fieldset,"Tire ID",raw+4,3*2); - fieldset_add_float(info->fieldset,"Pressure kpa",kpa,2); - fieldset_add_int(info->fieldset,"Temperature C",temp_c,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw + 4, 3 * 2); + fieldset_add_float(info->fieldset, "Pressure kpa", kpa, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp_c, 8); return true; } -ProtoViewDecoder SchraderEG53MA4TPMSDecoder = { - .name = "Schrader EG53MA4 TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder SchraderEG53MA4TPMSDecoder = + {.name = "Schrader EG53MA4 TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/protocols/tpms/toyota.c b/applications/plugins/protoview/protocols/tpms/toyota.c index b9dd1d959..b80af7647 100644 --- a/applications/plugins/protoview/protocols/tpms/toyota.c +++ b/applications/plugins/protoview/protocols/tpms/toyota.c @@ -24,40 +24,33 @@ #include "../../app.h" -static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo *info) { - - if (numbits-6 < 64*2) return false; /* Ask for 64 bit of data (each bit +static bool decode(uint8_t* bits, uint32_t numbytes, uint32_t numbits, ProtoViewMsgInfo* info) { + if(numbits - 6 < 64 * 2) + return false; /* Ask for 64 bit of data (each bit is two symbols in the bitmap). */ - char *sync[] = { - "00111100", - "001111100", - "00111101", - "001111101", - NULL - }; + char* sync[] = {"00111100", "001111100", "00111101", "001111101", NULL}; int j; uint32_t off = 0; - for (j = 0; sync[j]; j++) { - off = bitmap_seek_bits(bits,numbytes,0,numbits,sync[j]); - if (off != BITMAP_SEEK_NOT_FOUND) { + for(j = 0; sync[j]; j++) { + off = bitmap_seek_bits(bits, numbytes, 0, numbits, sync[j]); + if(off != BITMAP_SEEK_NOT_FOUND) { info->start_off = off; - off += strlen(sync[j])-2; + off += strlen(sync[j]) - 2; break; - } + } } - if (off == BITMAP_SEEK_NOT_FOUND) return false; + if(off == BITMAP_SEEK_NOT_FOUND) return false; FURI_LOG_E(TAG, "Toyota TPMS sync[%s] found", sync[j]); uint8_t raw[9]; - uint32_t decoded = - convert_from_diff_manchester(raw,sizeof(raw),bits,numbytes,off,true); + uint32_t decoded = convert_from_diff_manchester(raw, sizeof(raw), bits, numbytes, off, true); FURI_LOG_E(TAG, "Toyota TPMS decoded bits: %lu", decoded); - if (decoded < 8*9) return false; /* Require the full 8 bytes. */ - if (crc8(raw,8,0x80,7) != raw[8]) return false; /* Require sane CRC. */ + if(decoded < 8 * 9) return false; /* Require the full 8 bytes. */ + if(crc8(raw, 8, 0x80, 7) != raw[8]) return false; /* Require sane CRC. */ /* We detected a valid signal. However now info->start_off is actually * pointing to the sync part, not the preamble of alternating 0 and 1. @@ -65,25 +58,21 @@ static bool decode(uint8_t *bits, uint32_t numbytes, uint32_t numbits, ProtoView * for the decoder itself to fix the signal if neeeded, so that its * logical representation will be more accurate and better to save * and retransmit. */ - if (info->start_off >= 12) { + if(info->start_off >= 12) { info->start_off -= 12; - bitmap_set_pattern(bits,numbytes,info->start_off,"010101010101"); + bitmap_set_pattern(bits, numbytes, info->start_off, "010101010101"); } - info->pulses_count = (off+8*9*2) - info->start_off; + info->pulses_count = (off + 8 * 9 * 2) - info->start_off; - float psi = (float)((raw[4]&0x7f)<<1 | raw[5]>>7) * 0.25 - 7; - int temp = ((raw[5]&0x7f)<<1 | raw[6]>>7) - 40; + float psi = (float)((raw[4] & 0x7f) << 1 | raw[5] >> 7) * 0.25 - 7; + int temp = ((raw[5] & 0x7f) << 1 | raw[6] >> 7) - 40; - fieldset_add_bytes(info->fieldset,"Tire ID",raw,4*2); - fieldset_add_float(info->fieldset,"Pressure psi",psi,2); - fieldset_add_int(info->fieldset,"Temperature C",temp,8); + fieldset_add_bytes(info->fieldset, "Tire ID", raw, 4 * 2); + fieldset_add_float(info->fieldset, "Pressure psi", psi, 2); + fieldset_add_int(info->fieldset, "Temperature C", temp, 8); return true; } -ProtoViewDecoder ToyotaTPMSDecoder = { - .name = "Toyota TPMS", - .decode = decode, - .get_fields = NULL, - .build_message = NULL -}; +ProtoViewDecoder ToyotaTPMSDecoder = + {.name = "Toyota TPMS", .decode = decode, .get_fields = NULL, .build_message = NULL}; diff --git a/applications/plugins/protoview/raw_samples.c b/applications/plugins/protoview/raw_samples.c index f83cca361..54773f43f 100644 --- a/applications/plugins/protoview/raw_samples.c +++ b/applications/plugins/protoview/raw_samples.c @@ -8,15 +8,15 @@ #include "raw_samples.h" /* Allocate and initialize a samples buffer. */ -RawSamplesBuffer *raw_samples_alloc(void) { - RawSamplesBuffer *buf = malloc(sizeof(*buf)); +RawSamplesBuffer* raw_samples_alloc(void) { + RawSamplesBuffer* buf = malloc(sizeof(*buf)); buf->mutex = furi_mutex_alloc(FuriMutexTypeNormal); raw_samples_reset(buf); return buf; } /* Free a sample buffer. Should be called when the mutex is released. */ -void raw_samples_free(RawSamplesBuffer *s) { +void raw_samples_free(RawSamplesBuffer* s) { furi_mutex_free(s->mutex); free(s); } @@ -24,27 +24,27 @@ void raw_samples_free(RawSamplesBuffer *s) { /* This just set all the samples to zero and also resets the internal * index. There is no need to call it after raw_samples_alloc(), but only * when one wants to reset the whole buffer of samples. */ -void raw_samples_reset(RawSamplesBuffer *s) { - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_reset(RawSamplesBuffer* s) { + furi_mutex_acquire(s->mutex, FuriWaitForever); s->total = RAW_SAMPLES_NUM; s->idx = 0; s->short_pulse_dur = 0; - memset(s->samples,0,sizeof(s->samples)); + memset(s->samples, 0, sizeof(s->samples)); furi_mutex_release(s->mutex); } /* Set the raw sample internal index so that what is currently at * offset 'offset', will appear to be at 0 index. */ -void raw_samples_center(RawSamplesBuffer *s, uint32_t offset) { - s->idx = (s->idx+offset) % RAW_SAMPLES_NUM; +void raw_samples_center(RawSamplesBuffer* s, uint32_t offset) { + s->idx = (s->idx + offset) % RAW_SAMPLES_NUM; } /* Add the specified sample in the circular buffer. */ -void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); s->samples[s->idx].level = level; s->samples[s->idx].dur = dur; - s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; furi_mutex_release(s->mutex); } @@ -56,28 +56,25 @@ void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur) { * * This function is a bit slower so the internal data sampling should * be performed with raw_samples_add(). */ -void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur) { - furi_mutex_acquire(s->mutex,FuriWaitForever); - uint32_t previdx = (s->idx-1) % RAW_SAMPLES_NUM; - if (s->samples[previdx].level == level && - s->samples[previdx].dur != 0) - { +void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); + uint32_t previdx = (s->idx - 1) % RAW_SAMPLES_NUM; + if(s->samples[previdx].level == level && s->samples[previdx].dur != 0) { /* Update the last sample: it has the same level. */ s->samples[previdx].dur += dur; } else { /* Add a new sample. */ s->samples[s->idx].level = level; s->samples[s->idx].dur = dur; - s->idx = (s->idx+1) % RAW_SAMPLES_NUM; + s->idx = (s->idx + 1) % RAW_SAMPLES_NUM; } furi_mutex_release(s->mutex); } /* Get the sample from the buffer. It is possible to use out of range indexes * as 'idx' because the modulo operation will rewind back from the start. */ -void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur) -{ - furi_mutex_acquire(s->mutex,FuriWaitForever); +void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur) { + furi_mutex_acquire(s->mutex, FuriWaitForever); idx = (s->idx + idx) % RAW_SAMPLES_NUM; *level = s->samples[idx].level; *dur = s->samples[idx].dur; @@ -85,12 +82,12 @@ void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *d } /* Copy one buffer to the other, including current index. */ -void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src) { - furi_mutex_acquire(src->mutex,FuriWaitForever); - furi_mutex_acquire(dst->mutex,FuriWaitForever); +void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src) { + furi_mutex_acquire(src->mutex, FuriWaitForever); + furi_mutex_acquire(dst->mutex, FuriWaitForever); dst->idx = src->idx; dst->short_pulse_dur = src->short_pulse_dur; - memcpy(dst->samples,src->samples,sizeof(dst->samples)); + memcpy(dst->samples, src->samples, sizeof(dst->samples)); furi_mutex_release(src->mutex); furi_mutex_release(dst->mutex); } diff --git a/applications/plugins/protoview/raw_samples.h b/applications/plugins/protoview/raw_samples.h index 0b0422025..3493f07fd 100644 --- a/applications/plugins/protoview/raw_samples.h +++ b/applications/plugins/protoview/raw_samples.h @@ -4,16 +4,17 @@ /* Our circular buffer of raw samples, used in order to display * the signal. */ -#define RAW_SAMPLES_NUM 2048 /* Use a power of two: we take the modulo +#define RAW_SAMPLES_NUM \ + 2048 /* Use a power of two: we take the modulo of the index quite often to normalize inside the range, and division is slow. */ typedef struct RawSamplesBuffer { - FuriMutex *mutex; + FuriMutex* mutex; struct { - uint16_t level:1; - uint16_t dur:15; + uint16_t level : 1; + uint16_t dur : 15; } samples[RAW_SAMPLES_NUM]; - uint32_t idx; /* Current idx (next to write). */ + uint32_t idx; /* Current idx (next to write). */ uint32_t total; /* Total samples: same as RAW_SAMPLES_NUM, we provide this field for a cleaner interface with the user, but we always use RAW_SAMPLES_NUM when taking the modulo so @@ -22,11 +23,11 @@ typedef struct RawSamplesBuffer { uint32_t short_pulse_dur; /* Duration of the shortest pulse. */ } RawSamplesBuffer; -RawSamplesBuffer *raw_samples_alloc(void); -void raw_samples_reset(RawSamplesBuffer *s); -void raw_samples_center(RawSamplesBuffer *s, uint32_t offset); -void raw_samples_add(RawSamplesBuffer *s, bool level, uint32_t dur); -void raw_samples_add_or_update(RawSamplesBuffer *s, bool level, uint32_t dur); -void raw_samples_get(RawSamplesBuffer *s, uint32_t idx, bool *level, uint32_t *dur); -void raw_samples_copy(RawSamplesBuffer *dst, RawSamplesBuffer *src); -void raw_samples_free(RawSamplesBuffer *s); +RawSamplesBuffer* raw_samples_alloc(void); +void raw_samples_reset(RawSamplesBuffer* s); +void raw_samples_center(RawSamplesBuffer* s, uint32_t offset); +void raw_samples_add(RawSamplesBuffer* s, bool level, uint32_t dur); +void raw_samples_add_or_update(RawSamplesBuffer* s, bool level, uint32_t dur); +void raw_samples_get(RawSamplesBuffer* s, uint32_t idx, bool* level, uint32_t* dur); +void raw_samples_copy(RawSamplesBuffer* dst, RawSamplesBuffer* src); +void raw_samples_free(RawSamplesBuffer* s); diff --git a/applications/plugins/protoview/signal.c b/applications/plugins/protoview/signal.c index f4c5ebedf..a1c4b2b8f 100644 --- a/applications/plugins/protoview/signal.c +++ b/applications/plugins/protoview/signal.c @@ -3,7 +3,7 @@ #include "app.h" -bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info); +bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info); /* ============================================================================= * Raw signal detection @@ -16,7 +16,7 @@ uint32_t duration_delta(uint32_t a, uint32_t b) { } /* Reset the current signal, so that a new one can be detected. */ -void reset_current_signal(ProtoViewApp *app) { +void reset_current_signal(ProtoViewApp* app) { app->signal_bestlen = 0; app->signal_offset = 0; app->signal_decoded = false; @@ -39,47 +39,47 @@ void reset_current_signal(ProtoViewApp *app) { * For instance Oregon2 sensors, in the case of protocol 2.1 will send * pulses of ~400us (RF on) VS ~580us (RF off). */ #define SEARCH_CLASSES 3 -uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { +uint32_t search_coherent_signal(RawSamplesBuffer* s, uint32_t idx) { struct { - uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ - uint32_t count[2]; /* Associated observed frequency. */ + uint32_t dur[2]; /* dur[0] = low, dur[1] = high */ + uint32_t count[2]; /* Associated observed frequency. */ } classes[SEARCH_CLASSES]; - memset(classes,0,sizeof(classes)); + memset(classes, 0, sizeof(classes)); uint32_t minlen = 30, maxlen = 4000; /* Depends on data rate, here we allow for high and low. */ uint32_t len = 0; /* Observed len of coherent samples. */ s->short_pulse_dur = 0; - for (uint32_t j = idx; j < idx+500; j++) { + for(uint32_t j = idx; j < idx + 500; j++) { bool level; uint32_t dur; raw_samples_get(s, j, &level, &dur); - if (dur < minlen || dur > maxlen) break; /* return. */ + if(dur < minlen || dur > maxlen) break; /* return. */ /* Let's see if it matches a class we already have or if we * can populate a new (yet empty) class. */ uint32_t k; - for (k = 0; k < SEARCH_CLASSES; k++) { - if (classes[k].count[level] == 0) { + for(k = 0; k < SEARCH_CLASSES; k++) { + if(classes[k].count[level] == 0) { classes[k].dur[level] = dur; classes[k].count[level] = 1; break; /* Sample accepted. */ } else { uint32_t classavg = classes[k].dur[level]; uint32_t count = classes[k].count[level]; - uint32_t delta = duration_delta(dur,classavg); + uint32_t delta = duration_delta(dur, classavg); /* Is the difference in duration between this signal and * the class we are inspecting less than a given percentage? * If so, accept this signal. */ - if (delta < classavg/5) { /* 100%/5 = 20%. */ + if(delta < classavg / 5) { /* 100%/5 = 20%. */ /* It is useful to compute the average of the class * we are observing. We know how many samples we got so * far, so we can recompute the average easily. * By always having a better estimate of the pulse len * we can avoid missing next samples in case the first * observed samples are too off. */ - classavg = ((classavg * count) + dur) / (count+1); + classavg = ((classavg * count) + dur) / (count + 1); classes[k].dur[level] = classavg; classes[k].count[level]++; break; /* Sample accepted. */ @@ -87,7 +87,7 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { } } - if (k == SEARCH_CLASSES) break; /* No match, return. */ + if(k == SEARCH_CLASSES) break; /* No match, return. */ /* If we are here, we accepted this sample. Try with the next * one. */ @@ -97,14 +97,12 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { /* Update the buffer setting the shortest pulse we found * among the three classes. This will be used when scaling * for visualization. */ - uint32_t short_dur[2] = {0,0}; - for (int j = 0; j < SEARCH_CLASSES; j++) { - for (int level = 0; level < 2; level++) { - if (classes[j].dur[level] == 0) continue; - if (classes[j].count[level] < 3) continue; - if (short_dur[level] == 0 || - short_dur[level] > classes[j].dur[level]) - { + uint32_t short_dur[2] = {0, 0}; + for(int j = 0; j < SEARCH_CLASSES; j++) { + for(int level = 0; level < 2; level++) { + if(classes[j].dur[level] == 0) continue; + if(classes[j].count[level] < 3) continue; + if(short_dur[level] == 0 || short_dur[level] > classes[j].dur[level]) { short_dur[level] = classes[j].dur[level]; } } @@ -113,33 +111,28 @@ uint32_t search_coherent_signal(RawSamplesBuffer *s, uint32_t idx) { /* Use the average between high and low short pulses duration. * Often they are a bit different, and using the average is more robust * when we do decoding sampling at short_pulse_dur intervals. */ - if (short_dur[0] == 0) short_dur[0] = short_dur[1]; - if (short_dur[1] == 0) short_dur[1] = short_dur[0]; - s->short_pulse_dur = (short_dur[0]+short_dur[1])/2; + if(short_dur[0] == 0) short_dur[0] = short_dur[1]; + if(short_dur[1] == 0) short_dur[1] = short_dur[0]; + s->short_pulse_dur = (short_dur[0] + short_dur[1]) / 2; return len; } /* Called when we detect a message. Just blinks when the message was * not decoded. Vibrates, too, when the message was correctly decoded. */ -void notify_signal_detected(ProtoViewApp *app, bool decoded) { +void notify_signal_detected(ProtoViewApp* app, bool decoded) { static const NotificationSequence decoded_seq = { &message_vibro_on, &message_green_255, &message_delay_50, &message_green_0, &message_vibro_off, - NULL - }; + NULL}; static const NotificationSequence unknown_seq = { - &message_red_255, - &message_delay_50, - &message_red_0, - NULL - }; + &message_red_255, &message_delay_50, &message_red_0, NULL}; - if (decoded) + if(decoded) notification_message(app->notification, &decoded_seq); else notification_message(app->notification, &unknown_seq); @@ -149,57 +142,59 @@ void notify_signal_detected(ProtoViewApp *app, bool decoded) { * in order to find a coherent signal. If a signal that does not appear to * be just noise is found, it is set in DetectedSamples global signal * buffer, that is what is rendered on the screen. */ -void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) { +void scan_for_signal(ProtoViewApp* app, RawSamplesBuffer* source) { /* We need to work on a copy: the source buffer may be populated * by the background thread receiving data. */ - RawSamplesBuffer *copy = raw_samples_alloc(); - raw_samples_copy(copy,source); + RawSamplesBuffer* copy = raw_samples_alloc(); + raw_samples_copy(copy, source); /* Try to seek on data that looks to have a regular high low high low * pattern. */ - uint32_t minlen = 18; /* Min run of coherent samples. With less + uint32_t minlen = 18; /* Min run of coherent samples. With less than a few samples it's very easy to mistake noise for signal. */ uint32_t i = 0; - while (i < copy->total-1) { - uint32_t thislen = search_coherent_signal(copy,i); + while(i < copy->total - 1) { + uint32_t thislen = search_coherent_signal(copy, i); /* For messages that are long enough, attempt decoding. */ - if (thislen > minlen) { + if(thislen > minlen) { /* Allocate the message information that some decoder may * fill, in case it is able to decode a message. */ - ProtoViewMsgInfo *info = malloc(sizeof(ProtoViewMsgInfo)); - init_msg_info(info,app); + ProtoViewMsgInfo* info = malloc(sizeof(ProtoViewMsgInfo)); + init_msg_info(info, app); info->short_pulse_dur = copy->short_pulse_dur; uint32_t saved_idx = copy->idx; /* Save index, see later. */ /* decode_signal() expects the detected signal to start * from index zero .*/ - raw_samples_center(copy,i); - bool decoded = decode_signal(copy,thislen,info); + raw_samples_center(copy, i); + bool decoded = decode_signal(copy, thislen, info); copy->idx = saved_idx; /* Restore the index as we are scanning the signal in the loop. */ /* Accept this signal as the new signal if either it's longer * than the previous undecoded one, or the previous one was * unknown and this is decoded. */ - if ((thislen > app->signal_bestlen && app->signal_decoded == false) - || (app->signal_decoded == false && decoded)) - { + if((thislen > app->signal_bestlen && app->signal_decoded == false) || + (app->signal_decoded == false && decoded)) { free_msg_info(app->msg_info); app->msg_info = info; app->signal_bestlen = thislen; app->signal_decoded = decoded; - raw_samples_copy(DetectedSamples,copy); - raw_samples_center(DetectedSamples,i); - FURI_LOG_E(TAG, "===> Displayed sample updated (%d samples %lu us)", - (int)thislen, DetectedSamples->short_pulse_dur); + raw_samples_copy(DetectedSamples, copy); + raw_samples_center(DetectedSamples, i); + FURI_LOG_E( + TAG, + "===> Displayed sample updated (%d samples %lu us)", + (int)thislen, + DetectedSamples->short_pulse_dur); - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - notify_signal_detected(app,decoded); + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + notify_signal_detected(app, decoded); } else { /* If the structure was not filled, discard it. Otherwise * now the owner is app->msg_info. */ @@ -227,38 +222,42 @@ void scan_for_signal(ProtoViewApp *app, RawSamplesBuffer *source) { /* Set the 'bitpos' bit to value 'val', in the specified bitmap * 'b' of len 'blen'. * Out of range bits will silently be discarded. */ -void bitmap_set(uint8_t *b, uint32_t blen, uint32_t bitpos, bool val) { - uint32_t byte = bitpos/8; - uint32_t bit = 7-(bitpos&7); - if (byte >= blen) return; - if (val) - b[byte] |= 1<= blen) return; + if(val) + b[byte] |= 1 << bit; else - b[byte] &= ~(1<= blen) return 0; - return (b[byte] & (1<= blen) return 0; + return (b[byte] & (1 << bit)) != 0; } /* Copy 'count' bits from the bitmap 's' of 'slen' total bytes, to the * bitmap 'd' of 'dlen' total bytes. The bits are copied starting from * offset 'soff' of the source bitmap to the offset 'doff' of the * destination bitmap. */ -void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, - uint8_t *s, uint32_t slen, uint32_t soff, - uint32_t count) -{ +void bitmap_copy( + uint8_t* d, + uint32_t dlen, + uint32_t doff, + uint8_t* s, + uint32_t slen, + uint32_t soff, + uint32_t count) { /* If we are byte-aligned in both source and destination, use a fast * path for the number of bytes we can consume this way. */ - if ((doff & 7) == 0 && (soff & 7) == 0) { - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + if((doff & 7) == 0 && (soff & 7) == 0) { + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { d[didx++] = s[sidx++]; count -= 8; @@ -271,9 +270,9 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Copy the bits needed to reach an offset where we can copy * two half bytes of src to a full byte of destination. */ - while(count > 8 && (doff&7) != 0) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + while(count > 8 && (doff & 7) != 0) { + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } @@ -316,13 +315,12 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, * src[2] << 5, that is "WORLDS!!" >> 5 = ".....WOR" * That is "HELLOWOR" */ - if (count > 8) { + if(count > 8) { uint8_t skew = soff % 8; /* Don't worry, compiler will optimize. */ - uint32_t didx = doff/8; - uint32_t sidx = soff/8; + uint32_t didx = doff / 8; + uint32_t sidx = soff / 8; while(count > 8 && didx < dlen && sidx < slen) { - d[didx] = ((s[sidx] << skew) | - (s[sidx+1] >> (8-skew))); + d[didx] = ((s[sidx] << skew) | (s[sidx + 1] >> (8 - skew))); sidx++; didx++; soff += 8; @@ -334,8 +332,8 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* Here count is guaranteed to be < 8. * Copy the final bits bit by bit. */ while(count) { - bool bit = bitmap_get(s,slen,soff++); - bitmap_set(d,dlen,doff++,bit); + bool bit = bitmap_get(s, slen, soff++); + bitmap_set(d, dlen, doff++, bit); count--; } } @@ -343,15 +341,15 @@ void bitmap_copy(uint8_t *d, uint32_t dlen, uint32_t doff, /* We decode bits assuming the first bit we receive is the MSB * (see bitmap_set/get functions). Certain devices send data * encoded in the reverse way. */ -void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { - for (uint32_t j = 0; j < len; j++) { +void bitmap_reverse_bytes_bits(uint8_t* p, uint32_t len) { + for(uint32_t j = 0; j < len; j++) { uint32_t b = p[j]; /* Step 1: swap the two nibbles: 12345678 -> 56781234 */ - b = (b&0xf0)>>4 | (b&0x0f)<<4; + b = (b & 0xf0) >> 4 | (b & 0x0f) << 4; /* Step 2: swap adjacent pairs : 56781234 -> 78563412 */ - b = (b&0xcc)>>2 | (b&0x33)<<2; + b = (b & 0xcc) >> 2 | (b & 0x33) << 2; /* Step 3: swap adjacent bits : 78563412 -> 87654321 */ - b = (b&0xaa)>>1 | (b&0x55)<<1; + b = (b & 0xaa) >> 1 | (b & 0x55) << 1; p[j] = b; } } @@ -359,10 +357,10 @@ void bitmap_reverse_bytes_bits(uint8_t *p, uint32_t len) { /* Return true if the specified sequence of bits, provided as a string in the * form "11010110..." is found in the 'b' bitmap of 'blen' bits at 'bitpos' * position. */ -bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *bits) { - for (size_t j = 0; bits[j]; j++) { +bool bitmap_match_bits(uint8_t* b, uint32_t blen, uint32_t bitpos, const char* bits) { + for(size_t j = 0; bits[j]; j++) { bool expected = (bits[j] == '1') ? true : false; - if (bitmap_get(b,blen,bitpos+j) != expected) return false; + if(bitmap_get(b, blen, bitpos + j) != expected) return false; } return true; } @@ -375,12 +373,17 @@ bool bitmap_match_bits(uint8_t *b, uint32_t blen, uint32_t bitpos, const char *b * Note: there are better algorithms, such as Boyer-Moore. Here we hope that * for the kind of patterns we search we'll have a lot of early stops so * we use a vanilla approach. */ -uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t maxbits, const char *bits) { - uint32_t endpos = startpos+blen*8; - uint32_t end2 = startpos+maxbits; - if (end2 < endpos) endpos = end2; - for (uint32_t j = startpos; j < endpos; j++) - if (bitmap_match_bits(b,blen,j,bits)) return j; +uint32_t bitmap_seek_bits( + uint8_t* b, + uint32_t blen, + uint32_t startpos, + uint32_t maxbits, + const char* bits) { + uint32_t endpos = startpos + blen * 8; + uint32_t end2 = startpos + maxbits; + if(end2 < endpos) endpos = end2; + for(uint32_t j = startpos; j < endpos; j++) + if(bitmap_match_bits(b, blen, j, bits)) return j; return BITMAP_SEEK_NOT_FOUND; } @@ -391,10 +394,10 @@ uint32_t bitmap_seek_bits(uint8_t *b, uint32_t blen, uint32_t startpos, uint32_t * This function is useful in order to set the test vectors in the protocol * decoders, to see if the decoding works regardless of the fact we are able * to actually receive a given signal. */ -void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat) { +void bitmap_set_pattern(uint8_t* b, uint32_t blen, uint32_t off, const char* pat) { uint32_t i = 0; while(pat[i]) { - bitmap_set(b,blen,i+off,pat[i] == '1'); + bitmap_set(b, blen, i + off, pat[i] == '1'); i++; } } @@ -426,31 +429,36 @@ void bitmap_set_pattern(uint8_t *b, uint32_t blen, uint32_t off, const char *pat * bits set into the buffer 'b'. The 'rate' argument, in microseconds, is * the detected short-pulse duration. We expect the line code to be * meaningful when interpreted at multiples of 'rate'. */ -uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, uint32_t idx, uint32_t count, uint32_t rate) { - if (rate == 0) return 0; /* We can't perform the conversion. */ +uint32_t convert_signal_to_bits( + uint8_t* b, + uint32_t blen, + RawSamplesBuffer* s, + uint32_t idx, + uint32_t count, + uint32_t rate) { + if(rate == 0) return 0; /* We can't perform the conversion. */ uint32_t bitpos = 0; - for (uint32_t j = 0; j < count; j++) { + for(uint32_t j = 0; j < count; j++) { uint32_t dur; bool level; - raw_samples_get(s, j+idx, &level, &dur); + raw_samples_get(s, j + idx, &level, &dur); uint32_t numbits = dur / rate; /* full bits that surely fit. */ - uint32_t rest = dur % rate; /* How much we are left with. */ - if (rest > rate/2) numbits++; /* There is another one. */ + uint32_t rest = dur % rate; /* How much we are left with. */ + if(rest > rate / 2) numbits++; /* There is another one. */ /* Limit how much a single sample can spawn. There are likely no * protocols doing such long pulses when the rate is low. */ - if (numbits > 1024) numbits = 1024; + if(numbits > 1024) numbits = 1024; - if (0) /* Super verbose, so not under the DEBUG_MSG define. */ - FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", - dur,numbits,(int)level); + if(0) /* Super verbose, so not under the DEBUG_MSG define. */ + FURI_LOG_E(TAG, "%lu converted into %lu (%d) bits", dur, numbits, (int)level); /* If the signal is too short, let's claim it an interference * and ignore it completely. */ - if (numbits == 0) continue; + if(numbits == 0) continue; - while(numbits--) bitmap_set(b,blen,bitpos++,level); + while(numbits--) bitmap_set(b, blen, bitpos++, level); } return bitpos; } @@ -467,23 +475,29 @@ uint32_t convert_signal_to_bits(uint8_t *b, uint32_t blen, RawSamplesBuffer *s, * specified in bytes by the caller, via the 'len' parameters). * * The decoding starts at the specified offset (in bits) 'off'. */ -uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, const char *zero_pattern, const char *one_pattern) -{ +uint32_t convert_from_line_code( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + const char* zero_pattern, + const char* one_pattern) { uint32_t decoded = 0; /* Number of bits extracted. */ len *= 8; /* Convert bytes to bits. */ while(off < len) { bool bitval; - if (bitmap_match_bits(bits,len,off,zero_pattern)) { + if(bitmap_match_bits(bits, len, off, zero_pattern)) { bitval = false; off += strlen(zero_pattern); - } else if (bitmap_match_bits(bits,len,off,one_pattern)) { + } else if(bitmap_match_bits(bits, len, off, one_pattern)) { bitval = true; off += strlen(one_pattern); } else { break; } - bitmap_set(buf,buflen,decoded++,bitval); - if (decoded/8 == buflen) break; /* No space left on target buffer. */ + bitmap_set(buf, buflen, decoded++, bitval); + if(decoded / 8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -494,17 +508,22 @@ uint32_t convert_from_line_code(uint8_t *buf, uint64_t buflen, uint8_t *bits, ui * in differential codings the next bits depend on the previous one. * * Parameters and return values are like convert_from_line_code(). */ -uint32_t convert_from_diff_manchester(uint8_t *buf, uint64_t buflen, uint8_t *bits, uint32_t len, uint32_t off, bool previous) -{ +uint32_t convert_from_diff_manchester( + uint8_t* buf, + uint64_t buflen, + uint8_t* bits, + uint32_t len, + uint32_t off, + bool previous) { uint32_t decoded = 0; len *= 8; /* Conver to bits. */ - for (uint32_t j = off; j < len; j += 2) { - bool b0 = bitmap_get(bits,len,j); - bool b1 = bitmap_get(bits,len,j+1); - if (b0 == previous) break; /* Each new bit must switch value. */ - bitmap_set(buf,buflen,decoded++,b0 == b1); + for(uint32_t j = off; j < len; j += 2) { + bool b0 = bitmap_get(bits, len, j); + bool b1 = bitmap_get(bits, len, j + 1); + if(b0 == previous) break; /* Each new bit must switch value. */ + bitmap_set(buf, buflen, decoded++, b0 == b1); previous = b1; - if (decoded/8 == buflen) break; /* No space left on target buffer. */ + if(decoded / 8 == buflen) break; /* No space left on target buffer. */ } return decoded; } @@ -522,22 +541,21 @@ extern ProtoViewDecoder CitroenTPMSDecoder; extern ProtoViewDecoder FordTPMSDecoder; extern ProtoViewDecoder KeeloqDecoder; -ProtoViewDecoder *Decoders[] = { - &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ - &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ - &RenaultTPMSDecoder, /* Renault TPMS. */ - &ToyotaTPMSDecoder, /* Toyota TPMS. */ - &SchraderTPMSDecoder, /* Schrader TPMS. */ - &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ - &CitroenTPMSDecoder, /* Citroen TPMS. */ - &FordTPMSDecoder, /* Ford TPMS. */ - &KeeloqDecoder, /* Keeloq remote. */ - NULL -}; +ProtoViewDecoder* Decoders[] = { + &Oregon2Decoder, /* Oregon sensors v2.1 protocol. */ + &B4B1Decoder, /* PT, SC, ... 24 bits remotes. */ + &RenaultTPMSDecoder, /* Renault TPMS. */ + &ToyotaTPMSDecoder, /* Toyota TPMS. */ + &SchraderTPMSDecoder, /* Schrader TPMS. */ + &SchraderEG53MA4TPMSDecoder, /* Schrader EG53MA4 TPMS. */ + &CitroenTPMSDecoder, /* Citroen TPMS. */ + &FordTPMSDecoder, /* Ford TPMS. */ + &KeeloqDecoder, /* Keeloq remote. */ + NULL}; /* Free the message info and allocated data. */ -void free_msg_info(ProtoViewMsgInfo *i) { - if (i == NULL) return; +void free_msg_info(ProtoViewMsgInfo* i) { + if(i == NULL) return; fieldset_free(i->fieldset); free(i->bits); free(i); @@ -545,9 +563,9 @@ void free_msg_info(ProtoViewMsgInfo *i) { /* Reset the message info structure before passing it to the decoding * functions. */ -void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { +void init_msg_info(ProtoViewMsgInfo* i, ProtoViewApp* app) { UNUSED(app); - memset(i,0,sizeof(ProtoViewMsgInfo)); + memset(i, 0, sizeof(ProtoViewMsgInfo)); i->bits = NULL; i->fieldset = fieldset_new(); } @@ -556,23 +574,29 @@ void init_msg_info(ProtoViewMsgInfo *i, ProtoViewApp *app) { * to a bitstream, and the calls the protocol specific functions for * decoding. If the signal was decoded correctly by some protocol, true * is returned. Otherwise false is returned. */ -bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { - uint32_t bitmap_bits_size = 4096*8; - uint32_t bitmap_size = bitmap_bits_size/8; +bool decode_signal(RawSamplesBuffer* s, uint64_t len, ProtoViewMsgInfo* info) { + uint32_t bitmap_bits_size = 4096 * 8; + uint32_t bitmap_size = bitmap_bits_size / 8; /* We call the decoders with an offset a few samples before the actual * signal detected and for a len of a few bits after its end. */ uint32_t before_samples = 32; uint32_t after_samples = 100; - uint8_t *bitmap = malloc(bitmap_size); - uint32_t bits = convert_signal_to_bits(bitmap,bitmap_size,s,-before_samples,len+before_samples+after_samples,s->short_pulse_dur); + uint8_t* bitmap = malloc(bitmap_size); + uint32_t bits = convert_signal_to_bits( + bitmap, + bitmap_size, + s, + -before_samples, + len + before_samples + after_samples, + s->short_pulse_dur); - if (DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ - char *str = malloc(1024); + if(DEBUG_MSG) { /* Useful for debugging purposes. Don't remove. */ + char* str = malloc(1024); uint32_t j; - for (j = 0; j < bits && j < 1023; j++) { - str[j] = bitmap_get(bitmap,bitmap_size,j) ? '1' : '0'; + for(j = 0; j < bits && j < 1023; j++) { + str[j] = bitmap_get(bitmap, bitmap_size, j) ? '1' : '0'; } str[j] = 0; FURI_LOG_E(TAG, "%lu bits sampled: %s", bits, str); @@ -585,18 +609,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { bool decoded = false; while(Decoders[j]) { uint32_t start_time = furi_get_tick(); - decoded = Decoders[j]->decode(bitmap,bitmap_size,bits,info); + decoded = Decoders[j]->decode(bitmap, bitmap_size, bits, info); uint32_t delta = furi_get_tick() - start_time; - FURI_LOG_E(TAG, "Decoder %s took %lu ms", - Decoders[j]->name, (unsigned long)delta); - if (decoded) { + FURI_LOG_E(TAG, "Decoder %s took %lu ms", Decoders[j]->name, (unsigned long)delta); + if(decoded) { info->decoder = Decoders[j]; break; } j++; } - if (!decoded) { + if(!decoded) { FURI_LOG_E(TAG, "No decoding possible"); } else { FURI_LOG_E(TAG, "+++ Decoded %s", info->decoder->name); @@ -604,12 +627,17 @@ bool decode_signal(RawSamplesBuffer *s, uint64_t len, ProtoViewMsgInfo *info) { * with the decoded signal. The decoder may not implement offset/len * filling of the structure. In such case we have no info and * pulses_count will be set to zero. */ - if (info->pulses_count) { - info->bits_bytes = (info->pulses_count+7)/8; // Round to full byte. + if(info->pulses_count) { + info->bits_bytes = (info->pulses_count + 7) / 8; // Round to full byte. info->bits = malloc(info->bits_bytes); - bitmap_copy(info->bits,info->bits_bytes,0, - bitmap,bitmap_size,info->start_off, - info->pulses_count); + bitmap_copy( + info->bits, + info->bits_bytes, + 0, + bitmap, + bitmap_size, + info->start_off, + info->pulses_count); } } free(bitmap); diff --git a/applications/plugins/protoview/signal_file.c b/applications/plugins/protoview/signal_file.c index 31c8726fb..c60a6a181 100644 --- a/applications/plugins/protoview/signal_file.c +++ b/applications/plugins/protoview/signal_file.c @@ -13,57 +13,56 @@ * but it's logical representation stored in the app->msg_info bitmap, where * each 1 or 0 means a puls or gap for the specified short pulse duration time * (te). */ -bool save_signal(ProtoViewApp *app, const char *filename) { +bool save_signal(ProtoViewApp* app, const char* filename) { /* We have a message at all? */ - if (app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; - - Storage *storage = furi_record_open(RECORD_STORAGE); - FlipperFormat *file = flipper_format_file_alloc(storage); - Stream *stream = flipper_format_get_raw_stream(file); - FuriString *file_content = NULL; + if(app->msg_info == NULL || app->msg_info->pulses_count == 0) return false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* file = flipper_format_file_alloc(storage); + Stream* stream = flipper_format_get_raw_stream(file); + FuriString* file_content = NULL; bool success = true; - if (flipper_format_file_open_always(file, filename)) { + if(flipper_format_file_open_always(file, filename)) { /* Write the file header. */ - FuriString *file_content = furi_string_alloc(); - const char *preset_id = ProtoViewModulations[app->modulation].id; + FuriString* file_content = furi_string_alloc(); + const char* preset_id = ProtoViewModulations[app->modulation].id; - furi_string_printf(file_content, - "Filetype: Flipper SubGhz RAW File\n" - "Version: 1\n" - "Frequency: %ld\n" - "Preset: %s\n", - app->frequency, - preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); + furi_string_printf( + file_content, + "Filetype: Flipper SubGhz RAW File\n" + "Version: 1\n" + "Frequency: %ld\n" + "Preset: %s\n", + app->frequency, + preset_id ? preset_id : "FuriHalSubGhzPresetCustom"); /* For custom modulations, we need to emit a set of registers. */ - if (preset_id == NULL) { - FuriString *custom = furi_string_alloc(); - uint8_t *regs = ProtoViewModulations[app->modulation].custom; - furi_string_printf(custom, + if(preset_id == NULL) { + FuriString* custom = furi_string_alloc(); + uint8_t* regs = ProtoViewModulations[app->modulation].custom; + furi_string_printf( + custom, "Custom_preset_module: CC1101\n" - "Custom_preset_data: "); - for (int j = 0; regs[j]; j += 2) { - furi_string_cat_printf(custom, "%02X %02X ", - (int)regs[j], (int)regs[j+1]); + "Custom_preset_data: "); + for(int j = 0; regs[j]; j += 2) { + furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]); } size_t len = furi_string_size(file_content); - furi_string_set_char(custom,len-1,'\n'); - furi_string_cat(file_content,custom); + furi_string_set_char(custom, len - 1, '\n'); + furi_string_cat(file_content, custom); furi_string_free(custom); } /* We always save raw files. */ - furi_string_cat_printf(file_content, - "Protocol: RAW\n" - "RAW_Data: -10000\n"); // Start with 10 ms of gap + furi_string_cat_printf( + file_content, + "Protocol: RAW\n" + "RAW_Data: -10000\n"); // Start with 10 ms of gap /* Write header. */ size_t len = furi_string_size(file_content); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), len) - != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -76,15 +75,13 @@ bool save_signal(ProtoViewApp *app, const char *filename) { uint32_t this_line_samples = 0; uint32_t max_line_samples = 100; uint32_t idx = 0; // Iindex in the signal bitmap. - ProtoViewMsgInfo *i = app->msg_info; + ProtoViewMsgInfo* i = app->msg_info; while(idx < i->pulses_count) { - bool level = bitmap_get(i->bits,i->bits_bytes,idx); + bool level = bitmap_get(i->bits, i->bits_bytes, idx); uint32_t te_times = 1; idx++; /* Count the duration of the current pulse/gap. */ - while(idx < i->pulses_count && - bitmap_get(i->bits,i->bits_bytes,idx) == level) - { + while(idx < i->pulses_count && bitmap_get(i->bits, i->bits_bytes, idx) == level) { te_times++; idx++; } @@ -92,32 +89,29 @@ bool save_signal(ProtoViewApp *app, const char *filename) { // next gap or pulse. int32_t dur = (int32_t)i->short_pulse_dur * te_times; - if (level == 0) dur = -dur; /* Negative is gap in raw files. */ + if(level == 0) dur = -dur; /* Negative is gap in raw files. */ /* Emit the sample. If this is the first sample of the line, * also emit the RAW_Data: field. */ - if (this_line_samples == 0) - furi_string_cat_printf(file_content,"RAW_Data: "); - furi_string_cat_printf(file_content,"%d ",(int)dur); + if(this_line_samples == 0) furi_string_cat_printf(file_content, "RAW_Data: "); + furi_string_cat_printf(file_content, "%d ", (int)dur); this_line_samples++; /* Store the current set of samples on disk, when we reach a * given number or the end of the signal. */ bool end_reached = (idx == i->pulses_count); - if (this_line_samples == max_line_samples || end_reached) { + if(this_line_samples == max_line_samples || end_reached) { /* If that's the end, terminate the signal with a long * gap. */ - if (end_reached) furi_string_cat_printf(file_content,"-10000 "); + if(end_reached) furi_string_cat_printf(file_content, "-10000 "); /* We always have a trailing space in the last sample. Make it * a newline. */ size_t len = furi_string_size(file_content); - furi_string_set_char(file_content,len-1,'\n'); + furi_string_set_char(file_content, len - 1, '\n'); - if (stream_write(stream, - (uint8_t*) furi_string_get_cstr(file_content), - len) != len) - { + if(stream_write(stream, (uint8_t*)furi_string_get_cstr(file_content), len) != + len) { FURI_LOG_W(TAG, "Short write to file"); success = false; goto write_err; @@ -136,6 +130,6 @@ bool save_signal(ProtoViewApp *app, const char *filename) { write_err: furi_record_close(RECORD_STORAGE); flipper_format_free(file); - if (file_content != NULL) furi_string_free(file_content); + if(file_content != NULL) furi_string_free(file_content); return success; } diff --git a/applications/plugins/protoview/ui.c b/applications/plugins/protoview/ui.c index 8badab5bf..b0251f09f 100644 --- a/applications/plugins/protoview/ui.c +++ b/applications/plugins/protoview/ui.c @@ -10,36 +10,31 @@ /* Return the ID of the currently selected subview, of the current * view. */ -int ui_get_current_subview(ProtoViewApp *app) { +int ui_get_current_subview(ProtoViewApp* app) { return app->current_subview[app->current_view]; } /* Called by view rendering callback that has subviews, to show small triangles * facing down/up if there are other subviews the user can access with up * and down. */ -void ui_show_available_subviews(Canvas *canvas, ProtoViewApp *app, - int last_subview) -{ +void ui_show_available_subviews(Canvas* canvas, ProtoViewApp* app, int last_subview) { int subview = ui_get_current_subview(app); - if (subview != 0) - canvas_draw_triangle(canvas,120,5,8,5,CanvasDirectionBottomToTop); - if (subview != last_subview-1) - canvas_draw_triangle(canvas,120,59,8,5,CanvasDirectionTopToBottom); + if(subview != 0) canvas_draw_triangle(canvas, 120, 5, 8, 5, CanvasDirectionBottomToTop); + if(subview != last_subview - 1) + canvas_draw_triangle(canvas, 120, 59, 8, 5, CanvasDirectionTopToBottom); } /* Handle up/down keys when we are in a subview. If the function catched * such keypress, it returns true, so that the actual view input callback * knows it can just return ASAP without doing anything. */ -bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_subview) { +bool ui_process_subview_updown(ProtoViewApp* app, InputEvent input, int last_subview) { int subview = ui_get_current_subview(app); - if (input.type == InputTypePress) { - if (input.key == InputKeyUp) { - if (subview != 0) - app->current_subview[app->current_view]--; + if(input.type == InputTypePress) { + if(input.key == InputKeyUp) { + if(subview != 0) app->current_subview[app->current_view]--; return true; - } else if (input.key == InputKeyDown) { - if (subview != last_subview-1) - app->current_subview[app->current_view]++; + } else if(input.key == InputKeyDown) { + if(subview != last_subview - 1) app->current_subview[app->current_view]++; return true; } } @@ -62,16 +57,18 @@ bool ui_process_subview_updown(ProtoViewApp *app, InputEvent input, int last_sub * * Note: if the buffer is not a null-termined zero string, what it contains will * be used as initial input for the user. */ -void ui_show_keyboard(ProtoViewApp *app, char *buffer, uint32_t buflen, - void (*done_callback)(void*)) -{ +void ui_show_keyboard( + ProtoViewApp* app, + char* buffer, + uint32_t buflen, + void (*done_callback)(void*)) { app->show_text_input = true; app->text_input_buffer = buffer; app->text_input_buffer_len = buflen; app->text_input_done_callback = done_callback; } -void ui_dismiss_keyboard(ProtoViewApp *app) { +void ui_dismiss_keyboard(ProtoViewApp* app) { view_dispatcher_stop(app->view_dispatcher); } @@ -79,24 +76,24 @@ void ui_dismiss_keyboard(ProtoViewApp *app) { /* Set an alert message to be shown over any currently active view, for * the specified amount of time of 'ttl' milliseconds. */ -void ui_show_alert(ProtoViewApp *app, const char *text, uint32_t ttl) { +void ui_show_alert(ProtoViewApp* app, const char* text, uint32_t ttl) { app->alert_dismiss_time = furi_get_tick() + furi_ms_to_ticks(ttl); - snprintf(app->alert_text,ALERT_MAX_LEN,"%s",text); + snprintf(app->alert_text, ALERT_MAX_LEN, "%s", text); } /* Cancel the alert before its time has elapsed. */ -void ui_dismiss_alert(ProtoViewApp *app) { +void ui_dismiss_alert(ProtoViewApp* app) { app->alert_dismiss_time = 0; } /* Show the alert if an alert is set. This is called after the currently * active view displayed its stuff, so we overwrite the screen with the * alert message. */ -void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { - if (app->alert_dismiss_time == 0) { +void ui_draw_alert_if_needed(Canvas* canvas, ProtoViewApp* app) { + if(app->alert_dismiss_time == 0) { /* No active alert. */ return; - } else if (app->alert_dismiss_time < furi_get_tick()) { + } else if(app->alert_dismiss_time < furi_get_tick()) { /* Alert just expired. */ ui_dismiss_alert(app); return; @@ -106,41 +103,43 @@ void ui_draw_alert_if_needed(Canvas *canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t w = canvas_string_width(canvas, app->alert_text); uint8_t h = 8; // Font height. - uint8_t text_x = 64-(w/2); - uint8_t text_y = 32+4; + uint8_t text_x = 64 - (w / 2); + uint8_t text_y = 32 + 4; uint8_t padding = 3; - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,text_x-padding,text_y-padding-h,w+padding*2,h+padding*2); - canvas_set_color(canvas,ColorWhite); - canvas_draw_box(canvas,text_x-padding+1,text_y-padding-h+1,w+padding*2-2,h+padding*2-2); - canvas_set_color(canvas,ColorBlack); - canvas_draw_str(canvas,text_x,text_y,app->alert_text); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box( + canvas, text_x - padding, text_y - padding - h, w + padding * 2, h + padding * 2); + canvas_set_color(canvas, ColorWhite); + canvas_draw_box( + canvas, + text_x - padding + 1, + text_y - padding - h + 1, + w + padding * 2 - 2, + h + padding * 2 - 2); + canvas_set_color(canvas, ColorBlack); + canvas_draw_str(canvas, text_x, text_y, app->alert_text); } /* =========================== Canvas extensions ============================ */ -void canvas_draw_str_with_border(Canvas* canvas, uint8_t x, uint8_t y, const char* str, Color text_color, Color border_color) -{ +void canvas_draw_str_with_border( + Canvas* canvas, + uint8_t x, + uint8_t y, + const char* str, + Color text_color, + Color border_color) { struct { - uint8_t x; uint8_t y; - } dir[8] = { - {-1,-1}, - {0,-1}, - {1,-1}, - {1,0}, - {1,1}, - {0,1}, - {-1,1}, - {-1,0} - }; + uint8_t x; + uint8_t y; + } dir[8] = {{-1, -1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}}; /* Rotate in all the directions writing the same string to create a * border, then write the actual string in the other color in the * middle. */ canvas_set_color(canvas, border_color); - for (int j = 0; j < 8; j++) - canvas_draw_str(canvas,x+dir[j].x,y+dir[j].y,str); + for(int j = 0; j < 8; j++) canvas_draw_str(canvas, x + dir[j].x, y + dir[j].y, str); canvas_set_color(canvas, text_color); - canvas_draw_str(canvas,x,y,str); + canvas_draw_str(canvas, x, y, str); canvas_set_color(canvas, ColorBlack); } diff --git a/applications/plugins/protoview/view_build.c b/applications/plugins/protoview/view_build.c index fd276b61d..955855902 100644 --- a/applications/plugins/protoview/view_build.c +++ b/applications/plugins/protoview/view_build.c @@ -3,39 +3,38 @@ #include "app.h" -extern ProtoViewDecoder *Decoders[]; // Defined in signal.c. +extern ProtoViewDecoder* Decoders[]; // Defined in signal.c. /* Our view private data. */ #define USER_VALUE_LEN 64 typedef struct { - ProtoViewDecoder *decoder; /* Decoder we are using to create a + ProtoViewDecoder* decoder; /* Decoder we are using to create a message. */ - uint32_t cur_decoder; /* Decoder index when we are yet selecting + uint32_t cur_decoder; /* Decoder index when we are yet selecting a decoder. Used when decoder is NULL. */ - ProtoViewFieldSet *fieldset; /* The fields to populate. */ - uint32_t cur_field; /* Field we are editing right now. This + ProtoViewFieldSet* fieldset; /* The fields to populate. */ + uint32_t cur_field; /* Field we are editing right now. This is the index inside the 'fieldset' fields. */ - char *user_value; /* Keyboard input to replace the current + char* user_value; /* Keyboard input to replace the current field value goes here. */ } BuildViewPrivData; /* Not all the decoders support message bulding, so we can't just * increment / decrement the cur_decoder index here. */ -static void select_next_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - do { +static void select_next_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + do { privdata->cur_decoder++; - if (Decoders[privdata->cur_decoder] == NULL) - privdata->cur_decoder = 0; + if(Decoders[privdata->cur_decoder] == NULL) privdata->cur_decoder = 0; } while(Decoders[privdata->cur_decoder]->get_fields == NULL); } /* Like select_next_decoder() but goes backward. */ -static void select_prev_decoder(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void select_prev_decoder(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; do { - if (privdata->cur_decoder == 0) { + if(privdata->cur_decoder == 0) { /* Go one after the last one to wrap around. */ while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++; } @@ -45,69 +44,73 @@ static void select_prev_decoder(ProtoViewApp *app) { /* Render the view to select the decoder, among the ones that * support message building. */ -static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_select_decoder(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 0, 9, "Signal creator"); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose"); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,38,AlignCenter,AlignCenter, - Decoders[privdata->cur_decoder]->name); + canvas_draw_str_aligned( + canvas, 64, 38, AlignCenter, AlignCenter, Decoders[privdata->cur_decoder]->name); } /* Render the view that allows the user to populate the fields needed * for the selected decoder to build a message. */ -static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +static void render_view_set_fields(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; char buf[32]; - snprintf(buf,sizeof(buf), "%s field %d/%d", - privdata->decoder->name, (int)privdata->cur_field+1, + snprintf( + buf, + sizeof(buf), + "%s field %d/%d", + privdata->decoder->name, + (int)privdata->cur_field + 1, (int)privdata->fieldset->numfields); - canvas_set_color(canvas,ColorBlack); - canvas_draw_box(canvas,0,0,128,21); - canvas_set_color(canvas,ColorWhite); + canvas_set_color(canvas, ColorBlack); + canvas_draw_box(canvas, 0, 0, 128, 21); + canvas_set_color(canvas, ColorWhite); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 1, 9, buf); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 1, 19, "up/down: next field, ok: edit"); /* Write the field name, type, current content. */ - canvas_set_color(canvas,ColorBlack); - ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field]; - snprintf(buf,sizeof(buf), "%s %s:%d", field->name, - field_get_type_name(field), (int)field->len); + canvas_set_color(canvas, ColorBlack); + ProtoViewField* field = privdata->fieldset->fields[privdata->cur_field]; + snprintf( + buf, sizeof(buf), "%s %s:%d", field->name, field_get_type_name(field), (int)field->len); buf[0] = toupper(buf[0]); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf); + canvas_draw_str_aligned(canvas, 64, 30, AlignCenter, AlignCenter, buf); canvas_set_font(canvas, FontSecondary); /* Render the current value between "" */ - unsigned int written = (unsigned int) field_to_string(buf+1,sizeof(buf)-1,field); + unsigned int written = (unsigned int)field_to_string(buf + 1, sizeof(buf) - 1, field); buf[0] = '"'; - if (written+3 < sizeof(buf)) memcpy(buf+written+1,"\"\x00",2); - canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,buf); + if(written + 3 < sizeof(buf)) memcpy(buf + written + 1, "\"\x00", 2); + canvas_draw_str_aligned(canvas, 63, 45, AlignCenter, AlignCenter, buf); /* Footer instructions. */ canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr"); } /* Render the build message view. */ -void render_view_build_message(Canvas *const canvas, ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void render_view_build_message(Canvas* const canvas, ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; - if (privdata->decoder) - render_view_set_fields(canvas,app); + if(privdata->decoder) + render_view_set_fields(canvas, app); else - render_view_select_decoder(canvas,app); + render_view_select_decoder(canvas, app); } /* Handle input for the decoder selection. */ -static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { +static void process_input_select_decoder(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { privdata->decoder = Decoders[privdata->cur_decoder]; privdata->fieldset = fieldset_new(); privdata->decoder->get_fields(privdata->fieldset); @@ -116,11 +119,8 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { * same decoder the user selected, let's populate the * defaults with the current values. So the user will * actaully edit the current message. */ - if (app->signal_decoded && - app->msg_info->decoder == privdata->decoder) - { - fieldset_copy_matching_fields(privdata->fieldset, - app->msg_info->fieldset); + if(app->signal_decoded && app->msg_info->decoder == privdata->decoder) { + fieldset_copy_matching_fields(privdata->fieldset, app->msg_info->fieldset); } /* Now we use the subview system in order to protect the @@ -128,10 +128,10 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { Since we are technically into a subview now, we'll have control of < and >. */ InputEvent ii = {.type = InputTypePress, .key = InputKeyDown}; - ui_process_subview_updown(app,ii,2); - } else if (input.key == InputKeyDown) { + ui_process_subview_updown(app, ii, 2); + } else if(input.key == InputKeyDown) { select_next_decoder(app); - } else if (input.key == InputKeyUp) { + } else if(input.key == InputKeyUp) { select_prev_decoder(app); } } @@ -140,12 +140,13 @@ static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) { /* Called after the user typed the new field value in the keyboard. * Let's save it and remove the keyboard view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - BuildViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + BuildViewPrivData* privdata = app->view_privdata; - if (field_set_from_string(privdata->fieldset->fields[privdata->cur_field], - privdata->user_value, strlen(privdata->user_value)) == false) - { + if(field_set_from_string( + privdata->fieldset->fields[privdata->cur_field], + privdata->user_value, + strlen(privdata->user_value)) == false) { ui_show_alert(app, "Invalid value", 1500); } @@ -160,94 +161,88 @@ static void text_input_done_callback(void* context) { * decrement the current field in a much simpler way. * * The current filed is changed by 'incr' amount. */ -static bool increment_current_field(ProtoViewApp *app, int incr) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; - ProtoViewField *f = fs->fields[privdata->cur_field]; - return field_incr_value(f,incr); +static bool increment_current_field(ProtoViewApp* app, int incr) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; + ProtoViewField* f = fs->fields[privdata->cur_field]; + return field_incr_value(f, incr); } /* Handle input for fields editing mode. */ -static void process_input_set_fields(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - ProtoViewFieldSet *fs = privdata->fieldset; +static void process_input_set_fields(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + ProtoViewFieldSet* fs = privdata->fieldset; - if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show the keyboard to let the user type the new * value. */ - if (privdata->user_value == NULL) - privdata->user_value = malloc(USER_VALUE_LEN); - field_to_string(privdata->user_value, USER_VALUE_LEN, - fs->fields[privdata->cur_field]); - ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyDown) { - privdata->cur_field = (privdata->cur_field+1) % fs->numfields; - } else if (input.type == InputTypeShort && input.key == InputKeyUp) { - if (privdata->cur_field == 0) - privdata->cur_field = fs->numfields-1; + if(privdata->user_value == NULL) privdata->user_value = malloc(USER_VALUE_LEN); + field_to_string(privdata->user_value, USER_VALUE_LEN, fs->fields[privdata->cur_field]); + ui_show_keyboard(app, privdata->user_value, USER_VALUE_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyDown) { + privdata->cur_field = (privdata->cur_field + 1) % fs->numfields; + } else if(input.type == InputTypeShort && input.key == InputKeyUp) { + if(privdata->cur_field == 0) + privdata->cur_field = fs->numfields - 1; else privdata->cur_field--; - } else if (input.type == InputTypeShort && input.key == InputKeyRight) { - increment_current_field(app,1); - } else if (input.type == InputTypeShort && input.key == InputKeyLeft) { - increment_current_field(app,-1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyRight) { + } else if(input.type == InputTypeShort && input.key == InputKeyRight) { + increment_current_field(app, 1); + } else if(input.type == InputTypeShort && input.key == InputKeyLeft) { + increment_current_field(app, -1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyRight) { // The reason why we don't use a large increment directly // is that certain field types only support +1 -1 increments. int times = 10; - while(times--) increment_current_field(app,1); - } else if (input.type == InputTypeRepeat && input.key == InputKeyLeft) { + while(times--) increment_current_field(app, 1); + } else if(input.type == InputTypeRepeat && input.key == InputKeyLeft) { int times = 10; - while(times--) increment_current_field(app,-1); - } else if (input.type == InputTypeLong && input.key == InputKeyOk) { + while(times--) increment_current_field(app, -1); + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // Build the message in a fresh raw buffer. - if (privdata->decoder->build_message) { - RawSamplesBuffer *rs = raw_samples_alloc(); - privdata->decoder->build_message(rs,privdata->fieldset); + if(privdata->decoder->build_message) { + RawSamplesBuffer* rs = raw_samples_alloc(); + privdata->decoder->build_message(rs, privdata->fieldset); app->signal_decoded = false; // So that the new signal will be - // accepted as the current signal. - scan_for_signal(app,rs); + // accepted as the current signal. + scan_for_signal(app, rs); raw_samples_free(rs); - ui_show_alert(app,"Done: press back key",3000); + ui_show_alert(app, "Done: press back key", 3000); } } } /* Handle input for the build message view. */ -void process_input_build_message(ProtoViewApp *app, InputEvent input) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->decoder) - process_input_set_fields(app,input); +void process_input_build_message(ProtoViewApp* app, InputEvent input) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->decoder) + process_input_set_fields(app, input); else - process_input_select_decoder(app,input); + process_input_select_decoder(app, input); } /* Enter view callback. */ -void view_enter_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; +void view_enter_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; // When we enter the view, the current decoder is just set to zero. // Seek the next valid if needed. - if (Decoders[privdata->cur_decoder]->get_fields == NULL) { + if(Decoders[privdata->cur_decoder]->get_fields == NULL) { select_next_decoder(app); } // However if there is currently a decoded message, and the // decoder of such message supports message building, let's // select it. - if (app->signal_decoded && - app->msg_info->decoder->get_fields && - app->msg_info->decoder->build_message) - { - while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) - select_next_decoder(app); + if(app->signal_decoded && app->msg_info->decoder->get_fields && + app->msg_info->decoder->build_message) { + while(Decoders[privdata->cur_decoder] != app->msg_info->decoder) select_next_decoder(app); } } /* Called on exit for cleanup. */ -void view_exit_build_message(ProtoViewApp *app) { - BuildViewPrivData *privdata = app->view_privdata; - if (privdata->fieldset) fieldset_free(privdata->fieldset); - if (privdata->user_value) free(privdata->user_value); +void view_exit_build_message(ProtoViewApp* app) { + BuildViewPrivData* privdata = app->view_privdata; + if(privdata->fieldset) fieldset_free(privdata->fieldset); + if(privdata->user_value) free(privdata->user_value); } diff --git a/applications/plugins/protoview/view_direct_sampling.c b/applications/plugins/protoview/view_direct_sampling.c index 251a289b8..1ab90f096 100644 --- a/applications/plugins/protoview/view_direct_sampling.c +++ b/applications/plugins/protoview/view_direct_sampling.c @@ -7,47 +7,46 @@ /* Read directly from the G0 CC1101 pin, and draw a black or white * dot depending on the level. */ -void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) { - if (!app->direct_sampling_enabled) { +void render_view_direct_sampling(Canvas* const canvas, ProtoViewApp* app) { + if(!app->direct_sampling_enabled) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas,2,9,"Direct sampling is a special"); - canvas_draw_str(canvas,2,18,"mode that displays the signal"); - canvas_draw_str(canvas,2,27,"captured in real time. Like in"); - canvas_draw_str(canvas,2,36,"a old CRT TV. It's very slow."); - canvas_draw_str(canvas,2,45,"Can crash your Flipper."); + canvas_draw_str(canvas, 2, 9, "Direct sampling is a special"); + canvas_draw_str(canvas, 2, 18, "mode that displays the signal"); + canvas_draw_str(canvas, 2, 27, "captured in real time. Like in"); + canvas_draw_str(canvas, 2, 36, "a old CRT TV. It's very slow."); + canvas_draw_str(canvas, 2, 45, "Can crash your Flipper."); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas,14,60,"To enable press OK"); + canvas_draw_str(canvas, 14, 60, "To enable press OK"); return; } - for (int y = 0; y < 64; y++) { - for (int x = 0; x < 128; x++) { + for(int y = 0; y < 64; y++) { + for(int x = 0; x < 128; x++) { bool level = furi_hal_gpio_read(&gpio_cc1101_g0); - if (level) canvas_draw_dot(canvas,x,y); + if(level) canvas_draw_dot(canvas, x, y); /* Busy loop: this is a terrible approach as it blocks * everything else, but for now it's the best we can do * to obtain direct data with some spacing. */ - uint32_t x = 250; while(x--); + uint32_t x = 250; + while(x--) + ; } } canvas_set_font(canvas, FontSecondary); - canvas_draw_str_with_border(canvas,36,60,"Direct sampling", - ColorWhite,ColorBlack); + canvas_draw_str_with_border(canvas, 36, 60, "Direct sampling", ColorWhite, ColorBlack); } /* Handle input */ -void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypePress && input.key == InputKeyOk) { +void process_input_direct_sampling(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypePress && input.key == InputKeyOk) { app->direct_sampling_enabled = !app->direct_sampling_enabled; } } /* Enter view. Stop the subghz thread to prevent access as we read * the CC1101 data directly. */ -void view_enter_direct_sampling(ProtoViewApp *app) { - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { +void view_enter_direct_sampling(ProtoViewApp* app) { + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { subghz_worker_stop(app->txrx->worker); } else { raw_sampling_worker_stop(app); @@ -55,10 +54,8 @@ void view_enter_direct_sampling(ProtoViewApp *app) { } /* Exit view. Restore the subghz thread. */ -void view_exit_direct_sampling(ProtoViewApp *app) { - if (app->txrx->txrx_state == TxRxStateRx && - !app->txrx->debug_timer_sampling) - { +void view_exit_direct_sampling(ProtoViewApp* app) { + if(app->txrx->txrx_state == TxRxStateRx && !app->txrx->debug_timer_sampling) { subghz_worker_start(app->txrx->worker); } else { raw_sampling_worker_start(app); diff --git a/applications/plugins/protoview/view_info.c b/applications/plugins/protoview/view_info.c index 6aa69739c..75fc58411 100644 --- a/applications/plugins/protoview/view_info.c +++ b/applications/plugins/protoview/view_info.c @@ -20,31 +20,29 @@ typedef struct { * so that the user can see what they are saving. With left/right * you can move to next rows. Here we store where we are. */ uint32_t signal_display_start_row; - char *filename; + char* filename; uint8_t cur_info_page; // Info page to display. Useful when there are - // too many fields populated by the decoder that - // a single page is not enough. + // too many fields populated by the decoder that + // a single page is not enough. } InfoViewPrivData; /* Draw the text label and value of the specified info field at x,y. */ -static void render_info_field(Canvas *const canvas, - ProtoViewField *f, uint8_t x, uint8_t y) -{ +static void render_info_field(Canvas* const canvas, ProtoViewField* f, uint8_t x, uint8_t y) { char buf[64]; char strval[32]; - field_to_string(strval,sizeof(strval),f); - snprintf(buf,sizeof(buf),"%s: %s", f->name, strval); + field_to_string(strval, sizeof(strval), f); + snprintf(buf, sizeof(buf), "%s: %s", f->name, strval); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, x, y, buf); } /* Render the view with the detected message information. */ #define INFO_LINES_PER_PAGE 5 -static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; - uint8_t pages = (app->msg_info->fieldset->numfields - +(INFO_LINES_PER_PAGE-1)) / INFO_LINES_PER_PAGE; +static void render_subview_main(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; + uint8_t pages = + (app->msg_info->fieldset->numfields + (INFO_LINES_PER_PAGE - 1)) / INFO_LINES_PER_PAGE; privdata->cur_info_page %= pages; uint8_t current_page = privdata->cur_info_page; char buf[32]; @@ -53,9 +51,9 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { canvas_set_font(canvas, FontPrimary); uint8_t y = 8, lineheight = 10; - if (pages > 1) { - snprintf(buf,sizeof(buf),"%s %u/%u", app->msg_info->decoder->name, - current_page+1, pages); + if(pages > 1) { + snprintf( + buf, sizeof(buf), "%s %u/%u", app->msg_info->decoder->name, current_page + 1, pages); canvas_draw_str(canvas, 0, y, buf); } else { canvas_draw_str(canvas, 0, y, app->msg_info->decoder->name); @@ -64,26 +62,30 @@ static void render_subview_main(Canvas *const canvas, ProtoViewApp *app) { /* Draw the info fields. */ uint8_t max_lines = INFO_LINES_PER_PAGE; - uint32_t j = current_page*max_lines; - while (j < app->msg_info->fieldset->numfields) { - render_info_field(canvas,app->msg_info->fieldset->fields[j++],0,y); + uint32_t j = current_page * max_lines; + while(j < app->msg_info->fieldset->numfields) { + render_info_field(canvas, app->msg_info->fieldset->fields[j++], 0, y); y += lineheight; - if (--max_lines == 0) break; + if(--max_lines == 0) break; } /* Draw a vertical "save" label. Temporary solution, to switch to * something better ASAP. */ y = 37; lineheight = 7; - canvas_draw_str(canvas, 119, y, "s"); y += lineheight; - canvas_draw_str(canvas, 119, y, "a"); y += lineheight; - canvas_draw_str(canvas, 119, y, "v"); y += lineheight; - canvas_draw_str(canvas, 119, y, "e"); y += lineheight; + canvas_draw_str(canvas, 119, y, "s"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "a"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "v"); + y += lineheight; + canvas_draw_str(canvas, 119, y, "e"); + y += lineheight; } /* Render view with save option. */ -static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +static void render_subview_save(Canvas* const canvas, ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; /* Display our signal in digital form: here we don't show the * signal with the exact timing of the received samples, but as it @@ -92,21 +94,20 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { uint8_t rowheight = 11; uint8_t bitwidth = 4; uint8_t bitheight = 5; - uint32_t idx = privdata->signal_display_start_row * (128/4); + uint32_t idx = privdata->signal_display_start_row * (128 / 4); bool prevbit = false; - for (uint8_t y = bitheight+12; y <= rows*rowheight; y += rowheight) { - for (uint8_t x = 0; x < 128; x += 4) { - bool bit = bitmap_get(app->msg_info->bits, - app->msg_info->bits_bytes,idx); - uint8_t prevy = y + prevbit*(bitheight*-1) - 1; - uint8_t thisy = y + bit*(bitheight*-1) - 1; - canvas_draw_line(canvas,x,prevy,x,thisy); - canvas_draw_line(canvas,x,thisy,x+bitwidth-1,thisy); + for(uint8_t y = bitheight + 12; y <= rows * rowheight; y += rowheight) { + for(uint8_t x = 0; x < 128; x += 4) { + bool bit = bitmap_get(app->msg_info->bits, app->msg_info->bits_bytes, idx); + uint8_t prevy = y + prevbit * (bitheight * -1) - 1; + uint8_t thisy = y + bit * (bitheight * -1) - 1; + canvas_draw_line(canvas, x, prevy, x, thisy); + canvas_draw_line(canvas, x, thisy, x + bitwidth - 1, thisy); prevbit = bit; - if (idx >= app->msg_info->pulses_count) { + if(idx >= app->msg_info->pulses_count) { canvas_set_color(canvas, ColorWhite); - canvas_draw_dot(canvas, x+1,thisy); - canvas_draw_dot(canvas, x+3,thisy); + canvas_draw_dot(canvas, x + 1, thisy); + canvas_draw_dot(canvas, x + 3, thisy); canvas_set_color(canvas, ColorBlack); } idx++; // Draw next bit @@ -118,28 +119,32 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) { } /* Render the selected subview of this view. */ -void render_view_info(Canvas *const canvas, ProtoViewApp *app) { - if (app->signal_decoded == false) { +void render_view_info(Canvas* const canvas, ProtoViewApp* app) { + if(app->signal_decoded == false) { canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 30,36,"No signal decoded"); + canvas_draw_str(canvas, 30, 36, "No signal decoded"); return; } - ui_show_available_subviews(canvas,app,SubViewInfoLast); + ui_show_available_subviews(canvas, app, SubViewInfoLast); switch(app->current_subview[app->current_view]) { - case SubViewInfoMain: render_subview_main(canvas,app); break; - case SubViewInfoSave: render_subview_save(canvas,app); break; + case SubViewInfoMain: + render_subview_main(canvas, app); + break; + case SubViewInfoSave: + render_subview_save(canvas, app); + break; } } /* The user typed the file name. Let's save it and remove the keyboard * view. */ static void text_input_done_callback(void* context) { - ProtoViewApp *app = context; - InfoViewPrivData *privdata = app->view_privdata; + ProtoViewApp* app = context; + InfoViewPrivData* privdata = app->view_privdata; - FuriString *save_path = furi_string_alloc_printf( - "%s/%s.sub", EXT_PATH("subghz"), privdata->filename); + FuriString* save_path = + furi_string_alloc_printf("%s/%s.sub", EXT_PATH("subghz"), privdata->filename); save_signal(app, furi_string_get_cstr(save_path)); furi_string_free(save_path); @@ -151,22 +156,22 @@ static void text_input_done_callback(void* context) { /* Replace all the occurrences of character c1 with c2 in the specified * string. */ -void str_replace(char *buf, char c1, char c2) { - char *p = buf; +void str_replace(char* buf, char c1, char c2) { + char* p = buf; while(*p) { - if (*p == c1) *p = c2; + if(*p == c1) *p = c2; p++; } } /* Set a random filename the user can edit. */ -void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) { +void set_signal_random_filename(ProtoViewApp* app, char* buf, size_t buflen) { char suffix[6]; - set_random_name(suffix,sizeof(suffix)); - snprintf(buf,buflen,"%.10s-%s-%d",app->msg_info->decoder->name,suffix,rand()%1000); - str_replace(buf,' ','_'); - str_replace(buf,'-','_'); - str_replace(buf,'/','_'); + set_random_name(suffix, sizeof(suffix)); + snprintf(buf, buflen, "%.10s-%s-%d", app->msg_info->decoder->name, suffix, rand() % 1000); + str_replace(buf, ' ', '_'); + str_replace(buf, '-', '_'); + str_replace(buf, '/', '_'); } /* ========================== Signal transmission =========================== */ @@ -180,20 +185,20 @@ typedef enum { SendSignalEndTransmission } SendSignalState; -#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ -#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_START_GAP 10000 /* microseconds. */ +#define PROTOVIEW_SENDSIGNAL_END_GAP 10000 /* microseconds. */ typedef struct { - SendSignalState state; // Current state. - uint32_t curpos; // Current bit position of data to send. - ProtoViewApp *app; // App reference. + SendSignalState state; // Current state. + uint32_t curpos; // Current bit position of data to send. + ProtoViewApp* app; // App reference. uint32_t start_gap_dur; // Gap to send at the start. - uint32_t end_gap_dur; // Gap to send at the end. + uint32_t end_gap_dur; // Gap to send at the end. } SendSignalCtx; /* Setup the state context for the callback responsible to feed data * to the subghz async tx system. */ -static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { +static void send_signal_init(SendSignalCtx* ss, ProtoViewApp* app) { ss->state = SendSignalSendStartGap; ss->curpos = 0; ss->app = app; @@ -214,27 +219,26 @@ static void send_signal_init(SendSignalCtx *ss, ProtoViewApp *app) { * message we are, in ss->curoff. We also send a start and end gap in order * to make sure the transmission is clear. */ -LevelDuration radio_tx_feed_data(void *ctx) { - SendSignalCtx *ss = ctx; +LevelDuration radio_tx_feed_data(void* ctx) { + SendSignalCtx* ss = ctx; /* Send start gap. */ - if (ss->state == SendSignalSendStartGap) { + if(ss->state == SendSignalSendStartGap) { ss->state = SendSignalSendBits; - return level_duration_make(0,ss->start_gap_dur); + return level_duration_make(0, ss->start_gap_dur); } /* Send data. */ - if (ss->state == SendSignalSendBits) { + if(ss->state == SendSignalSendBits) { uint32_t dur = 0, j; uint32_t level = 0; /* Let's see how many consecutive bits we have with the same * level. */ - for (j = 0; ss->curpos+j < ss->app->msg_info->pulses_count; j++) { - uint32_t l = bitmap_get(ss->app->msg_info->bits, - ss->app->msg_info->bits_bytes, - ss->curpos+j); - if (j == 0) { + for(j = 0; ss->curpos + j < ss->app->msg_info->pulses_count; j++) { + uint32_t l = + bitmap_get(ss->app->msg_info->bits, ss->app->msg_info->bits_bytes, ss->curpos + j); + if(j == 0) { /* At the first bit of this sequence, we store the * level of the sequence. */ level = l; @@ -244,22 +248,21 @@ LevelDuration radio_tx_feed_data(void *ctx) { /* As long as the level is the same, we update the duration. * Otherwise stop the loop and return this sample. */ - if (l != level) break; + if(l != level) break; dur += ss->app->msg_info->short_pulse_dur; } ss->curpos += j; /* If this was the last set of bits, change the state to * send the final gap. */ - if (ss->curpos >= ss->app->msg_info->pulses_count) - ss->state = SendSignalSendEndGap; + if(ss->curpos >= ss->app->msg_info->pulses_count) ss->state = SendSignalSendEndGap; return level_duration_make(level, dur); } /* Send end gap. */ - if (ss->state == SendSignalSendEndGap) { + if(ss->state == SendSignalSendEndGap) { ss->state = SendSignalEndTransmission; - return level_duration_make(0,ss->end_gap_dur); + return level_duration_make(0, ss->end_gap_dur); } /* End transmission. Here state is guaranteed @@ -268,7 +271,7 @@ LevelDuration radio_tx_feed_data(void *ctx) { } /* Vibrate and produce a click sound when a signal is sent. */ -void notify_signal_sent(ProtoViewApp *app) { +void notify_signal_sent(ProtoViewApp* app) { static const NotificationSequence sent_seq = { &message_blue_255, &message_vibro_on, @@ -277,59 +280,53 @@ void notify_signal_sent(ProtoViewApp *app) { &message_sound_off, &message_vibro_off, &message_blue_0, - NULL - }; + NULL}; notification_message(app->notification, &sent_seq); } /* Handle input for the info view. */ -void process_input_info(ProtoViewApp *app, InputEvent input) { +void process_input_info(ProtoViewApp* app, InputEvent input) { /* If we don't have a decoded signal, we don't allow to go up/down * in the subviews: they are only useful when a loaded signal. */ - if (app->signal_decoded && - ui_process_subview_updown(app,input,SubViewInfoLast)) return; + if(app->signal_decoded && ui_process_subview_updown(app, input, SubViewInfoLast)) return; - InfoViewPrivData *privdata = app->view_privdata; + InfoViewPrivData* privdata = app->view_privdata; int subview = ui_get_current_subview(app); /* Main subview. */ - if (subview == SubViewInfoMain) { - if (input.type == InputTypeLong && input.key == InputKeyOk) { + if(subview == SubViewInfoMain) { + if(input.type == InputTypeLong && input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { /* Show next info page. */ privdata->cur_info_page++; } - } else if (subview == SubViewInfoSave) { - /* Save subview. */ - if (input.type == InputTypePress && input.key == InputKeyRight) { + } else if(subview == SubViewInfoSave) { + /* Save subview. */ + if(input.type == InputTypePress && input.key == InputKeyRight) { privdata->signal_display_start_row++; - } else if (input.type == InputTypePress && input.key == InputKeyLeft) { - if (privdata->signal_display_start_row != 0) - privdata->signal_display_start_row--; - } else if (input.type == InputTypeLong && input.key == InputKeyOk) - { + } else if(input.type == InputTypePress && input.key == InputKeyLeft) { + if(privdata->signal_display_start_row != 0) privdata->signal_display_start_row--; + } else if(input.type == InputTypeLong && input.key == InputKeyOk) { // We have have the buffer already allocated, in case the // user aborted with BACK a previous saving. - if (privdata->filename == NULL) - privdata->filename = malloc(SAVE_FILENAME_LEN); - set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN); - ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, - text_input_done_callback); - } else if (input.type == InputTypeShort && input.key == InputKeyOk) { + if(privdata->filename == NULL) privdata->filename = malloc(SAVE_FILENAME_LEN); + set_signal_random_filename(app, privdata->filename, SAVE_FILENAME_LEN); + ui_show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN, text_input_done_callback); + } else if(input.type == InputTypeShort && input.key == InputKeyOk) { SendSignalCtx send_state; - send_signal_init(&send_state,app); - radio_tx_signal(app,radio_tx_feed_data,&send_state); + send_signal_init(&send_state, app); + radio_tx_signal(app, radio_tx_feed_data, &send_state); notify_signal_sent(app); } } } /* Called on view exit. */ -void view_exit_info(ProtoViewApp *app) { - InfoViewPrivData *privdata = app->view_privdata; +void view_exit_info(ProtoViewApp* app) { + InfoViewPrivData* privdata = app->view_privdata; // When the user aborts the keyboard input, we are left with the // filename buffer allocated. - if (privdata->filename) free(privdata->filename); + if(privdata->filename) free(privdata->filename); } diff --git a/applications/plugins/protoview/view_raw_signal.c b/applications/plugins/protoview/view_raw_signal.c index 023e986f9..38354bef9 100644 --- a/applications/plugins/protoview/view_raw_signal.c +++ b/applications/plugins/protoview/view_raw_signal.c @@ -12,7 +12,7 @@ * * The 'idx' argument is the first sample to render in the circular * buffer. */ -void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *buf, uint32_t idx) { +void render_signal(ProtoViewApp* app, Canvas* const canvas, RawSamplesBuffer* buf, uint32_t idx) { canvas_set_color(canvas, ColorBlack); int rows = 8; @@ -20,31 +20,29 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu uint32_t start_idx = idx; bool level = 0; uint32_t dur = 0, sample_num = 0; - for (int row = 0; row < rows ; row++) { - for (int x = 0; x < 128; x++) { - int y = 3 + row*8; - if (dur < time_per_pixel/2) { + for(int row = 0; row < rows; row++) { + for(int x = 0; x < 128; x++) { + int y = 3 + row * 8; + if(dur < time_per_pixel / 2) { /* Get more data. */ raw_samples_get(buf, idx++, &level, &dur); sample_num++; } - canvas_draw_line(canvas, x,y,x,y-(level*3)); + canvas_draw_line(canvas, x, y, x, y - (level * 3)); /* Write a small triangle under the last sample detected. */ - if (app->signal_bestlen != 0 && - sample_num+start_idx == app->signal_bestlen+1) - { - canvas_draw_dot(canvas,x,y+2); - canvas_draw_dot(canvas,x-1,y+3); - canvas_draw_dot(canvas,x,y+3); - canvas_draw_dot(canvas,x+1,y+3); + if(app->signal_bestlen != 0 && sample_num + start_idx == app->signal_bestlen + 1) { + canvas_draw_dot(canvas, x, y + 2); + canvas_draw_dot(canvas, x - 1, y + 3); + canvas_draw_dot(canvas, x, y + 3); + canvas_draw_dot(canvas, x + 1, y + 3); sample_num++; /* Make sure we don't mark the next, too. */ } /* Remove from the current level duration the time we * just plot. */ - if (dur > time_per_pixel) + if(dur > time_per_pixel) dur -= time_per_pixel; else dur = 0; @@ -53,61 +51,63 @@ void render_signal(ProtoViewApp *app, Canvas *const canvas, RawSamplesBuffer *bu } /* Raw pulses rendering. This is our default view. */ -void render_view_raw_pulses(Canvas *const canvas, ProtoViewApp *app) { +void render_view_raw_pulses(Canvas* const canvas, ProtoViewApp* app) { /* Show signal. */ render_signal(app, canvas, DetectedSamples, app->signal_offset); /* Show signal information. */ char buf[64]; - snprintf(buf,sizeof(buf),"%luus", - (unsigned long)DetectedSamples->short_pulse_dur); + snprintf(buf, sizeof(buf), "%luus", (unsigned long)DetectedSamples->short_pulse_dur); canvas_set_font(canvas, FontSecondary); canvas_draw_str_with_border(canvas, 97, 63, buf, ColorWhite, ColorBlack); - if (app->signal_decoded) { + if(app->signal_decoded) { canvas_set_font(canvas, FontPrimary); - canvas_draw_str_with_border(canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack); + canvas_draw_str_with_border( + canvas, 1, 61, app->msg_info->decoder->name, ColorWhite, ColorBlack); } } /* Handle input for the raw pulses view. */ -void process_input_raw_pulses(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypeRepeat) { +void process_input_raw_pulses(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypeRepeat) { /* Handle panning of the signal window. Long pressing * right will show successive samples, long pressing left * previous samples. */ - if (input.key == InputKeyRight) app->signal_offset++; - else if (input.key == InputKeyLeft) app->signal_offset--; - } else if (input.type == InputTypeLong) { - if (input.key == InputKeyOk) { + if(input.key == InputKeyRight) + app->signal_offset++; + else if(input.key == InputKeyLeft) + app->signal_offset--; + } else if(input.type == InputTypeLong) { + if(input.key == InputKeyOk) { /* Reset the current sample to capture the next. */ reset_current_signal(app); } - } else if (input.type == InputTypeShort) { - if (input.key == InputKeyOk) { + } else if(input.type == InputTypeShort) { + if(input.key == InputKeyOk) { app->signal_offset = 0; - adjust_raw_view_scale(app,DetectedSamples->short_pulse_dur); - } else if (input.key == InputKeyDown) { + adjust_raw_view_scale(app, DetectedSamples->short_pulse_dur); + } else if(input.key == InputKeyDown) { /* Rescaling. The set becomes finer under 50us per pixel. */ uint32_t scale_step = app->us_scale >= 50 ? 50 : 10; - if (app->us_scale < 500) app->us_scale += scale_step; - } else if (input.key == InputKeyUp) { + if(app->us_scale < 500) app->us_scale += scale_step; + } else if(input.key == InputKeyUp) { uint32_t scale_step = app->us_scale > 50 ? 50 : 10; - if (app->us_scale > 10) app->us_scale -= scale_step; + if(app->us_scale > 10) app->us_scale -= scale_step; } } } /* Adjust raw view scale depending on short pulse duration. */ -void adjust_raw_view_scale(ProtoViewApp *app, uint32_t short_pulse_dur) { - if (short_pulse_dur == 0) +void adjust_raw_view_scale(ProtoViewApp* app, uint32_t short_pulse_dur) { + if(short_pulse_dur == 0) app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; - else if (short_pulse_dur < 75) + else if(short_pulse_dur < 75) app->us_scale = 10; - else if (short_pulse_dur < 145) + else if(short_pulse_dur < 145) app->us_scale = 30; - else if (short_pulse_dur < 400) + else if(short_pulse_dur < 400) app->us_scale = 100; - else if (short_pulse_dur < 1000) + else if(short_pulse_dur < 1000) app->us_scale = 200; else app->us_scale = PROTOVIEW_RAW_VIEW_DEFAULT_SCALE; diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c index 1e2dce226..09abf5a2a 100644 --- a/applications/plugins/protoview/view_settings.c +++ b/applications/plugins/protoview/view_settings.c @@ -6,30 +6,30 @@ /* Renders a single view with frequency and modulation setting. However * this are logically two different views, and only one of the settings * will be highlighted. */ -void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { +void render_view_settings(Canvas* const canvas, ProtoViewApp* app) { canvas_set_font(canvas, FontPrimary); - if (app->current_view == ViewFrequencySettings) - canvas_draw_str_with_border(canvas,1,10,"Frequency",ColorWhite,ColorBlack); + if(app->current_view == ViewFrequencySettings) + canvas_draw_str_with_border(canvas, 1, 10, "Frequency", ColorWhite, ColorBlack); else - canvas_draw_str(canvas,1,10,"Frequency"); + canvas_draw_str(canvas, 1, 10, "Frequency"); - if (app->current_view == ViewModulationSettings) - canvas_draw_str_with_border(canvas,70,10,"Modulation",ColorWhite,ColorBlack); + if(app->current_view == ViewModulationSettings) + canvas_draw_str_with_border(canvas, 70, 10, "Modulation", ColorWhite, ColorBlack); else - canvas_draw_str(canvas,70,10,"Modulation"); + canvas_draw_str(canvas, 70, 10, "Modulation"); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas,10,61,"Use up and down to modify"); + canvas_draw_str(canvas, 10, 61, "Use up and down to modify"); - if (app->txrx->debug_timer_sampling) - canvas_draw_str(canvas,3,52,"(DEBUG timer sampling is ON)"); + if(app->txrx->debug_timer_sampling) + canvas_draw_str(canvas, 3, 52, "(DEBUG timer sampling is ON)"); /* Show frequency. We can use big numbers font since it's just a number. */ - if (app->current_view == ViewFrequencySettings) { + if(app->current_view == ViewFrequencySettings) { char buf[16]; - snprintf(buf,sizeof(buf),"%.2f",(double)app->frequency/1000000); + snprintf(buf, sizeof(buf), "%.2f", (double)app->frequency / 1000000); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str(canvas, 30, 40, buf); - } else if (app->current_view == ViewModulationSettings) { + } else if(app->current_view == ViewModulationSettings) { int current = app->modulation; canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 33, 39, ProtoViewModulations[current].name); @@ -37,13 +37,13 @@ void render_view_settings(Canvas *const canvas, ProtoViewApp *app) { } /* Handle input for the settings view. */ -void process_input_settings(ProtoViewApp *app, InputEvent input) { - if (input.type == InputTypeLong && input.key == InputKeyOk) { +void process_input_settings(ProtoViewApp* app, InputEvent input) { + if(input.type == InputTypeLong && input.key == InputKeyOk) { /* Long pressing to OK sets the default frequency and * modulation. */ app->frequency = subghz_setting_get_default_frequency(app->setting); app->modulation = 0; - } else if (0 && input.type == InputTypeLong && input.key == InputKeyDown) { + } else if(0 && input.type == InputTypeLong && input.key == InputKeyDown) { /* Long pressing to down switches between normal and debug * timer sampling mode. NOTE: this feature is disabled for users, * only useful for devs (if useful at all). */ @@ -55,42 +55,40 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) { app->txrx->debug_timer_sampling = !app->txrx->debug_timer_sampling; radio_begin(app); radio_rx(app); - } else if (input.type == InputTypePress && - (input.key != InputKeyDown || input.key != InputKeyUp)) - { + } else if(input.type == InputTypePress && (input.key != InputKeyDown || input.key != InputKeyUp)) { /* Handle up and down to change frequency or modulation. */ - if (app->current_view == ViewFrequencySettings) { + if(app->current_view == ViewFrequencySettings) { size_t curidx = 0, i; size_t count = subghz_setting_get_frequency_count(app->setting); /* Scan the list of frequencies to check for the index of the * currently set frequency. */ for(i = 0; i < count; i++) { - uint32_t freq = subghz_setting_get_frequency(app->setting,i); - if (freq == app->frequency) { + uint32_t freq = subghz_setting_get_frequency(app->setting, i); + if(freq == app->frequency) { curidx = i; break; } } - if (i == count) return; /* Should never happen. */ + if(i == count) return; /* Should never happen. */ - if (input.key == InputKeyUp) { - curidx = curidx == 0 ? count-1 : curidx-1; - } else if (input.key == InputKeyDown) { - curidx = (curidx+1) % count; + if(input.key == InputKeyUp) { + curidx = curidx == 0 ? count - 1 : curidx - 1; + } else if(input.key == InputKeyDown) { + curidx = (curidx + 1) % count; } else { return; } - app->frequency = subghz_setting_get_frequency(app->setting,curidx); - } else if (app->current_view == ViewModulationSettings) { + app->frequency = subghz_setting_get_frequency(app->setting, curidx); + } else if(app->current_view == ViewModulationSettings) { uint32_t count = 0; uint32_t modid = app->modulation; while(ProtoViewModulations[count].name != NULL) count++; - if (input.key == InputKeyUp) { - modid = modid == 0 ? count-1 : modid-1; - } else if (input.key == InputKeyDown) { - modid = (modid+1) % count; + if(input.key == InputKeyUp) { + modid = modid == 0 ? count - 1 : modid - 1; + } else if(input.key == InputKeyDown) { + modid = (modid + 1) % count; } else { return; } @@ -106,9 +104,13 @@ void process_input_settings(ProtoViewApp *app, InputEvent input) { /* When the user switches to some other view, if they changed the parameters * we need to restart the radio with the right frequency and modulation. */ -void view_exit_settings(ProtoViewApp *app) { - if (app->txrx->freq_mod_changed) { - FURI_LOG_E(TAG, "Setting view, setting frequency/modulation to %lu %s", app->frequency, ProtoViewModulations[app->modulation].name); +void view_exit_settings(ProtoViewApp* app) { + if(app->txrx->freq_mod_changed) { + FURI_LOG_E( + TAG, + "Setting view, setting frequency/modulation to %lu %s", + app->frequency, + ProtoViewModulations[app->modulation].name); radio_rx_end(app); radio_begin(app); radio_rx(app); diff --git a/applications/plugins/tama_p1/hal.c b/applications/plugins/tama_p1/hal.c index 211457803..585cd88c4 100644 --- a/applications/plugins/tama_p1/hal.c +++ b/applications/plugins/tama_p1/hal.c @@ -40,7 +40,7 @@ static void tama_p1_hal_log(log_level_t level, char* buff, ...) { va_list args; va_start(args, buff); furi_string_cat_vprintf(string, buff, args); - va_end(args); + va_end(args); switch(level) { case LOG_ERROR: diff --git a/applications/plugins/tama_p1/tama.h b/applications/plugins/tama_p1/tama.h index e2a267443..e8eecc945 100644 --- a/applications/plugins/tama_p1/tama.h +++ b/applications/plugins/tama_p1/tama.h @@ -13,7 +13,6 @@ #define STATE_FILE_VERSION 2 #define TAMA_SAVE_PATH EXT_PATH("tama_p1/save.bin") - typedef struct { FuriThread* thread; hal_t hal; diff --git a/applications/plugins/tama_p1/tama_p1.c b/applications/plugins/tama_p1/tama_p1.c index 7184638d7..0e7686a06 100644 --- a/applications/plugins/tama_p1/tama_p1.c +++ b/applications/plugins/tama_p1/tama_p1.c @@ -52,7 +52,6 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { uint16_t lcd_icon_lower_left = lcd_matrix_left; uint16_t lcd_icon_spacing_horiz = (lcd_matrix_scaled_width - (4 * TAMA_LCD_ICON_SIZE)) / 3 + TAMA_LCD_ICON_SIZE; - uint16_t y = lcd_matrix_top; for(uint8_t row = 0; row < 16; ++row) { @@ -71,7 +70,7 @@ static void tama_p1_draw_callback(Canvas* const canvas, void* cb_ctx) { // Start drawing icons uint8_t lcd_icons = g_ctx->icons; - + // Draw top icons y = lcd_icon_upper_top; // y = 64 - TAMA_LCD_ICON_SIZE; @@ -114,135 +113,134 @@ static void tama_p1_update_timer_callback(FuriMessageQueue* event_queue) { TamaEvent event = {.type = EventTypeTick}; furi_message_queue_put(event_queue, &event, 0); } - -static void tama_p1_load_state() { - state_t *state; + +static void tama_p1_load_state() { + state_t* state; uint8_t buf[4]; - bool error = false; + bool error = false; state = tamalib_get_state(); - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); - if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - - storage_file_read(file, &buf, 4); - if (buf[0] != (uint8_t) STATE_FILE_MAGIC[0] || buf[1] != (uint8_t) STATE_FILE_MAGIC[1] || - buf[2] != (uint8_t) STATE_FILE_MAGIC[2] || buf[3] != (uint8_t) STATE_FILE_MAGIC[3]) { + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); + if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + storage_file_read(file, &buf, 4); + if(buf[0] != (uint8_t)STATE_FILE_MAGIC[0] || buf[1] != (uint8_t)STATE_FILE_MAGIC[1] || + buf[2] != (uint8_t)STATE_FILE_MAGIC[2] || buf[3] != (uint8_t)STATE_FILE_MAGIC[3]) { FURI_LOG_E(TAG, "FATAL: Wrong state file magic in \"%s\" !\n", TAMA_SAVE_PATH); error = true; } - storage_file_read(file, &buf, 1); - if (buf[0] != STATE_FILE_VERSION) { + storage_file_read(file, &buf, 1); + if(buf[0] != STATE_FILE_VERSION) { FURI_LOG_E(TAG, "FATAL: Unsupported version"); error = true; } - if (!error) { + if(!error) { FURI_LOG_D(TAG, "Reading save.bin"); - storage_file_read(file, &buf, 2); - *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8); - - storage_file_read(file, &buf, 2); - *(state->x) = buf[0] | ((buf[1] & 0xF) << 8); + storage_file_read(file, &buf, 2); + *(state->pc) = buf[0] | ((buf[1] & 0x1F) << 8); - storage_file_read(file, &buf, 2); - *(state->y) = buf[0] | ((buf[1] & 0xF) << 8); + storage_file_read(file, &buf, 2); + *(state->x) = buf[0] | ((buf[1] & 0xF) << 8); - storage_file_read(file, &buf, 1); - *(state->a) = buf[0] & 0xF; + storage_file_read(file, &buf, 2); + *(state->y) = buf[0] | ((buf[1] & 0xF) << 8); - storage_file_read(file, &buf, 1); - *(state->b) = buf[0] & 0xF; + storage_file_read(file, &buf, 1); + *(state->a) = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); + *(state->b) = buf[0] & 0xF; + + storage_file_read(file, &buf, 1); *(state->np) = buf[0] & 0x1F; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->sp) = buf[0]; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->flags) = buf[0] & 0xF; - storage_file_read(file, &buf, 4); + storage_file_read(file, &buf, 4); *(state->tick_counter) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); - storage_file_read(file, &buf, 4); - *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + storage_file_read(file, &buf, 4); + *(state->clk_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | + (buf[3] << 24); - storage_file_read(file, &buf, 4); - *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + storage_file_read(file, &buf, 4); + *(state->prog_timer_timestamp) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | + (buf[3] << 24); - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_enabled) = buf[0] & 0x1; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_data) = buf[0]; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); *(state->prog_timer_rld) = buf[0]; - storage_file_read(file, &buf, 4); + storage_file_read(file, &buf, 4); *(state->call_depth) = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); FURI_LOG_D(TAG, "Restoring Interupts"); - for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < INT_SLOT_NUM; i++) { + storage_file_read(file, &buf, 1); state->interrupts[i].factor_flag_reg = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); state->interrupts[i].mask_reg = buf[0] & 0xF; - storage_file_read(file, &buf, 1); + storage_file_read(file, &buf, 1); state->interrupts[i].triggered = buf[0] & 0x1; } /* First 640 half bytes correspond to the RAM */ FURI_LOG_D(TAG, "Restoring RAM"); - for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + storage_file_read(file, &buf, 1); SET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR, buf[0] & 0xF); } /* I/Os are from 0xF00 to 0xF7F */ FURI_LOG_D(TAG, "Restoring I/O"); - for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { - storage_file_read(file, &buf, 1); + for(uint32_t i = 0; i < MEM_IO_SIZE; i++) { + storage_file_read(file, &buf, 1); SET_IO_MEMORY(state->memory, i + MEM_IO_ADDR, buf[0] & 0xF); - } - FURI_LOG_D(TAG, "Refreshing Hardware"); - tamalib_refresh_hw(); - } + } + FURI_LOG_D(TAG, "Refreshing Hardware"); + tamalib_refresh_hw(); + } } - + storage_file_close(file); storage_file_free(file); - furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_STORAGE); } - static void tama_p1_save_state() { - // Saving state FURI_LOG_D(TAG, "Saving Gamestate"); uint8_t buf[4]; - state_t *state; + state_t* state; uint32_t offset = 0; state = tamalib_get_state(); - - Storage* storage = furi_record_open(RECORD_STORAGE); - File* file = storage_file_alloc(storage); + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* file = storage_file_alloc(storage); if(storage_file_open(file, TAMA_SAVE_PATH, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { - buf[0] = (uint8_t) STATE_FILE_MAGIC[0]; - buf[1] = (uint8_t) STATE_FILE_MAGIC[1]; - buf[2] = (uint8_t) STATE_FILE_MAGIC[2]; - buf[3] = (uint8_t) STATE_FILE_MAGIC[3]; + buf[0] = (uint8_t)STATE_FILE_MAGIC[0]; + buf[1] = (uint8_t)STATE_FILE_MAGIC[1]; + buf[2] = (uint8_t)STATE_FILE_MAGIC[2]; + buf[3] = (uint8_t)STATE_FILE_MAGIC[3]; offset += storage_file_write(file, &buf, sizeof(buf)); - + buf[0] = STATE_FILE_VERSION & 0xFF; offset += storage_file_write(file, &buf, 1); - + buf[0] = *(state->pc) & 0xFF; buf[1] = (*(state->pc) >> 8) & 0x1F; offset += storage_file_write(file, &buf, 2); @@ -303,7 +301,7 @@ static void tama_p1_save_state() { buf[3] = (*(state->call_depth) >> 24) & 0xFF; offset += storage_file_write(file, &buf, sizeof(buf)); - for (uint32_t i = 0; i < INT_SLOT_NUM; i++) { + for(uint32_t i = 0; i < INT_SLOT_NUM; i++) { buf[0] = state->interrupts[i].factor_flag_reg & 0xF; offset += storage_file_write(file, &buf, 1); @@ -315,17 +313,17 @@ static void tama_p1_save_state() { } /* First 640 half bytes correspond to the RAM */ - for (uint32_t i = 0; i < MEM_RAM_SIZE; i++) { + for(uint32_t i = 0; i < MEM_RAM_SIZE; i++) { buf[0] = GET_RAM_MEMORY(state->memory, i + MEM_RAM_ADDR) & 0xF; offset += storage_file_write(file, &buf, 1); } /* I/Os are from 0xF00 to 0xF7F */ - for (uint32_t i = 0; i < MEM_IO_SIZE; i++) { + for(uint32_t i = 0; i < MEM_IO_SIZE; i++) { buf[0] = GET_IO_MEMORY(state->memory, i + MEM_IO_ADDR) & 0xF; offset += storage_file_write(file, &buf, 1); } - } + } storage_file_close(file); storage_file_free(file); furi_record_close(RECORD_STORAGE); @@ -333,7 +331,6 @@ static void tama_p1_save_state() { FURI_LOG_D(TAG, "Finished Writing %lu", offset); } - static int32_t tama_p1_worker(void* context) { bool running = true; FuriMutex* mutex = context; @@ -357,8 +354,6 @@ static int32_t tama_p1_worker(void* context) { furi_mutex_release(mutex); return 0; } - - static void tama_p1_init(TamaApp* const ctx) { g_ctx = ctx; @@ -485,9 +480,9 @@ int32_t tama_p1_app(void* p) { tamalib_set_button(BTN_MIDDLE, tama_btn_state); } else if(event.input.key == InputKeyRight) { tamalib_set_button(BTN_RIGHT, tama_btn_state); - } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { + } else if(event.input.key == InputKeyDown && event.input.type == InputTypeShort) { // TODO: pause or fast-forward tamagotchi - tama_p1_save_state(); + tama_p1_save_state(); } else if(event.input.key == InputKeyUp) { // mute tamagotchi tamalib_set_button(BTN_LEFT, tama_btn_state); tamalib_set_button(BTN_RIGHT, tama_btn_state); @@ -500,7 +495,7 @@ int32_t tama_p1_app(void* p) { furi_timer_stop(timer); running = false; - tama_p1_save_state(); + tama_p1_save_state(); } } diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index a993b6cae..ad3ae71c9 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -370,8 +370,8 @@ static void bt_close_connection(Bt* bt) { furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } -static void bt_restart(Bt *bt) { - if (bt->profile == BtProfileHidKeyboard) { +static void bt_restart(Bt* bt) { + if(bt->profile == BtProfileHidKeyboard) { furi_hal_bt_change_app(FuriHalBtProfileHidKeyboard, bt_on_gap_event_callback, bt); } else { furi_hal_bt_change_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt); @@ -379,7 +379,7 @@ static void bt_restart(Bt *bt) { furi_hal_bt_start_advertising(); } -void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) { +void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) { furi_assert(bt); furi_assert(fmt); @@ -388,7 +388,7 @@ void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) { va_start(args, fmt); vsnprintf(name, sizeof(name), fmt, args); va_end(args); - if (bt->profile == BtProfileHidKeyboard) { + if(bt->profile == BtProfileHidKeyboard) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, name); } else { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileSerial, name); @@ -397,46 +397,45 @@ void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...) { bt_restart(bt); } -const char *bt_get_profile_adv_name(Bt *bt) { +const char* bt_get_profile_adv_name(Bt* bt) { furi_assert(bt); - if (bt->profile == BtProfileHidKeyboard) { + if(bt->profile == BtProfileHidKeyboard) { return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard); } else { return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileSerial); } } -void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]) { +void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) { furi_assert(bt); furi_assert(mac); - if (bt->profile == BtProfileHidKeyboard) { + if(bt->profile == BtProfileHidKeyboard) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac); } else { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileSerial, mac); } - + bt_restart(bt); } -const uint8_t *bt_get_profile_mac_address(Bt *bt) { +const uint8_t* bt_get_profile_mac_address(Bt* bt) { furi_assert(bt); - if (bt->profile == BtProfileHidKeyboard) { + if(bt->profile == BtProfileHidKeyboard) { return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard); } else { return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileSerial); } } -bool bt_remote_rssi(Bt *bt, BtRssi *rssi) { +bool bt_remote_rssi(Bt* bt, BtRssi* rssi) { furi_assert(bt); UNUSED(rssi); uint8_t rssi_val; uint32_t since = furi_hal_bt_get_conn_rssi(&rssi_val); - if (since == 0) - return false; + if(since == 0) return false; rssi->rssi = rssi_val; rssi->since = since; diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index 2e6bbf1bb..60420a7f7 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -28,7 +28,6 @@ typedef struct { uint32_t since; } BtRssi; - typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); /** Change BLE Profile @@ -41,15 +40,13 @@ typedef void (*BtStatusChangedCallback)(BtStatus status, void* context); */ bool bt_set_profile(Bt* bt, BtProfile profile); +void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...); +const char* bt_get_profile_adv_name(Bt* bt); -void bt_set_profile_adv_name(Bt *bt, const char* fmt, ...); -const char *bt_get_profile_adv_name(Bt *bt); - -void bt_set_profile_mac_address(Bt *bt, const uint8_t mac[6]); -const uint8_t *bt_get_profile_mac_address(Bt *bt); - -bool bt_remote_rssi(Bt *bt, BtRssi *rssi); +void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]); +const uint8_t* bt_get_profile_mac_address(Bt* bt); +bool bt_remote_rssi(Bt* bt, BtRssi* rssi); /** Disconnect from Central * diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index c1eb50d59..5239d72d5 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -145,7 +145,8 @@ void animation_manager_check_blocking_process(AnimationManager* animation_manage const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(animation_manager->current_animation); - bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); + bool valid = animation_manager_is_valid_idle_animation( + manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); if(!valid) { animation_manager_start_new_idle(animation_manager); @@ -201,8 +202,10 @@ static void animation_manager_start_new_idle(AnimationManager* animation_manager animation_storage_get_bubble_animation(animation_manager->current_animation); animation_manager->state = AnimationManagerStateIdle; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims); - furi_timer_start(animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); + int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : + (xtreme_settings->cycle_anims); + furi_timer_start( + animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); } static bool animation_manager_check_blocking(AnimationManager* animation_manager) { @@ -355,7 +358,7 @@ static bool animation_manager_is_valid_idle_animation( result = (sd_status == FSE_NOT_READY); } - if (!unlock) { + if(!unlock) { if((stats->butthurt < info->min_butthurt) || (stats->butthurt > info->max_butthurt)) { result = false; } @@ -370,8 +373,9 @@ static bool animation_manager_is_valid_idle_animation( static StorageAnimation* animation_manager_select_idle_animation(AnimationManager* animation_manager) { const char* old_animation_name = NULL; - if (animation_manager->current_animation) { - old_animation_name = animation_storage_get_meta(animation_manager->current_animation)->name; + if(animation_manager->current_animation) { + old_animation_name = + animation_storage_get_meta(animation_manager->current_animation)->name; } StorageAnimationList_t animation_list; @@ -391,8 +395,8 @@ static StorageAnimation* animation_storage_get_meta(storage_animation); bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, unlock); - if (old_animation_name != NULL) { - if (strcmp(manifest_info->name, old_animation_name) == 0) { + if(old_animation_name != NULL) { + if(strcmp(manifest_info->name, old_animation_name) == 0) { valid = false; } } @@ -512,7 +516,8 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m furi_record_close(RECORD_DOLPHIN); const StorageAnimationManifestInfo* manifest_info = animation_storage_get_meta(restore_animation); - bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); + bool valid = animation_manager_is_valid_idle_animation( + manifest_info, &stats, XTREME_SETTINGS()->unlock_anims); if(valid) { animation_manager_replace_current_animation( animation_manager, restore_animation); @@ -523,12 +528,16 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m animation_manager->idle_animation_timer, animation_manager->freezed_animation_time_left); } else { - const BubbleAnimation* bubble_animation = animation_storage_get_bubble_animation( - animation_manager->current_animation); + const BubbleAnimation* bubble_animation = + animation_storage_get_bubble_animation( + animation_manager->current_animation); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - int32_t duration = (xtreme_settings->cycle_anims == 0) ? (bubble_animation->duration) : (xtreme_settings->cycle_anims); + int32_t duration = (xtreme_settings->cycle_anims == 0) ? + (bubble_animation->duration) : + (xtreme_settings->cycle_anims); furi_timer_start( - animation_manager->idle_animation_timer, (duration > 0) ? (duration * 1000) : 0); + animation_manager->idle_animation_timer, + (duration > 0) ? (duration * 1000) : 0); } } } else { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 28cdfe810..c717b0c36 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -35,18 +35,18 @@ void animation_handler_select_manifest() { FuriString* anim_dir = furi_string_alloc(); FuriString* manifest = furi_string_alloc(); bool use_asset_pack = xtreme_settings->asset_pack[0] != '\0'; - if (use_asset_pack) { + if(use_asset_pack) { furi_string_printf(anim_dir, "%s/%s/Anims", PACKS_DIR, xtreme_settings->asset_pack); furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); - if (storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { + if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { FURI_LOG_I(TAG, "Custom Manifest selected"); } else { use_asset_pack = false; } furi_record_close(RECORD_STORAGE); } - if (!use_asset_pack) { + if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); if(xtreme_settings->nsfw_mode) { furi_string_cat_str(anim_dir, "/nsfw"); @@ -58,7 +58,8 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); - strlcpy(ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE)); + strlcpy( + ANIMATION_MANIFEST_FILE, furi_string_get_cstr(manifest), sizeof(ANIMATION_MANIFEST_FILE)); furi_string_free(manifest); furi_string_free(anim_dir); } diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 4cdfa54da..594676c28 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -68,7 +68,7 @@ void desktop_lock_menu_draw_callback(Canvas* canvas, void* model) { str = "Set PIN + Off"; } } else if(i == DesktopLockMenuIndexXtremeSettings) { - str = "Xtreme Settings"; + str = "Xtreme Settings"; } if(str) //-V547 diff --git a/applications/services/dolphin/helpers/dolphin_state.c b/applications/services/dolphin/helpers/dolphin_state.c index 72fa75ac0..419245a7c 100644 --- a/applications/services/dolphin/helpers/dolphin_state.c +++ b/applications/services/dolphin/helpers/dolphin_state.c @@ -15,9 +15,10 @@ #define DOLPHIN_STATE_HEADER_MAGIC 0xD0 #define DOLPHIN_STATE_HEADER_VERSION 0x01 -const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, 1350, 1600, - 1850, 2100, 2400, 2700, 3000, 3350, 3700, 4050, 4450, 4850, - 5250, 5700, 6150, 6600, 7100, 7600, 8100, 8650, 9200}; +const int DOLPHIN_LEVELS[DOLPHIN_LEVEL_COUNT] = {100, 200, 300, 450, 600, 750, 950, 1150, + 1350, 1600, 1850, 2100, 2400, 2700, 3000, 3350, + 3700, 4050, 4450, 4850, 5250, 5700, 6150, 6600, + 7100, 7600, 8100, 8650, 9200}; #define BUTTHURT_MAX 14 #define BUTTHURT_MIN 0 diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index f0f7735fb..e52cb4e10 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -26,7 +26,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge); if((battery_style == BatteryStylePercent) && - (power->state != + (power->state != PowerStateCharging)) { //if display battery percentage, black background white text canvas_set_font(canvas, FontBatteryPercent); canvas_set_color(canvas, ColorBlack); @@ -36,7 +36,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { } else if( (battery_style == BatteryStyleInvertedPercent) && (power->state != - PowerStateCharging)) { //if display inverted percentage, white background black text + PowerStateCharging)) { //if display inverted percentage, white background black text canvas_set_font(canvas, FontBatteryPercent); canvas_set_color(canvas, ColorBlack); canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); @@ -74,7 +74,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { (battery_style == BatteryStyleBarPercent) && (power->state != PowerStateCharging) && // Default bar display with percentage (power->info.voltage_battery_charging >= - 4.2)) { // not looking nice with low voltage indicator + 4.2)) { // not looking nice with low voltage indicator canvas_set_font(canvas, FontBatteryPercent); // align charge dispaly value with digits to draw @@ -145,8 +145,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); // TODO: replace -1 magic for uint8_t with re-framing - if(battery_style == BatteryStylePercent || - battery_style == BatteryStyleBarPercent) { + if(battery_style == BatteryStylePercent || battery_style == BatteryStyleBarPercent) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 42712bc9c..918083265 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -222,9 +222,9 @@ static void draw_battery(Canvas* canvas, PowerInfo* info, int x, int y) { snprintf(header, sizeof(header), "Charged!"); } - if (!strcmp(value, "")) { + if(!strcmp(value, "")) { canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, header); - } else if (!strcmp(header, "")) { + } else if(!strcmp(header, "")) { canvas_draw_str_aligned(canvas, x + 92, y + 14, AlignCenter, AlignCenter, value); } else { canvas_draw_str_aligned(canvas, x + 92, y + 9, AlignCenter, AlignCenter, header); @@ -298,7 +298,6 @@ const AboutDialogScreen about_screens[] = { const int about_screens_count = sizeof(about_screens) / sizeof(AboutDialogScreen); - int32_t about_settings_app(void* p) { bool battery_info = false; if(p && strlen(p) && !strcmp(p, "batt")) { @@ -324,24 +323,19 @@ int32_t about_settings_app(void* p) { DialogMessageButton screen_result; // draw empty screen to prevent menu flickering - view_dispatcher_add_view( - view_dispatcher, battery_info_index, battery_view); + view_dispatcher_add_view(view_dispatcher, battery_info_index, battery_view); view_dispatcher_add_view( view_dispatcher, empty_screen_index, empty_screen_get_view(empty_screen)); view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); screen_index = -1 + !battery_info; while(screen_index > -2) { - - if (screen_index == -1) { - if (!battery_info) { + if(screen_index == -1) { + if(!battery_info) { break; } with_view_model( - battery_view, - PowerInfo * model, - { power_get_info(power, model); }, - true); + battery_view, PowerInfo * model, { power_get_info(power, model); }, true); view_dispatcher_switch_to_view(view_dispatcher, battery_info_index); furi_semaphore_acquire(semaphore, 2000); } else { @@ -360,7 +354,6 @@ int32_t about_settings_app(void* p) { screen_index = -2; } } - } dialog_message_free(message); diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 3bea705ac..f0430de5d 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -67,7 +67,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { uint32_t xp_need = dolphin_state_xp_to_levelup(stats->icounter); uint32_t xp_above_last_levelup = dolphin_state_xp_above_last_levelup(stats->icounter); uint32_t xp_levelup = 0; - if (ctx->progress_total) { + if(ctx->progress_total) { xp_levelup = xp_need + stats->icounter; } else { xp_levelup = xp_need + xp_above_last_levelup; diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 90bdde83f..53d679efb 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -15,8 +15,12 @@ static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1)); - strlcpy(XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), MAX_PACK_NAME_LEN); + variable_item_set_current_value_text( + item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1)); + strlcpy( + XTREME_SETTINGS()->asset_pack, + index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), + MAX_PACK_NAME_LEN); app->settings_changed = true; app->assets_changed = true; } @@ -33,8 +37,20 @@ static void xtreme_settings_scene_start_anim_speed_changed(VariableItem* item) { app->settings_changed = true; } -const char* const cycle_anims_names[] = - {"OFF", "Meta.txt", "30 S", "1 M", "5 M", "10 M", "15 M", "30 M", "1 H", "2 H", "6 H", "12 H", "24 H"}; +const char* const cycle_anims_names[] = { + "OFF", + "Meta.txt", + "30 S", + "1 M", + "5 M", + "10 M", + "15 M", + "30 M", + "1 H", + "2 H", + "6 H", + "12 H", + "24 H"}; const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; static void xtreme_settings_scene_start_cycle_anims_changed(VariableItem* item) { @@ -62,8 +78,7 @@ const int32_t battery_style_values[COUNT_OF(battery_style_names)] = { BatteryStyleInvertedPercent, BatteryStyleRetro3, BatteryStyleRetro5, - BatteryStyleBarPercent -}; + BatteryStyleBarPercent}; static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -112,7 +127,8 @@ void xtreme_settings_scene_start_on_enter(void* context) { app->subghz_extend = false; app->subghz_bypass = false; if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + flipper_format_read_bool( + subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); flipper_format_read_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); } flipper_format_free(subghz_range); @@ -123,14 +139,15 @@ void xtreme_settings_scene_start_on_enter(void* context) { FileInfo info; char* name = malloc(MAX_PACK_NAME_LEN); do { - if (!storage_dir_open(folder, PACKS_DIR)) break; + if(!storage_dir_open(folder, PACKS_DIR)) break; while(true) { - if (!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break; + if(!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break; if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); asset_packs_push_back(app->asset_packs, copy); - if (strcmp(name, xtreme_settings->asset_pack) == 0) current_pack = asset_packs_size(app->asset_packs); + if(strcmp(name, xtreme_settings->asset_pack) == 0) + current_pack = asset_packs_size(app->asset_packs); } } } while(false); @@ -139,11 +156,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { furi_record_close(RECORD_STORAGE); item = variable_item_list_add( - var_item_list, - "Base Graphics", - 2, - xtreme_settings_scene_start_base_graphics_changed, - app); + var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); @@ -154,7 +167,8 @@ void xtreme_settings_scene_start_on_enter(void* context) { xtreme_settings_scene_start_asset_pack_changed, app); variable_item_set_current_value_index(item, current_pack); - variable_item_set_current_value_text(item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1)); + variable_item_set_current_value_text( + item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1)); item = variable_item_list_add( var_item_list, @@ -179,11 +193,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, cycle_anims_names[value_index]); item = variable_item_list_add( - var_item_list, - "Unlock Anims", - 2, - xtreme_settings_scene_start_unlock_anims_changed, - app); + var_item_list, "Unlock Anims", 2, xtreme_settings_scene_start_unlock_anims_changed, app); variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); @@ -210,30 +220,18 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, level_str); item = variable_item_list_add( - var_item_list, - "SubGHz Extend", - 2, - xtreme_settings_scene_start_subghz_extend_changed, - app); + var_item_list, "SubGHz Extend", 2, xtreme_settings_scene_start_subghz_extend_changed, app); variable_item_set_current_value_index(item, app->subghz_extend); variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); item = variable_item_list_add( - var_item_list, - "SubGHz Bypass", - 2, - xtreme_settings_scene_start_subghz_bypass_changed, - app); + var_item_list, "SubGHz Bypass", 2, xtreme_settings_scene_start_subghz_bypass_changed, app); variable_item_set_current_value_index(item, app->subghz_bypass); variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - FuriString* version_tag = furi_string_alloc_printf("%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); - item = variable_item_list_add( - var_item_list, - furi_string_get_cstr(version_tag), - 0, - NULL, - app); + FuriString* version_tag = furi_string_alloc_printf( + "%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); + item = variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList); } @@ -248,7 +246,7 @@ bool xtreme_settings_scene_start_on_event(void* context, SceneManagerEvent event void xtreme_settings_scene_start_on_exit(void* context) { XtremeSettingsApp* app = context; asset_packs_it_t it; - for (asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { + for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { free(*asset_packs_cref(it)); } asset_packs_clear(app->asset_packs); diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 444b50951..0f6ab998d 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -5,94 +5,158 @@ XtremeAssets* xtreme_assets = NULL; XtremeAssets* XTREME_ASSETS() { - if (xtreme_assets == NULL) { + if(xtreme_assets == NULL) { XTREME_ASSETS_LOAD(); } return xtreme_assets; } void XTREME_ASSETS_LOAD() { - if (xtreme_assets != NULL) return; + if(xtreme_assets != NULL) return; xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - if (xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + if(xtreme_settings->nsfw_mode) { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_flipper; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_flipper; + xtreme_assets->I_passport_okay_46x49 = &I_flipper; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = + &I_iButtonDolphinVerySuccess_108x52_sfw; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; + xtreme_assets->I_passport_DB = &I_passport_DB_sfw; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; + xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; } - if (xtreme_settings->asset_pack[0] == '\0') return; + if(xtreme_settings->asset_pack[0] == '\0') return; FileInfo info; FuriString* path = furi_string_alloc(); const char* pack = xtreme_settings->asset_pack; furi_string_printf(path, PACKS_DIR "/%s", pack); Storage* storage = furi_record_open(RECORD_STORAGE); - if (storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - swap_bmx_icon(&xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinCommon_56x48, pack, "Dolphin/DolphinCommon_56x48.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinMafia_115x62, pack, "iButton/DolphinMafia_115x62.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_iButtonDolphinVerySuccess_108x52, pack, "iButton/iButtonDolphinVerySuccess_108x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_DolphinReadingSuccess_59x63, pack, "Infrared/DolphinReadingSuccess_59x63.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_NFC_dolphin_emulation_47x61, pack, "NFC/NFC_dolphin_emulation_47x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_bad_46x49, pack, "Passport/passport_bad_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_happy_46x49, pack, "Passport/passport_happy_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_passport_okay_46x49, pack, "Passport/passport_okay_46x49.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinReceive_97x61, pack, "RFID/RFIDDolphinReceive_97x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSend_97x61, pack, "RFID/RFIDDolphinSend_97x61.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_RFIDDolphinSuccess_108x57, pack, "RFID/RFIDDolphinSuccess_108x57.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinCommon_56x48, + pack, + "Dolphin/DolphinCommon_56x48.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinMafia_115x62, + pack, + "iButton/DolphinMafia_115x62.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, + pack, + "iButton/iButtonDolphinVerySuccess_108x52.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinReadingSuccess_59x63, + pack, + "Infrared/DolphinReadingSuccess_59x63.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_NFC_dolphin_emulation_47x61, + pack, + "NFC/NFC_dolphin_emulation_47x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_bad_46x49, + pack, + "Passport/passport_bad_46x49.bmx", + path, + file); + swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_passport_happy_46x49, + pack, + "Passport/passport_happy_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_okay_46x49, + pack, + "Passport/passport_okay_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinReceive_97x61, + pack, + "RFID/RFIDDolphinReceive_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSend_97x61, + pack, + "RFID/RFIDDolphinSend_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSuccess_108x57, + pack, + "RFID/RFIDDolphinSuccess_108x57.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); storage_file_free(file); } @@ -100,9 +164,14 @@ void XTREME_ASSETS_LOAD() { furi_string_free(path); } -void swap_bmx_icon(const Icon** replace, const char* pack, const char* name, FuriString* path, File* file) { +void swap_bmx_icon( + const Icon** replace, + const char* pack, + const char* name, + FuriString* path, + File* file) { furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); - if (storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; storage_file_read(file, &width, 4); diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index df618c47d..c49f5b590 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -34,6 +34,11 @@ XtremeAssets* XTREME_ASSETS(); void XTREME_ASSETS_LOAD(); -void swap_bmx_icon(const Icon** replace, const char* base, const char* name, FuriString* path, File* file); +void swap_bmx_icon( + const Icon** replace, + const char* base, + const char* name, + FuriString* path, + File* file); void free_bmx_icon(Icon* icon); diff --git a/applications/settings/xtreme_settings/xtreme_settings.c b/applications/settings/xtreme_settings/xtreme_settings.c index 84018bcf0..3db0a4c1c 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.c +++ b/applications/settings/xtreme_settings/xtreme_settings.c @@ -3,17 +3,21 @@ XtremeSettings* xtreme_settings = NULL; XtremeSettings* XTREME_SETTINGS() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } return xtreme_settings; } bool XTREME_SETTINGS_LOAD() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { xtreme_settings = malloc(sizeof(XtremeSettings)); bool loaded = saved_struct_load( - XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION); + XTREME_SETTINGS_PATH, + xtreme_settings, + sizeof(XtremeSettings), + XTREME_SETTINGS_MAGIC, + XTREME_SETTINGS_VERSION); if(!loaded) { memset(xtreme_settings, 0, sizeof(XtremeSettings)); loaded = XTREME_SETTINGS_SAVE(); @@ -24,9 +28,13 @@ bool XTREME_SETTINGS_LOAD() { } bool XTREME_SETTINGS_SAVE() { - if (xtreme_settings == NULL) { + if(xtreme_settings == NULL) { XTREME_SETTINGS_LOAD(); } return saved_struct_save( - XTREME_SETTINGS_PATH, xtreme_settings, sizeof(XtremeSettings), XTREME_SETTINGS_MAGIC, XTREME_SETTINGS_VERSION); + XTREME_SETTINGS_PATH, + xtreme_settings, + sizeof(XtremeSettings), + XTREME_SETTINGS_MAGIC, + XTREME_SETTINGS_VERSION); } diff --git a/applications/settings/xtreme_settings/xtreme_settings_app.c b/applications/settings/xtreme_settings/xtreme_settings_app.c index 780e76730..6b0a12f1c 100644 --- a/applications/settings/xtreme_settings/xtreme_settings_app.c +++ b/applications/settings/xtreme_settings/xtreme_settings_app.c @@ -15,10 +15,10 @@ static bool xtreme_settings_back_event_callback(void* context) { furi_assert(context); XtremeSettingsApp* app = context; - if (app->level_changed) { + if(app->level_changed) { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); - if (app->dolphin_level != stats.level) { + if(app->dolphin_level != stats.level) { int xp = app->dolphin_level > 1 ? dolphin_get_levels()[app->dolphin_level - 2] : 0; dolphin->state->data.icounter = xp + 1; dolphin->state->dirty = true; @@ -27,20 +27,22 @@ static bool xtreme_settings_back_event_callback(void* context) { furi_record_close(RECORD_DOLPHIN); } - if (app->subghz_changed) { + if(app->subghz_changed) { Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* subghz_range = flipper_format_file_alloc(storage); if(flipper_format_file_open_existing(subghz_range, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_insert_or_update_bool(subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); - flipper_format_insert_or_update_bool(subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); + flipper_format_insert_or_update_bool( + subghz_range, "use_ext_range_at_own_risk", &app->subghz_extend, 1); + flipper_format_insert_or_update_bool( + subghz_range, "ignore_default_tx_region", &app->subghz_bypass, 1); } flipper_format_free(subghz_range); furi_record_close(RECORD_STORAGE); } - if (app->settings_changed) { + if(app->settings_changed) { XTREME_SETTINGS_SAVE(); - if (app->assets_changed) { + if(app->assets_changed) { popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter); popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter); popup_set_callback(app->popup, xtreme_settings_reboot); @@ -81,9 +83,7 @@ XtremeSettingsApp* xtreme_settings_app_alloc() { app->popup = popup_alloc(); view_dispatcher_add_view( - app->view_dispatcher, - XtremeSettingsAppViewPopup, - popup_get_view(app->popup)); + app->view_dispatcher, XtremeSettingsAppViewPopup, popup_get_view(app->popup)); // Set first scene scene_manager_next_scene(app->scene_manager, XtremeSettingsAppSceneStart); diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 8023b8915..725a69dfb 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -56,8 +56,8 @@ static Gap* gap = NULL; static inline void fetch_rssi() { uint8_t ret_rssi = 127; - if (hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) { - gap->conn_rssi = (int8_t) ret_rssi; + if(hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) { + gap->conn_rssi = (int8_t)ret_rssi; gap->time_rssi_sample = furi_get_tick(); return; } @@ -166,7 +166,6 @@ SVCCTL_UserEvtFlowStatus_t SVCCTL_App_Notification(void* pckt) { gap->connection_params.slave_latency = event->Conn_Latency; gap->connection_params.supervisor_timeout = event->Supervision_Timeout; - // Stop advertising as connection completed furi_timer_stop(gap->advertise_timer); @@ -383,7 +382,7 @@ static void gap_init_svc(Gap* gap) { keypress_supported, CFG_ENCRYPTION_KEY_SIZE_MIN, CFG_ENCRYPTION_KEY_SIZE_MAX, - CFG_USED_FIXED_PIN, // 0x0 for no pin + CFG_USED_FIXED_PIN, // 0x0 for no pin 0, PUBLIC_ADDR); // Configure whitelist @@ -504,9 +503,15 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { // Initialization of GATT & GAP layer gap->service.adv_name = config->adv_name; FURI_LOG_I(TAG, "Advertising name: %s", &(gap->service.adv_name[1])); - FURI_LOG_I(TAG, "MAC @ : %02X:%02X:%02X:%02X:%02X:%02X", - config->mac_address[0], config->mac_address[1], config->mac_address[2], - config->mac_address[3], config->mac_address[4], config->mac_address[5]); + FURI_LOG_I( + TAG, + "MAC @ : %02X:%02X:%02X:%02X:%02X:%02X", + config->mac_address[0], + config->mac_address[1], + config->mac_address[2], + config->mac_address[3], + config->mac_address[4], + config->mac_address[5]); gap_init_svc(gap); // Initialization of the BLE Services SVCCTL_Init(); @@ -538,15 +543,12 @@ bool gap_init(GapConfig* config, GapEventCallback on_event_cb, void* context) { return true; } -uint32_t gap_get_remote_conn_rssi(int8_t *rssi) { - if (gap && gap->state == GapStateConnected) { +uint32_t gap_get_remote_conn_rssi(int8_t* rssi) { + if(gap && gap->state == GapStateConnected) { fetch_rssi(); *rssi = gap->conn_rssi; - FURI_LOG_D(TAG, "RSSI: %d", *rssi); - - if (gap->time_rssi_sample) - return furi_get_tick() - gap->time_rssi_sample; + if(gap->time_rssi_sample) return furi_get_tick() - gap->time_rssi_sample; } return 0; } diff --git a/firmware/targets/f7/ble_glue/gap.h b/firmware/targets/f7/ble_glue/gap.h index 3b9ca5b28..7b317e06c 100644 --- a/firmware/targets/f7/ble_glue/gap.h +++ b/firmware/targets/f7/ble_glue/gap.h @@ -81,7 +81,7 @@ GapState gap_get_state(); void gap_thread_stop(); -uint32_t gap_get_remote_conn_rssi(int8_t *rssi); +uint32_t gap_get_remote_conn_rssi(int8_t* rssi); #ifdef __cplusplus } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index f01b66d14..1e7b80040 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -429,15 +429,13 @@ float furi_hal_bt_get_rssi() { * the beginning of the connection * */ -uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi) { - +uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) { int8_t ret_rssi = 0; uint32_t since = gap_get_remote_conn_rssi(&ret_rssi); - if (ret_rssi == 127 || since == 0) - return 0; + if(ret_rssi == 127 || since == 0) return 0; - *rssi = (uint8_t) abs(ret_rssi); + *rssi = (uint8_t)abs(ret_rssi); return since; } @@ -469,12 +467,14 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode) { void furi_hal_bt_set_profile_adv_name( FuriHalBtProfile profile, - const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]) { + const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]) { furi_assert(profile < FuriHalBtProfileNumber); furi_assert(name); memcpy( - &(profile_config[profile].config.adv_name[1]), name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1); + &(profile_config[profile].config.adv_name[1]), + name, + FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1); } const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 501704420..424999fc0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -199,8 +199,7 @@ bool furi_hal_bt_hid_kb_press(uint16_t button) { bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) { furi_assert(kb_report); for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { - if(kb_report->key[i] == 0) - n_empty_slots--; + if(kb_report->key[i] == 0) n_empty_slots--; } return (n_empty_slots == 0); } diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 45d2a4261..fb17436f4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -228,19 +228,23 @@ bool furi_hal_bt_ensure_c2_mode(BleGlueC2Mode mode); * @param[in] profile profile type * @param[in] name new adv name */ -void furi_hal_bt_set_profile_adv_name(FuriHalBtProfile profile, const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH-1]); +void furi_hal_bt_set_profile_adv_name( + FuriHalBtProfile profile, + const char name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH - 1]); -const char *furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile); +const char* furi_hal_bt_get_profile_adv_name(FuriHalBtProfile profile); /** Modify profile mac address and restart bluetooth * @param[in] profile profile type * @param[in] mac new mac address */ -void furi_hal_bt_set_profile_mac_addr(FuriHalBtProfile profile, const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); +void furi_hal_bt_set_profile_mac_addr( + FuriHalBtProfile profile, + const uint8_t mac_addr[GAP_MAC_ADDR_SIZE]); -const uint8_t *furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile); +const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile); -uint32_t furi_hal_bt_get_conn_rssi(uint8_t *rssi); +uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi); #ifdef __cplusplus } diff --git a/flipper.log b/flipper.log deleted file mode 100644 index f267750c0..000000000 --- a/flipper.log +++ /dev/null @@ -1,3146 +0,0 @@ - - _.-------.._ -, - .-"```"--..,,_/ /`-, -, \ - .:" /:/ /'\ \ ,_..., `. | | - / ,----/:/ /`\ _\~`_-"` _; - ' / /`"""'\ \ \.~`_-' ,-"'/ - | | | 0 | | .-' ,/` / - | ,..\ \ ,.-"` ,/` / - ; : `/`""\` ,/--==,/-----, - | `-...| -.___-Z:_______J...---; - : ` _-' - _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ -| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| -| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | -|_| |____||___||_| |_| |___||_|_\ \___||____||___| - -Welcome to Flipper Zero Command Line Interface! -Read Manual https://docs.flipperzero.one - -Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) - ->: log -Press CTRL+C to stop... -218220 [D][BrowserWorker] End -218233 [I][BtGap] Stop advertising -218237 [D][BtGap] set_non_discoverable success -218252 [I][SavedStruct] Loading "/int/.desktop.settings" -218390 [I][SavedStruct] Loading "/int/.desktop.settings" -218442 [I][FuriHalBt] Disconnect and stop advertising -218444 [I][FuriHalBt] Stop current profile services -218453 [I][FuriHalBt] Stop BLE related RTOS threads -218479 [I][FuriHalBt] Reset SHCI -218590 [I][SavedStruct] Loading "/int/.desktop.settings" -218594 [I][FuriHalBt] Start BT initialization -218601 [I][Core2] Core2 started -218604 [I][Core2] C2 boot completed, mode: Stack -218608 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -218612 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -218619 [I][Core2] Radio stack started -218623 [I][Core2] Flash activity control switched to SEM7 -218626 [I][BtGap] Advertising name: Keyboard -218629 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -218646 [D][BtBatterySvc] Updating power state characteristic -218651 [I][BtGap] Start advertising -218654 [I][SavedStruct] Loading "/int/.bt.settings" -218671 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -218681 [I][FuriHalBt] Disconnect and stop advertising -218683 [I][BtGap] Stop advertising -218686 [D][BtGap] set_non_discoverable success -218689 [I][FuriHalBt] Stop current profile services -218698 [I][FuriHalBt] Stop BLE related RTOS threads -218723 [I][FuriHalBt] Reset SHCI -218748 [I][SavedStruct] Loading "/int/.desktop.settings" -218790 [I][SavedStruct] Loading "/int/.desktop.settings" -218837 [I][FuriHalBt] Start BT initialization -218842 [I][Core2] Core2 started -218844 [I][Core2] C2 boot completed, mode: Stack -218847 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -218849 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -218854 [I][Core2] Radio stack started -218856 [I][Core2] Flash activity control switched to SEM7 -218859 [I][BtGap] Advertising name: Rumik1 -218861 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -218879 [D][BtBatterySvc] Updating power state characteristic -218889 [I][BtSrv] Bt App started -218891 [I][BtGap] Start advertising -218894 [I][BadBleWorker] Init -218896 [I][SavedStruct] Loading "/int/.desktop.settings" -218923 [I][BadBleWorker] BLE Key timeout : 16 -218990 [I][SavedStruct] Loading "/int/.desktop.settings" -219190 [I][SavedStruct] Loading "/int/.desktop.settings" -219248 [I][SavedStruct] Loading "/int/.desktop.settings" -219285 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -219287 [I][BtGap] Rssi: 295 -219390 [D][BtGap] Slave security initiated -219392 [I][SavedStruct] Loading "/int/.desktop.settings" -219508 [I][BtGap] Pairing complete -219511 [D][BtBatterySvc] Updating battery level characteristic -219515 [I][BadBleWorker] BLE Key timeout : 16 -219590 [I][SavedStruct] Loading "/int/.desktop.settings" -219687 [I][BtGap] Rx MTU size: 414 -219748 [I][SavedStruct] Loading "/int/.desktop.settings" -219790 [I][SavedStruct] Loading "/int/.desktop.settings" -219990 [I][SavedStruct] Loading "/int/.desktop.settings" -220190 [I][SavedStruct] Loading "/int/.desktop.settings" -220248 [I][SavedStruct] Loading "/int/.desktop.settings" -220282 [I][BtGap] Connection parameters event complete -220284 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -220287 [W][BtGap] Unsupported connection interval. Request connection parameters update -220290 [I][BtGap] Rssi: 298 -220335 [D][BtGap] Connection parameters accepted -220390 [I][SavedStruct] Loading "/int/.desktop.settings" -220498 [I][BtGap] Connection parameters event complete -220500 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -220506 [I][BtGap] Rssi: 293 -220590 [I][SavedStruct] Loading "/int/.desktop.settings" -220748 [I][SavedStruct] Loading "/int/.desktop.settings" -220790 [I][SavedStruct] Loading "/int/.desktop.settings" -220990 [I][SavedStruct] Loading "/int/.desktop.settings" -221190 [I][SavedStruct] Loading "/int/.desktop.settings" -221248 [I][SavedStruct] Loading "/int/.desktop.settings" -221390 [I][SavedStruct] Loading "/int/.desktop.settings" -221590 [I][SavedStruct] Loading "/int/.desktop.settings" -221748 [I][SavedStruct] Loading "/int/.desktop.settings" -221790 [I][SavedStruct] Loading "/int/.desktop.settings" -221990 [I][SavedStruct] Loading "/int/.desktop.settings" -222190 [I][SavedStruct] Loading "/int/.desktop.settings" -222248 [I][SavedStruct] Loading "/int/.desktop.settings" -222390 [I][SavedStruct] Loading "/int/.desktop.settings" -222590 [I][SavedStruct] Loading "/int/.desktop.settings" -222748 [I][SavedStruct] Loading "/int/.desktop.settings" -222790 [I][SavedStruct] Loading "/int/.desktop.settings" -222990 [I][SavedStruct] Loading "/int/.desktop.settings" -223190 [I][SavedStruct] Loading "/int/.desktop.settings" -223248 [I][SavedStruct] Loading "/int/.desktop.settings" -223390 [I][SavedStruct] Loading "/int/.desktop.settings" -223590 [I][SavedStruct] Loading "/int/.desktop.settings" -223748 [I][SavedStruct] Loading "/int/.desktop.settings" -223790 [I][SavedStruct] Loading "/int/.desktop.settings" -223990 [I][SavedStruct] Loading "/int/.desktop.settings" -224190 [I][SavedStruct] Loading "/int/.desktop.settings" -224248 [I][SavedStruct] Loading "/int/.desktop.settings" -224390 [I][SavedStruct] Loading "/int/.desktop.settings" -224590 [I][SavedStruct] Loading "/int/.desktop.settings" -224748 [I][SavedStruct] Loading "/int/.desktop.settings" -224790 [I][SavedStruct] Loading "/int/.desktop.settings" -224990 [I][SavedStruct] Loading "/int/.desktop.settings" -225190 [I][SavedStruct] Loading "/int/.desktop.settings" -225248 [I][SavedStruct] Loading "/int/.desktop.settings" -225390 [I][SavedStruct] Loading "/int/.desktop.settings" -225590 [I][SavedStruct] Loading "/int/.desktop.settings" -225748 [I][SavedStruct] Loading "/int/.desktop.settings" -225790 [I][SavedStruct] Loading "/int/.desktop.settings" -225990 [I][SavedStruct] Loading "/int/.desktop.settings" -226190 [I][SavedStruct] Loading "/int/.desktop.settings" -226248 [I][SavedStruct] Loading "/int/.desktop.settings" -226390 [I][SavedStruct] Loading "/int/.desktop.settings" -226596 [I][SavedStruct] Loading "/int/.desktop.settings" -226748 [I][SavedStruct] Loading "/int/.desktop.settings" -226790 [I][SavedStruct] Loading "/int/.desktop.settings" -226990 [I][SavedStruct] Loading "/int/.desktop.settings" -227190 [I][SavedStruct] Loading "/int/.desktop.settings" -227248 [I][SavedStruct] Loading "/int/.desktop.settings" -227390 [I][SavedStruct] Loading "/int/.desktop.settings" -227590 [I][SavedStruct] Loading "/int/.desktop.settings" -227748 [I][SavedStruct] Loading "/int/.desktop.settings" -227790 [I][SavedStruct] Loading "/int/.desktop.settings" -227990 [I][SavedStruct] Loading "/int/.desktop.settings" -228190 [I][SavedStruct] Loading "/int/.desktop.settings" -228248 [I][SavedStruct] Loading "/int/.desktop.settings" -228390 [I][SavedStruct] Loading "/int/.desktop.settings" -228590 [I][SavedStruct] Loading "/int/.desktop.settings" -228748 [I][SavedStruct] Loading "/int/.desktop.settings" -228790 [I][SavedStruct] Loading "/int/.desktop.settings" -234485 [I][FuriHalBt] Disconnect and stop advertising -234489 [I][BtGap] Stop advertising -234493 [D][BtGap] terminate success -234497 [E][BtGap] set_non_discoverable failed 12 -234502 [I][FuriHalBt] Stop current profile services -234507 [I][BadBleWorker] BLE Key timeout : 16 -234526 [I][FuriHalBt] Stop BLE related RTOS threads -234551 [I][FuriHalBt] Reset SHCI -234665 [I][FuriHalBt] Start BT initialization -234670 [I][Core2] Core2 started -234672 [I][Core2] C2 boot completed, mode: Stack -234675 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -234677 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -234681 [I][Core2] Radio stack started -234683 [I][Core2] Flash activity control switched to SEM7 -234685 [I][BtGap] Advertising name: Mmm_ -234687 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -234711 [D][BtBatterySvc] Updating power state characteristic -234721 [I][BtGap] Start advertising -235977 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -235982 [I][BtGap] Rssi: 308 -236066 [D][BtGap] Slave security initiated -236187 [I][BtGap] Pairing complete -236190 [D][BtBatterySvc] Updating battery level characteristic -236195 [I][BadBleWorker] BLE Key timeout : 16 -236366 [I][BtGap] Rx MTU size: 414 -236935 [I][BtGap] Connection parameters event complete -236938 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -236941 [W][BtGap] Unsupported connection interval. Request connection parameters update -236944 [I][BtGap] Rssi: 314 -236975 [D][BtGap] Connection parameters accepted -237139 [I][BtGap] Connection parameters event complete -237141 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -237144 [I][BtGap] Rssi: 326 -240985 [I][FuriHalBt] Disconnect and stop advertising -240989 [I][BtGap] Stop advertising -240993 [D][BtGap] terminate success -240997 [E][BtGap] set_non_discoverable failed 12 -241002 [I][FuriHalBt] Stop current profile services -241007 [I][BadBleWorker] BLE Key timeout : 16 -241028 [I][FuriHalBt] Stop BLE related RTOS threads -241054 [I][FuriHalBt] Reset SHCI -241168 [I][FuriHalBt] Start BT initialization -241173 [I][Core2] Core2 started -241175 [I][Core2] C2 boot completed, mode: Stack -241178 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -241180 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -241184 [I][Core2] Radio stack started -241186 [I][Core2] Flash activity control switched to SEM7 -241188 [I][BtGap] Advertising name: Mmm_ -241190 [I][BtGap] MAC @ : 35:F2:47:26:81:88 -241212 [D][BtBatterySvc] Updating power state characteristic -241222 [I][BtGap] Start advertising -242157 [D][ViewDispatcher] View changed while key press 20010600 -> 20010708. Sending key: Back, type: Release, sequence: 0000002C to previous view port -242161 [I][SavedStruct] Loading "/int/.desktop.settings" -242190 [I][SavedStruct] Loading "/int/.desktop.settings" -242390 [I][SavedStruct] Loading "/int/.desktop.settings" -242590 [I][SavedStruct] Loading "/int/.desktop.settings" -242662 [I][SavedStruct] Loading "/int/.desktop.settings" -242790 [I][SavedStruct] Loading "/int/.desktop.settings" -242836 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -242839 [I][BtGap] Rssi: 305 -242929 [D][BtGap] Slave security initiated -242990 [I][SavedStruct] Loading "/int/.desktop.settings" -243018 [I][BtGap] Rx MTU size: 414 -243162 [I][SavedStruct] Loading "/int/.desktop.settings" -243190 [I][SavedStruct] Loading "/int/.desktop.settings" -243390 [I][SavedStruct] Loading "/int/.desktop.settings" -243524 [D][BtGap] Bond lost event. Start rebonding -243590 [I][SavedStruct] Loading "/int/.desktop.settings" -243662 [I][SavedStruct] Loading "/int/.desktop.settings" -243702 [I][BtGap] Verify numeric comparison: 382889 -243829 [I][SavedStruct] Loading "/int/.desktop.settings" -245253 [I][SavedStruct] Loading "/int/.desktop.settings" -245274 [I][SavedStruct] Loading "/int/.desktop.settings" -245390 [I][SavedStruct] Loading "/int/.desktop.settings" -245590 [I][SavedStruct] Loading "/int/.desktop.settings" -245662 [I][SavedStruct] Loading "/int/.desktop.settings" -245679 [I][BtGap] Pairing complete -245683 [D][BtBatterySvc] Updating battery level characteristic -245687 [I][BadBleWorker] BLE Key timeout : 16 -245790 [I][SavedStruct] Loading "/int/.desktop.settings" -245990 [I][SavedStruct] Loading "/int/.desktop.settings" -246162 [I][SavedStruct] Loading "/int/.desktop.settings" -246190 [I][SavedStruct] Loading "/int/.desktop.settings" -246390 [I][SavedStruct] Loading "/int/.desktop.settings" -246590 [I][SavedStruct] Loading "/int/.desktop.settings" -246660 [I][BtGap] Connection parameters event complete -246663 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -246667 [W][BtGap] Unsupported connection interval. Request connection parameters update -246670 [I][SavedStruct] Loading "/int/.desktop.settings" -246673 [I][BtGap] Rssi: 304 -246699 [D][BtGap] Connection parameters accepted -246790 [I][SavedStruct] Loading "/int/.desktop.settings" -246862 [I][BtGap] Connection parameters event complete -246866 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -246871 [I][BtGap] Rssi: 303 -246990 [I][SavedStruct] Loading "/int/.desktop.settings" -247162 [I][SavedStruct] Loading "/int/.desktop.settings" -247190 [I][SavedStruct] Loading "/int/.desktop.settings" -247390 [I][SavedStruct] Loading "/int/.desktop.settings" -247590 [I][SavedStruct] Loading "/int/.desktop.settings" -247662 [I][SavedStruct] Loading "/int/.desktop.settings" -247790 [I][SavedStruct] Loading "/int/.desktop.settings" -247990 [I][SavedStruct] Loading "/int/.desktop.settings" -248162 [I][SavedStruct] Loading "/int/.desktop.settings" -248190 [I][SavedStruct] Loading "/int/.desktop.settings" -248390 [I][SavedStruct] Loading "/int/.desktop.settings" -248590 [I][SavedStruct] Loading "/int/.desktop.settings" -248747 [D][DolphinState] icounter 1325, butthurt 0 -248750 [I][BadBleWorker] BLE Key timeout : 16 -248754 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -248757 [I][BadBleWorker] BLE Key timeout : 16 -248759 [D][BadBleWorker] line:DELAY 1000 -248763 [I][BadBleWorker] BLE Key timeout : 16 -248790 [I][SavedStruct] Loading "/int/.desktop.settings" -248990 [I][SavedStruct] Loading "/int/.desktop.settings" -249190 [I][SavedStruct] Loading "/int/.desktop.settings" -249248 [I][SavedStruct] Loading "/int/.desktop.settings" -249390 [I][SavedStruct] Loading "/int/.desktop.settings" -249590 [I][SavedStruct] Loading "/int/.desktop.settings" -249748 [I][SavedStruct] Loading "/int/.desktop.settings" -249766 [D][BadBleWorker] line:GUI SPACE -249768 [I][BadBleWorker] Special key pressed 82c - -249773 [I][BadBleWorker] BT RSSI: 0.000000 -249790 [I][SavedStruct] Loading "/int/.desktop.settings" -249794 [I][BadBleWorker] BLE Key timeout : 16 -249800 [D][BadBleWorker] line:DELAY 500 -249804 [I][BadBleWorker] BLE Key timeout : 16 -249990 [I][SavedStruct] Loading "/int/.desktop.settings" -250190 [I][SavedStruct] Loading "/int/.desktop.settings" -250248 [I][SavedStruct] Loading "/int/.desktop.settings" -250308 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -250312 [I][BadBleWorker] BT RSSI: 0.000000 -250332 [I][BadBleWorker] BT RSSI: 0.000000 -250351 [I][BadBleWorker] BT RSSI: 0.000000 -250371 [I][BadBleWorker] BT RSSI: 0.000000 -250390 [I][SavedStruct] Loading "/int/.desktop.settings" -250392 [I][BadBleWorker] BT RSSI: 0.000000 -250414 [I][BadBleWorker] BT RSSI: 0.000000 -250434 [I][BadBleWorker] BT RSSI: 0.000000 -250454 [I][BadBleWorker] BT RSSI: 0.000000 -250474 [I][BadBleWorker] BT RSSI: 0.000000 -250494 [I][BadBleWorker] BT RSSI: 0.000000 -250514 [I][BadBleWorker] BT RSSI: 0.000000 -250534 [I][BadBleWorker] BT RSSI: 0.000000 -250554 [I][BadBleWorker] BT RSSI: 0.000000 -250574 [I][BadBleWorker] BT RSSI: 0.000000 -250590 [I][SavedStruct] Loading "/int/.desktop.settings" -250596 [I][BadBleWorker] BT RSSI: 0.000000 -250618 [I][BadBleWorker] BT RSSI: 0.000000 -250638 [I][BadBleWorker] BT RSSI: 0.000000 -250658 [I][BadBleWorker] BT RSSI: 0.000000 -250678 [I][BadBleWorker] BT RSSI: 0.000000 -250698 [I][BadBleWorker] BT RSSI: 0.000000 -250718 [I][BadBleWorker] BT RSSI: 0.000000 -250737 [I][BadBleWorker] BT RSSI: 0.000000 -250748 [I][SavedStruct] Loading "/int/.desktop.settings" -250760 [I][BadBleWorker] BT RSSI: 0.000000 -250781 [I][BadBleWorker] BT RSSI: 0.000000 -250790 [I][SavedStruct] Loading "/int/.desktop.settings" -250803 [I][BadBleWorker] BT RSSI: 0.000000 -250824 [I][BadBleWorker] BT RSSI: 0.000000 -250844 [I][BadBleWorker] BT RSSI: 0.000000 -250864 [I][BadBleWorker] BT RSSI: 0.000000 -250884 [I][BadBleWorker] BT RSSI: 0.000000 -250904 [I][BadBleWorker] BT RSSI: 0.000000 -250926 [I][BadBleWorker] BT RSSI: 0.000000 -250947 [I][BadBleWorker] BT RSSI: 0.000000 -250967 [I][BadBleWorker] BT RSSI: 0.000000 -250987 [I][BadBleWorker] BT RSSI: 0.000000 -250990 [I][SavedStruct] Loading "/int/.desktop.settings" -251008 [I][BadBleWorker] BT RSSI: 0.000000 -251028 [I][BadBleWorker] BT RSSI: 0.000000 -251048 [I][BadBleWorker] BT RSSI: 0.000000 -251068 [I][BadBleWorker] BT RSSI: 0.000000 -251088 [I][BadBleWorker] BT RSSI: 0.000000 -251108 [I][BadBleWorker] BT RSSI: 0.000000 -251128 [I][BadBleWorker] BT RSSI: 0.000000 -251147 [I][BadBleWorker] BLE Key timeout : 16 -251150 [D][BadBleWorker] line:DELAY 200 -251152 [I][BadBleWorker] BLE Key timeout : 16 -251190 [I][SavedStruct] Loading "/int/.desktop.settings" -251248 [I][SavedStruct] Loading "/int/.desktop.settings" -251355 [D][BadBleWorker] line:ENTER -251357 [I][BadBleWorker] Special key pressed 28 - -251360 [I][BadBleWorker] BT RSSI: 0.000000 -251379 [I][BadBleWorker] BLE Key timeout : 16 -251382 [D][BadBleWorker] line:GUI SPACE -251384 [I][BadBleWorker] Special key pressed 82c - -251387 [I][BadBleWorker] BT RSSI: 0.000000 -251391 [I][SavedStruct] Loading "/int/.desktop.settings" -251408 [I][BadBleWorker] BLE Key timeout : 16 -251411 [D][BadBleWorker] line:DELAY 500 -251413 [I][BadBleWorker] BLE Key timeout : 16 -251590 [I][SavedStruct] Loading "/int/.desktop.settings" -251748 [I][SavedStruct] Loading "/int/.desktop.settings" -251790 [I][SavedStruct] Loading "/int/.desktop.settings" -251915 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -251919 [I][BadBleWorker] BT RSSI: 0.000000 -251941 [I][BadBleWorker] BT RSSI: 0.000000 -251962 [I][BadBleWorker] BT RSSI: 0.000000 -251982 [I][BadBleWorker] BT RSSI: 0.000000 -251990 [I][SavedStruct] Loading "/int/.desktop.settings" -252004 [I][BadBleWorker] BT RSSI: 0.000000 -252026 [I][BadBleWorker] BT RSSI: 0.000000 -252046 [I][BadBleWorker] BT RSSI: 0.000000 -252066 [I][BadBleWorker] BT RSSI: 0.000000 -252086 [I][BadBleWorker] BT RSSI: 0.000000 -252106 [I][BadBleWorker] BT RSSI: 0.000000 -252126 [I][BadBleWorker] BT RSSI: 0.000000 -252146 [I][BadBleWorker] BT RSSI: 0.000000 -252166 [I][BadBleWorker] BT RSSI: 0.000000 -252186 [I][BadBleWorker] BT RSSI: 0.000000 -252190 [I][SavedStruct] Loading "/int/.desktop.settings" -252207 [I][BadBleWorker] BT RSSI: 0.000000 -252227 [I][BadBleWorker] BT RSSI: 0.000000 -252248 [I][BadBleWorker] BT RSSI: 0.000000 -252251 [I][SavedStruct] Loading "/int/.desktop.settings" -252269 [I][BadBleWorker] BT RSSI: 0.000000 -252289 [I][BadBleWorker] BT RSSI: 0.000000 -252309 [I][BadBleWorker] BT RSSI: 0.000000 -252329 [I][BadBleWorker] BT RSSI: 0.000000 -252348 [I][BadBleWorker] BT RSSI: 0.000000 -252368 [I][BadBleWorker] BT RSSI: 0.000000 -252388 [I][BadBleWorker] BT RSSI: 0.000000 -252391 [I][SavedStruct] Loading "/int/.desktop.settings" -252409 [I][BadBleWorker] BT RSSI: 0.000000 -252429 [I][BadBleWorker] BT RSSI: 0.000000 -252449 [I][BadBleWorker] BT RSSI: 0.000000 -252469 [I][BadBleWorker] BT RSSI: 0.000000 -252489 [I][BadBleWorker] BT RSSI: 0.000000 -252509 [I][BadBleWorker] BT RSSI: 0.000000 -252529 [I][BadBleWorker] BT RSSI: 0.000000 -252549 [I][BadBleWorker] BT RSSI: 0.000000 -252569 [I][BadBleWorker] BT RSSI: 0.000000 -252590 [I][SavedStruct] Loading "/int/.desktop.settings" -252592 [I][BadBleWorker] BT RSSI: 0.000000 -252614 [I][BadBleWorker] BT RSSI: 0.000000 -252634 [I][BadBleWorker] BT RSSI: 0.000000 -252654 [I][BadBleWorker] BT RSSI: 0.000000 -252674 [I][BadBleWorker] BT RSSI: 0.000000 -252694 [I][BadBleWorker] BT RSSI: 0.000000 -252714 [I][BadBleWorker] BT RSSI: 0.000000 -252733 [I][BadBleWorker] BT RSSI: 0.000000 -252748 [I][SavedStruct] Loading "/int/.desktop.settings" -252754 [I][BadBleWorker] BLE Key timeout : 16 -252758 [D][BadBleWorker] line:DELAY 200 -252763 [I][BadBleWorker] BLE Key timeout : 16 -252790 [I][SavedStruct] Loading "/int/.desktop.settings" -252967 [D][BadBleWorker] line:ENTER -252969 [I][BadBleWorker] Special key pressed 28 - -252972 [I][BadBleWorker] BT RSSI: 0.000000 -252990 [I][SavedStruct] Loading "/int/.desktop.settings" -252993 [I][BadBleWorker] BLE Key timeout : 16 -253000 [D][BadBleWorker] line:GUI SPACE -253002 [I][BadBleWorker] Special key pressed 82c - -253007 [I][BadBleWorker] BT RSSI: 0.000000 -253028 [I][BadBleWorker] BLE Key timeout : 16 -253031 [D][BadBleWorker] line:DELAY 500 -253033 [I][BadBleWorker] BLE Key timeout : 16 -253190 [I][SavedStruct] Loading "/int/.desktop.settings" -253248 [I][SavedStruct] Loading "/int/.desktop.settings" -253390 [I][SavedStruct] Loading "/int/.desktop.settings" -253536 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -253540 [I][BadBleWorker] BT RSSI: 0.000000 -253560 [I][BadBleWorker] BT RSSI: 0.000000 -253580 [I][BadBleWorker] BT RSSI: 0.000000 -253590 [I][SavedStruct] Loading "/int/.desktop.settings" -253602 [I][BadBleWorker] BT RSSI: 0.000000 -253624 [I][BadBleWorker] BT RSSI: 0.000000 -253644 [I][BadBleWorker] BT RSSI: 0.000000 -253664 [I][BadBleWorker] BT RSSI: 0.000000 -253684 [I][BadBleWorker] BT RSSI: 0.000000 -253704 [I][BadBleWorker] BT RSSI: 0.000000 -253724 [I][BadBleWorker] BT RSSI: 0.000000 -253743 [I][BadBleWorker] BT RSSI: 0.000000 -253748 [I][SavedStruct] Loading "/int/.desktop.settings" -253765 [I][BadBleWorker] BT RSSI: 0.000000 -253785 [I][BadBleWorker] BT RSSI: 0.000000 -253790 [I][SavedStruct] Loading "/int/.desktop.settings" -253807 [I][BadBleWorker] BT RSSI: 0.000000 -253827 [I][BadBleWorker] BT RSSI: 0.000000 -253847 [I][BadBleWorker] BT RSSI: 0.000000 -253867 [I][BadBleWorker] BT RSSI: 0.000000 -253887 [I][BadBleWorker] BT RSSI: 0.000000 -253907 [I][BadBleWorker] BT RSSI: 0.000000 -253927 [I][BadBleWorker] BT RSSI: 0.000000 -253947 [I][BadBleWorker] BT RSSI: 0.000000 -253969 [I][BadBleWorker] BT RSSI: 0.000000 -253990 [I][SavedStruct] Loading "/int/.desktop.settings" -253993 [I][BadBleWorker] BT RSSI: 0.000000 -254015 [I][BadBleWorker] BT RSSI: 0.000000 -254035 [I][BadBleWorker] BT RSSI: 0.000000 -254055 [I][BadBleWorker] BT RSSI: 0.000000 -254075 [I][BadBleWorker] BT RSSI: 0.000000 -254095 [I][BadBleWorker] BT RSSI: 0.000000 -254115 [I][BadBleWorker] BT RSSI: 0.000000 -254135 [I][BadBleWorker] BT RSSI: 0.000000 -254155 [I][BadBleWorker] BT RSSI: 0.000000 -254174 [I][BadBleWorker] BT RSSI: 0.000000 -254190 [I][SavedStruct] Loading "/int/.desktop.settings" -254196 [I][BadBleWorker] BT RSSI: 0.000000 -254217 [I][BadBleWorker] BT RSSI: 0.000000 -254237 [I][BadBleWorker] BT RSSI: 0.000000 -254248 [I][SavedStruct] Loading "/int/.desktop.settings" -254259 [I][BadBleWorker] BT RSSI: 0.000000 -254281 [I][BadBleWorker] BT RSSI: 0.000000 -254300 [I][BadBleWorker] BT RSSI: 0.000000 -254320 [I][BadBleWorker] BT RSSI: 0.000000 -254340 [I][BadBleWorker] BT RSSI: 0.000000 -254360 [I][BadBleWorker] BT RSSI: 0.000000 -254379 [I][BadBleWorker] BLE Key timeout : 16 -254383 [D][BadBleWorker] line:DELAY 200 -254386 [I][BadBleWorker] BLE Key timeout : 16 -254390 [I][SavedStruct] Loading "/int/.desktop.settings" -254588 [D][BadBleWorker] line:ENTER -254590 [I][BadBleWorker] Special key pressed 28 - -254593 [I][SavedStruct] Loading "/int/.desktop.settings" -254596 [I][BadBleWorker] BT RSSI: 0.000000 -254617 [I][BadBleWorker] BLE Key timeout : 16 -254621 [D][BadBleWorker] line:GUI SPACE -254623 [I][BadBleWorker] Special key pressed 82c - -254627 [I][BadBleWorker] BT RSSI: 0.000000 -254646 [I][BadBleWorker] BLE Key timeout : 16 -254650 [D][BadBleWorker] line:DELAY 500 -254653 [I][BadBleWorker] BLE Key timeout : 16 -254748 [I][SavedStruct] Loading "/int/.desktop.settings" -254790 [I][SavedStruct] Loading "/int/.desktop.settings" -254990 [I][SavedStruct] Loading "/int/.desktop.settings" -255155 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -255159 [I][BadBleWorker] BT RSSI: 0.000000 -255179 [I][BadBleWorker] BT RSSI: 0.000000 -255190 [I][SavedStruct] Loading "/int/.desktop.settings" -255201 [I][BadBleWorker] BT RSSI: 0.000000 -255223 [I][BadBleWorker] BT RSSI: 0.000000 -255243 [I][BadBleWorker] BT RSSI: 0.000000 -255248 [I][SavedStruct] Loading "/int/.desktop.settings" -255265 [I][BadBleWorker] BT RSSI: 0.000000 -255286 [I][BadBleWorker] BT RSSI: 0.000000 -255306 [I][BadBleWorker] BT RSSI: 0.000000 -255326 [I][BadBleWorker] BT RSSI: 0.000000 -255346 [I][BadBleWorker] BT RSSI: 0.000000 -255366 [I][BadBleWorker] BT RSSI: 0.000000 -255386 [I][BadBleWorker] BT RSSI: 0.000000 -255390 [I][SavedStruct] Loading "/int/.desktop.settings" -255407 [I][BadBleWorker] BT RSSI: 0.000000 -255427 [I][BadBleWorker] BT RSSI: 0.000000 -255446 [I][BadBleWorker] BT RSSI: 0.000000 -255466 [I][BadBleWorker] BT RSSI: 0.000000 -255485 [I][BadBleWorker] BT RSSI: 0.000000 -255505 [I][BadBleWorker] BT RSSI: 0.000000 -255525 [I][BadBleWorker] BT RSSI: 0.000000 -255545 [I][BadBleWorker] BT RSSI: 0.000000 -255565 [I][BadBleWorker] BT RSSI: 0.000000 -255585 [I][BadBleWorker] BT RSSI: 0.000000 -255590 [I][SavedStruct] Loading "/int/.desktop.settings" -255607 [I][BadBleWorker] BT RSSI: 0.000000 -255628 [I][BadBleWorker] BT RSSI: 0.000000 -255648 [I][BadBleWorker] BT RSSI: 0.000000 -255668 [I][BadBleWorker] BT RSSI: 0.000000 -255688 [I][BadBleWorker] BT RSSI: 0.000000 -255708 [I][BadBleWorker] BT RSSI: 0.000000 -255728 [I][BadBleWorker] BT RSSI: 0.000000 -255748 [I][SavedStruct] Loading "/int/.desktop.settings" -255750 [I][BadBleWorker] BT RSSI: 0.000000 -255772 [I][BadBleWorker] BT RSSI: 0.000000 -255790 [I][SavedStruct] Loading "/int/.desktop.settings" -255794 [I][BadBleWorker] BT RSSI: 0.000000 -255816 [I][BadBleWorker] BT RSSI: 0.000000 -255836 [I][BadBleWorker] BT RSSI: 0.000000 -255856 [I][BadBleWorker] BT RSSI: 0.000000 -255876 [I][BadBleWorker] BT RSSI: 0.000000 -255896 [I][BadBleWorker] BT RSSI: 0.000000 -255915 [I][BadBleWorker] BT RSSI: 0.000000 -255935 [I][BadBleWorker] BT RSSI: 0.000000 -255955 [I][BadBleWorker] BT RSSI: 0.000000 -255975 [I][BadBleWorker] BT RSSI: 0.000000 -255996 [I][BadBleWorker] BLE Key timeout : 16 -256000 [D][BadBleWorker] line:DELAY 200 -256003 [I][BadBleWorker] BLE Key timeout : 16 -256006 [I][SavedStruct] Loading "/int/.desktop.settings" -256190 [I][SavedStruct] Loading "/int/.desktop.settings" -256207 [D][BadBleWorker] line:ENTER -256209 [I][BadBleWorker] Special key pressed 28 - -256213 [I][BadBleWorker] BT RSSI: 0.000000 -256232 [I][BadBleWorker] BLE Key timeout : 16 -256236 [I][BadBleWorker] BLE Key timeout : 16 -256248 [I][SavedStruct] Loading "/int/.desktop.settings" -256390 [I][SavedStruct] Loading "/int/.desktop.settings" -256590 [I][SavedStruct] Loading "/int/.desktop.settings" -256748 [I][SavedStruct] Loading "/int/.desktop.settings" -256790 [I][SavedStruct] Loading "/int/.desktop.settings" -256990 [I][SavedStruct] Loading "/int/.desktop.settings" -257190 [I][SavedStruct] Loading "/int/.desktop.settings" -257248 [I][SavedStruct] Loading "/int/.desktop.settings" -257390 [I][SavedStruct] Loading "/int/.desktop.settings" -257436 [I][BtGap] Stop advertising -257439 [D][BtGap] terminate success -257442 [E][BtGap] set_non_discoverable failed 12 -257446 [I][SavedStruct] Loading "/int/.desktop.settings" -257590 [I][SavedStruct] Loading "/int/.desktop.settings" -257646 [I][SavedStruct] Loading "/int/.bt.settings" -257659 [I][SavedStruct] Loading "/int/.bt.keys" -257669 [I][FuriHalBt] Disconnect and stop advertising -257671 [I][FuriHalBt] Stop current profile services -257683 [I][FuriHalBt] Stop BLE related RTOS threads -257707 [I][FuriHalBt] Reset SHCI -257790 [I][SavedStruct] Loading "/int/.desktop.settings" -257821 [I][FuriHalBt] Start BT initialization -257826 [I][Core2] Core2 started -257828 [I][Core2] C2 boot completed, mode: Stack -257831 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -257833 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -257837 [I][Core2] Radio stack started -257839 [I][Core2] Flash activity control switched to SEM7 -257841 [I][BtGap] Advertising name: Keyboard -257843 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -257860 [D][BtBatterySvc] Updating power state characteristic -257866 [I][BtSrv] Bt App started -257868 [I][BtGap] Start advertising -257871 [I][BadBleWorker] End -257873 [I][SavedStruct] Loading "/int/.desktop.settings" -257893 [D][BrowserWorker] Start -257909 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -257911 [D][BrowserWorker] Load offset: 0 cnt: 50 -257983 [D][GuiSrv] ViewPort changed while key press 2000D718 -> 20010BD0. Discarding key: Back, type: Short, sequence: 00000031 -257987 [D][GuiSrv] ViewPort changed while key press 2000D718 -> 20010BD0. Sending key: Back, type: Release, sequence: 00000031 to previous view port -258434 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 -258438 [D][BrowserWorker] Load offset: 0 cnt: 50 -278749 [I][Dolphin] Flush stats -278751 [I][SavedStruct] Saving "/int/.dolphin.state" -278762 [D][StorageInt] Device erase: page 2, translated page: cf -278771 [D][StorageInt] Device sync: skipping -278774 [I][DolphinState] State saved -317982 [D][BtGap] set_non_discoverable success -377984 [D][BtGap] set_non_discoverable success - - _.-------.._ -, - .-"```"--..,,_/ /`-, -, \ - .:" /:/ /'\ \ ,_..., `. | | - / ,----/:/ /`\ _\~`_-"` _; - ' / /`"""'\ \ \.~`_-' ,-"'/ - | | | 0 | | .-' ,/` / - | ,..\ \ ,.-"` ,/` / - ; : `/`""\` ,/--==,/-----, - | `-...| -.___-Z:_______J...---; - : ` _-' - _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ -| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| -| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | -|_| |____||___||_| |_| |___||_|_\ \___||____||___| - -Welcome to Flipper Zero Command Line Interface! -Read Manual https://docs.flipperzero.one - -Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) - ->: log -Press CTRL+C to stop... -48839 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 -48842 [D][BrowserWorker] Load offset: 0 cnt: 50 -51685 [D][BrowserWorker] End -51698 [I][BtGap] Stop advertising -51702 [D][BtGap] set_non_discoverable success -51716 [I][SavedStruct] Loading "/int/.desktop.settings" -51843 [I][SavedStruct] Loading "/int/.desktop.settings" -51908 [I][FuriHalBt] Disconnect and stop advertising -51910 [I][FuriHalBt] Stop current profile services -51919 [I][FuriHalBt] Stop BLE related RTOS threads -51943 [I][FuriHalBt] Reset SHCI -52043 [I][SavedStruct] Loading "/int/.desktop.settings" -52057 [I][FuriHalBt] Start BT initialization -52063 [I][Core2] Core2 started -52065 [I][Core2] C2 boot completed, mode: Stack -52068 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -52070 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -52074 [I][Core2] Radio stack started -52077 [I][Core2] Flash activity control switched to SEM7 -52079 [I][BtGap] Advertising name: Keyboard -52082 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -52098 [D][BtBatterySvc] Updating power state characteristic -52104 [I][BtGap] Start advertising -52106 [I][SavedStruct] Loading "/int/.bt.settings" -52124 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -52134 [I][FuriHalBt] Disconnect and stop advertising -52136 [I][BtGap] Stop advertising -52139 [D][BtGap] set_non_discoverable success -52142 [I][FuriHalBt] Stop current profile services -52152 [I][FuriHalBt] Stop BLE related RTOS threads -52177 [I][FuriHalBt] Reset SHCI -52212 [I][SavedStruct] Loading "/int/.desktop.settings" -52243 [I][SavedStruct] Loading "/int/.desktop.settings" -52291 [I][FuriHalBt] Start BT initialization -52296 [I][Core2] Core2 started -52298 [I][Core2] C2 boot completed, mode: Stack -52301 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -52303 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -52308 [I][Core2] Radio stack started -52310 [I][Core2] Flash activity control switched to SEM7 -52314 [I][BtGap] Advertising name: Rumik1 -52316 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -52333 [D][BtBatterySvc] Updating power state characteristic -52342 [I][BtSrv] Bt App started -52343 [I][BtGap] Start advertising -52347 [I][BadBleWorker] Init -52351 [I][SavedStruct] Loading "/int/.desktop.settings" -52378 [I][BadBleWorker] BLE Key timeout : 16 -52443 [I][SavedStruct] Loading "/int/.desktop.settings" -52643 [I][SavedStruct] Loading "/int/.desktop.settings" -52712 [I][SavedStruct] Loading "/int/.desktop.settings" -52843 [I][SavedStruct] Loading "/int/.desktop.settings" -53043 [I][SavedStruct] Loading "/int/.desktop.settings" -53243 [I][SavedStruct] Loading "/int/.desktop.settings" -53861 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -53865 [I][BtGap] Rssi: 302 -53954 [D][BtGap] Slave security initiated -54073 [I][BtGap] Pairing complete -54076 [D][BtBatterySvc] Updating battery level characteristic -54079 [I][BadBleWorker] BLE Key timeout : 16 -54252 [I][BtGap] Rx MTU size: 414 -54822 [I][BtGap] Connection parameters event complete -54824 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -54826 [W][BtGap] Unsupported connection interval. Request connection parameters update -54829 [I][BtGap] Rssi: 290 -54859 [D][BtGap] Connection parameters accepted -55022 [I][BtGap] Connection parameters event complete -55025 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -55027 [I][BtGap] Rssi: 293 -59176 [I][FuriHalBt] Disconnect and stop advertising -59180 [I][BtGap] Stop advertising -59184 [D][BtGap] terminate success -59188 [E][BtGap] set_non_discoverable failed 12 -59193 [I][FuriHalBt] Stop current profile services -59198 [I][BadBleWorker] BLE Key timeout : 16 -59216 [I][FuriHalBt] Stop BLE related RTOS threads -59240 [I][FuriHalBt] Reset SHCI -59354 [I][FuriHalBt] Start BT initialization -59360 [I][Core2] Core2 started -59363 [I][Core2] C2 boot completed, mode: Stack -59366 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -59368 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -59372 [I][Core2] Radio stack started -59375 [I][Core2] Flash activity control switched to SEM7 -59377 [I][BtGap] Advertising name: Kkk -59380 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -59397 [D][BtBatterySvc] Updating power state characteristic -59407 [I][BtGap] Start advertising -60280 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -60282 [I][BtGap] Rssi: 302 -60374 [D][BtGap] Slave security initiated -60495 [I][BtGap] Pairing complete -60498 [D][BtBatterySvc] Updating battery level characteristic -60500 [I][BadBleWorker] BLE Key timeout : 16 -60673 [I][BtGap] Rx MTU size: 414 -61244 [I][BtGap] Connection parameters event complete -61248 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -61252 [W][BtGap] Unsupported connection interval. Request connection parameters update -61258 [I][BtGap] Rssi: 297 -61284 [D][BtGap] Connection parameters accepted -61448 [I][BtGap] Connection parameters event complete -61452 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -61456 [I][BtGap] Rssi: 298 -66066 [I][BtGap] Disconnect from client. Reason: 13 -66069 [I][BadBleWorker] BLE Key timeout : 16 -69485 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -69489 [I][BtGap] Rssi: 297 -69600 [D][BtGap] Slave security initiated -69720 [I][BtGap] Pairing complete -69723 [D][BtBatterySvc] Updating battery level characteristic -69727 [I][BadBleWorker] BLE Key timeout : 16 -69900 [I][BtGap] Rx MTU size: 414 -71400 [I][BtGap] Connection parameters event complete -71402 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -71405 [W][BtGap] Unsupported connection interval. Request connection parameters update -71408 [I][BtGap] Rssi: 296 -71438 [D][BtGap] Connection parameters accepted -71603 [I][BtGap] Connection parameters event complete -71605 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -71609 [I][BtGap] Rssi: 299 -73790 [I][BtGap] Disconnect from client. Reason: 13 -73792 [I][BadBleWorker] BLE Key timeout : 16 -80587 [I][FuriHalBt] Disconnect and stop advertising -80591 [I][BtGap] Stop advertising -80595 [D][BtGap] set_non_discoverable success -80600 [I][FuriHalBt] Stop current profile services -80621 [I][FuriHalBt] Stop BLE related RTOS threads -80647 [I][FuriHalBt] Reset SHCI -80762 [I][FuriHalBt] Start BT initialization -80767 [I][Core2] Core2 started -80769 [I][Core2] C2 boot completed, mode: Stack -80772 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -80774 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -80778 [I][Core2] Radio stack started -80781 [I][Core2] Flash activity control switched to SEM7 -80783 [I][BtGap] Advertising name: Kkk -80786 [I][BtGap] MAC @ : 35:F2:47:26:11:11 -80803 [D][BtBatterySvc] Updating power state characteristic -80813 [I][BtGap] Start advertising -81988 [D][ViewDispatcher] View changed while key press 200101B0 -> 200102B8. Sending key: Back, type: Release, sequence: 00000028 to previous view port -81992 [I][SavedStruct] Loading "/int/.desktop.settings" -82043 [I][SavedStruct] Loading "/int/.desktop.settings" -82243 [I][SavedStruct] Loading "/int/.desktop.settings" -82443 [I][SavedStruct] Loading "/int/.desktop.settings" -82493 [I][SavedStruct] Loading "/int/.desktop.settings" -82643 [I][SavedStruct] Loading "/int/.desktop.settings" -82843 [I][SavedStruct] Loading "/int/.desktop.settings" -82993 [I][SavedStruct] Loading "/int/.desktop.settings" -83043 [I][SavedStruct] Loading "/int/.desktop.settings" -83243 [I][SavedStruct] Loading "/int/.desktop.settings" -83443 [I][SavedStruct] Loading "/int/.desktop.settings" -83493 [I][SavedStruct] Loading "/int/.desktop.settings" -83643 [I][SavedStruct] Loading "/int/.desktop.settings" -83843 [I][SavedStruct] Loading "/int/.desktop.settings" -83993 [I][SavedStruct] Loading "/int/.desktop.settings" -84043 [I][SavedStruct] Loading "/int/.desktop.settings" -84243 [I][SavedStruct] Loading "/int/.desktop.settings" -84443 [I][SavedStruct] Loading "/int/.desktop.settings" -84493 [I][SavedStruct] Loading "/int/.desktop.settings" -84643 [I][SavedStruct] Loading "/int/.desktop.settings" -84843 [I][SavedStruct] Loading "/int/.desktop.settings" -84993 [I][SavedStruct] Loading "/int/.desktop.settings" -85043 [I][SavedStruct] Loading "/int/.desktop.settings" -85243 [I][SavedStruct] Loading "/int/.desktop.settings" -85443 [I][SavedStruct] Loading "/int/.desktop.settings" -85493 [I][SavedStruct] Loading "/int/.desktop.settings" -85643 [I][SavedStruct] Loading "/int/.desktop.settings" -85843 [I][SavedStruct] Loading "/int/.desktop.settings" -85993 [I][SavedStruct] Loading "/int/.desktop.settings" -86043 [I][SavedStruct] Loading "/int/.desktop.settings" -86243 [I][SavedStruct] Loading "/int/.desktop.settings" -86443 [I][SavedStruct] Loading "/int/.desktop.settings" -86493 [I][SavedStruct] Loading "/int/.desktop.settings" -86643 [I][SavedStruct] Loading "/int/.desktop.settings" -86843 [I][SavedStruct] Loading "/int/.desktop.settings" -86993 [I][SavedStruct] Loading "/int/.desktop.settings" -87043 [I][SavedStruct] Loading "/int/.desktop.settings" -87243 [I][SavedStruct] Loading "/int/.desktop.settings" -87443 [I][SavedStruct] Loading "/int/.desktop.settings" -87493 [I][SavedStruct] Loading "/int/.desktop.settings" -87643 [I][SavedStruct] Loading "/int/.desktop.settings" -87843 [I][SavedStruct] Loading "/int/.desktop.settings" -87993 [I][SavedStruct] Loading "/int/.desktop.settings" -88043 [I][SavedStruct] Loading "/int/.desktop.settings" -88243 [I][SavedStruct] Loading "/int/.desktop.settings" -88443 [I][SavedStruct] Loading "/int/.desktop.settings" -88493 [I][SavedStruct] Loading "/int/.desktop.settings" -88643 [I][SavedStruct] Loading "/int/.desktop.settings" -88843 [I][SavedStruct] Loading "/int/.desktop.settings" -88993 [I][SavedStruct] Loading "/int/.desktop.settings" -89043 [I][SavedStruct] Loading "/int/.desktop.settings" -89243 [I][SavedStruct] Loading "/int/.desktop.settings" -89443 [I][SavedStruct] Loading "/int/.desktop.settings" -89493 [I][SavedStruct] Loading "/int/.desktop.settings" -89643 [I][SavedStruct] Loading "/int/.desktop.settings" -89843 [I][SavedStruct] Loading "/int/.desktop.settings" -89993 [I][SavedStruct] Loading "/int/.desktop.settings" -90043 [I][SavedStruct] Loading "/int/.desktop.settings" -90243 [I][SavedStruct] Loading "/int/.desktop.settings" -90443 [I][SavedStruct] Loading "/int/.desktop.settings" -90493 [I][SavedStruct] Loading "/int/.desktop.settings" -90643 [I][SavedStruct] Loading "/int/.desktop.settings" -90843 [I][SavedStruct] Loading "/int/.desktop.settings" -90993 [I][SavedStruct] Loading "/int/.desktop.settings" -91043 [I][SavedStruct] Loading "/int/.desktop.settings" -91243 [I][SavedStruct] Loading "/int/.desktop.settings" -91443 [I][SavedStruct] Loading "/int/.desktop.settings" -91493 [I][SavedStruct] Loading "/int/.desktop.settings" -91643 [I][SavedStruct] Loading "/int/.desktop.settings" -91843 [I][SavedStruct] Loading "/int/.desktop.settings" -91993 [I][SavedStruct] Loading "/int/.desktop.settings" -92043 [I][SavedStruct] Loading "/int/.desktop.settings" -92243 [I][SavedStruct] Loading "/int/.desktop.settings" -92443 [I][SavedStruct] Loading "/int/.desktop.settings" -92493 [I][SavedStruct] Loading "/int/.desktop.settings" -92643 [I][SavedStruct] Loading "/int/.desktop.settings" -92843 [I][SavedStruct] Loading "/int/.desktop.settings" -92993 [I][SavedStruct] Loading "/int/.desktop.settings" -93043 [I][SavedStruct] Loading "/int/.desktop.settings" -93243 [I][SavedStruct] Loading "/int/.desktop.settings" -93443 [I][SavedStruct] Loading "/int/.desktop.settings" -93493 [I][SavedStruct] Loading "/int/.desktop.settings" -93643 [I][SavedStruct] Loading "/int/.desktop.settings" -93843 [I][SavedStruct] Loading "/int/.desktop.settings" -93993 [I][SavedStruct] Loading "/int/.desktop.settings" -94043 [I][SavedStruct] Loading "/int/.desktop.settings" -94243 [I][SavedStruct] Loading "/int/.desktop.settings" -94443 [I][SavedStruct] Loading "/int/.desktop.settings" -94493 [I][SavedStruct] Loading "/int/.desktop.settings" -94643 [I][SavedStruct] Loading "/int/.desktop.settings" -94843 [I][SavedStruct] Loading "/int/.desktop.settings" -94993 [I][SavedStruct] Loading "/int/.desktop.settings" -95043 [I][SavedStruct] Loading "/int/.desktop.settings" -95243 [I][SavedStruct] Loading "/int/.desktop.settings" -95443 [I][SavedStruct] Loading "/int/.desktop.settings" -95493 [I][SavedStruct] Loading "/int/.desktop.settings" -95643 [I][SavedStruct] Loading "/int/.desktop.settings" -95843 [I][SavedStruct] Loading "/int/.desktop.settings" -95993 [I][SavedStruct] Loading "/int/.desktop.settings" -96043 [I][SavedStruct] Loading "/int/.desktop.settings" -96243 [I][SavedStruct] Loading "/int/.desktop.settings" -96443 [I][SavedStruct] Loading "/int/.desktop.settings" -96493 [I][SavedStruct] Loading "/int/.desktop.settings" -96643 [I][SavedStruct] Loading "/int/.desktop.settings" -96846 [I][SavedStruct] Loading "/int/.desktop.settings" -96913 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -96916 [I][BtGap] Rssi: 324 -96993 [I][SavedStruct] Loading "/int/.desktop.settings" -97006 [D][BtGap] Slave security initiated -97043 [I][SavedStruct] Loading "/int/.desktop.settings" -97094 [I][BtGap] Rx MTU size: 414 -97243 [I][SavedStruct] Loading "/int/.desktop.settings" -97443 [I][SavedStruct] Loading "/int/.desktop.settings" -97493 [I][SavedStruct] Loading "/int/.desktop.settings" -97601 [D][BtGap] Bond lost event. Start rebonding -97643 [I][SavedStruct] Loading "/int/.desktop.settings" -97780 [I][BtGap] Verify numeric comparison: 898056 -100222 [I][SavedStruct] Loading "/int/.desktop.settings" -100243 [I][SavedStruct] Loading "/int/.desktop.settings" -100264 [I][SavedStruct] Loading "/int/.desktop.settings" -100443 [I][SavedStruct] Loading "/int/.desktop.settings" -100493 [I][SavedStruct] Loading "/int/.desktop.settings" -100643 [I][SavedStruct] Loading "/int/.desktop.settings" -100657 [I][BtGap] Pairing complete -100661 [D][BtBatterySvc] Updating battery level characteristic -100666 [I][BadBleWorker] BLE Key timeout : 16 -100843 [I][SavedStruct] Loading "/int/.desktop.settings" -100993 [I][SavedStruct] Loading "/int/.desktop.settings" -101043 [I][SavedStruct] Loading "/int/.desktop.settings" -101243 [I][SavedStruct] Loading "/int/.desktop.settings" -101443 [I][SavedStruct] Loading "/int/.desktop.settings" -101493 [I][SavedStruct] Loading "/int/.desktop.settings" -101643 [I][SavedStruct] Loading "/int/.desktop.settings" -101668 [I][BtGap] Connection parameters event complete -101670 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -101673 [W][BtGap] Unsupported connection interval. Request connection parameters update -101677 [I][BtGap] Rssi: 345 -101707 [D][BtGap] Connection parameters accepted -101843 [I][SavedStruct] Loading "/int/.desktop.settings" -101870 [I][BtGap] Connection parameters event complete -101873 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -101875 [I][BtGap] Rssi: 337 -101993 [I][SavedStruct] Loading "/int/.desktop.settings" -102043 [I][SavedStruct] Loading "/int/.desktop.settings" -102243 [I][SavedStruct] Loading "/int/.desktop.settings" -102443 [I][SavedStruct] Loading "/int/.desktop.settings" -102493 [I][SavedStruct] Loading "/int/.desktop.settings" -102643 [I][SavedStruct] Loading "/int/.desktop.settings" -102843 [I][SavedStruct] Loading "/int/.desktop.settings" -102993 [I][SavedStruct] Loading "/int/.desktop.settings" -103043 [I][SavedStruct] Loading "/int/.desktop.settings" -103243 [I][SavedStruct] Loading "/int/.desktop.settings" -103443 [I][SavedStruct] Loading "/int/.desktop.settings" -103493 [I][SavedStruct] Loading "/int/.desktop.settings" -103643 [I][SavedStruct] Loading "/int/.desktop.settings" -103843 [I][SavedStruct] Loading "/int/.desktop.settings" -103993 [I][SavedStruct] Loading "/int/.desktop.settings" -104043 [I][SavedStruct] Loading "/int/.desktop.settings" -104243 [I][SavedStruct] Loading "/int/.desktop.settings" -104443 [I][SavedStruct] Loading "/int/.desktop.settings" -104493 [I][SavedStruct] Loading "/int/.desktop.settings" -104643 [I][SavedStruct] Loading "/int/.desktop.settings" -104843 [I][SavedStruct] Loading "/int/.desktop.settings" -104993 [I][SavedStruct] Loading "/int/.desktop.settings" -105043 [I][SavedStruct] Loading "/int/.desktop.settings" -105243 [I][SavedStruct] Loading "/int/.desktop.settings" -105443 [I][SavedStruct] Loading "/int/.desktop.settings" -105493 [I][SavedStruct] Loading "/int/.desktop.settings" -105643 [I][SavedStruct] Loading "/int/.desktop.settings" -105843 [I][SavedStruct] Loading "/int/.desktop.settings" -105993 [I][SavedStruct] Loading "/int/.desktop.settings" -106043 [I][SavedStruct] Loading "/int/.desktop.settings" -106243 [I][SavedStruct] Loading "/int/.desktop.settings" -106443 [I][SavedStruct] Loading "/int/.desktop.settings" -106493 [I][SavedStruct] Loading "/int/.desktop.settings" -106643 [I][SavedStruct] Loading "/int/.desktop.settings" -106843 [I][SavedStruct] Loading "/int/.desktop.settings" -106993 [I][SavedStruct] Loading "/int/.desktop.settings" -107043 [I][SavedStruct] Loading "/int/.desktop.settings" -107243 [I][SavedStruct] Loading "/int/.desktop.settings" -107443 [I][SavedStruct] Loading "/int/.desktop.settings" -107493 [I][SavedStruct] Loading "/int/.desktop.settings" -107643 [I][SavedStruct] Loading "/int/.desktop.settings" -107843 [I][SavedStruct] Loading "/int/.desktop.settings" -107995 [I][SavedStruct] Loading "/int/.desktop.settings" -108043 [I][SavedStruct] Loading "/int/.desktop.settings" -108243 [I][SavedStruct] Loading "/int/.desktop.settings" -108443 [I][SavedStruct] Loading "/int/.desktop.settings" -108493 [I][SavedStruct] Loading "/int/.desktop.settings" -108643 [I][SavedStruct] Loading "/int/.desktop.settings" -108843 [I][SavedStruct] Loading "/int/.desktop.settings" -108993 [I][SavedStruct] Loading "/int/.desktop.settings" -109043 [I][SavedStruct] Loading "/int/.desktop.settings" -109243 [I][SavedStruct] Loading "/int/.desktop.settings" -109443 [I][SavedStruct] Loading "/int/.desktop.settings" -109536 [D][DolphinState] icounter 1325, butthurt 0 -109539 [I][BadBleWorker] BLE Key timeout : 16 -109543 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -109546 [I][BadBleWorker] BLE Key timeout : 16 -109549 [D][BadBleWorker] line:DELAY 1000 -109552 [I][BadBleWorker] BLE Key timeout : 16 -109643 [I][SavedStruct] Loading "/int/.desktop.settings" -109843 [I][SavedStruct] Loading "/int/.desktop.settings" -110037 [I][SavedStruct] Loading "/int/.desktop.settings" -110055 [I][SavedStruct] Loading "/int/.desktop.settings" -110243 [I][SavedStruct] Loading "/int/.desktop.settings" -110443 [I][SavedStruct] Loading "/int/.desktop.settings" -110537 [I][SavedStruct] Loading "/int/.desktop.settings" -110555 [D][BadBleWorker] line:GUI SPACE -110557 [I][BadBleWorker] Special key pressed 82c - -110578 [I][BadBleWorker] BLE Key timeout : 16 -110582 [D][BadBleWorker] line:DELAY 500 -110585 [I][BadBleWorker] BLE Key timeout : 16 -110643 [I][SavedStruct] Loading "/int/.desktop.settings" -110843 [I][SavedStruct] Loading "/int/.desktop.settings" -111039 [I][SavedStruct] Loading "/int/.desktop.settings" -111066 [I][SavedStruct] Loading "/int/.desktop.settings" -111088 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -111243 [I][SavedStruct] Loading "/int/.desktop.settings" -111443 [I][SavedStruct] Loading "/int/.desktop.settings" -111537 [I][SavedStruct] Loading "/int/.desktop.settings" -111643 [I][SavedStruct] Loading "/int/.desktop.settings" -111792 [E][BtHid] Failed updating report characteristic: 100 -111811 [E][BtHid] Failed updating report characteristic: 100 -111814 [E][BtHid] Failed updating report characteristic: 100 -111833 [E][BtHid] Failed updating report characteristic: 100 -111836 [I][BadBleWorker] BLE Key timeout : 16 -111838 [D][BadBleWorker] line:DELAY 200 -111840 [I][BadBleWorker] BLE Key timeout : 16 -111843 [I][SavedStruct] Loading "/int/.desktop.settings" -112037 [I][SavedStruct] Loading "/int/.desktop.settings" -112044 [D][BadBleWorker] line:ENTER -112046 [I][BadBleWorker] Special key pressed 28 - -112069 [I][BadBleWorker] BLE Key timeout : 16 -112072 [D][BadBleWorker] line:GUI SPACE -112074 [I][BadBleWorker] Special key pressed 82c - -112077 [I][SavedStruct] Loading "/int/.desktop.settings" -112095 [I][BadBleWorker] BLE Key timeout : 16 -112099 [D][BadBleWorker] line:DELAY 500 -112102 [I][BadBleWorker] BLE Key timeout : 16 -112243 [I][SavedStruct] Loading "/int/.desktop.settings" -112443 [I][SavedStruct] Loading "/int/.desktop.settings" -112537 [I][SavedStruct] Loading "/int/.desktop.settings" -112605 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -112643 [I][SavedStruct] Loading "/int/.desktop.settings" -112843 [I][SavedStruct] Loading "/int/.desktop.settings" -113037 [I][SavedStruct] Loading "/int/.desktop.settings" -113055 [I][SavedStruct] Loading "/int/.desktop.settings" -113243 [I][SavedStruct] Loading "/int/.desktop.settings" -113347 [I][BadBleWorker] BLE Key timeout : 16 -113351 [D][BadBleWorker] line:DELAY 200 -113354 [I][BadBleWorker] BLE Key timeout : 16 -113443 [I][SavedStruct] Loading "/int/.desktop.settings" -113537 [I][SavedStruct] Loading "/int/.desktop.settings" -113557 [D][BadBleWorker] line:ENTER -113559 [I][BadBleWorker] Special key pressed 28 - -113579 [I][BadBleWorker] BLE Key timeout : 16 -113583 [D][BadBleWorker] line:GUI SPACE -113585 [I][BadBleWorker] Special key pressed 82c - -113605 [I][BadBleWorker] BLE Key timeout : 16 -113608 [D][BadBleWorker] line:DELAY 500 -113610 [I][BadBleWorker] BLE Key timeout : 16 -113643 [I][SavedStruct] Loading "/int/.desktop.settings" -113843 [I][SavedStruct] Loading "/int/.desktop.settings" -114037 [I][SavedStruct] Loading "/int/.desktop.settings" -114055 [I][SavedStruct] Loading "/int/.desktop.settings" -114113 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -114243 [I][SavedStruct] Loading "/int/.desktop.settings" -114443 [I][SavedStruct] Loading "/int/.desktop.settings" -114537 [I][SavedStruct] Loading "/int/.desktop.settings" -114643 [I][SavedStruct] Loading "/int/.desktop.settings" -114843 [I][SavedStruct] Loading "/int/.desktop.settings" -114853 [I][BadBleWorker] BLE Key timeout : 16 -114861 [D][BadBleWorker] line:DELAY 200 -114865 [I][BadBleWorker] BLE Key timeout : 16 -115037 [I][SavedStruct] Loading "/int/.desktop.settings" -115055 [I][SavedStruct] Loading "/int/.desktop.settings" -115069 [D][BadBleWorker] line:ENTER -115071 [I][BadBleWorker] Special key pressed 28 - -115093 [I][BadBleWorker] BLE Key timeout : 16 -115097 [D][BadBleWorker] line:GUI SPACE -115100 [I][BadBleWorker] Special key pressed 82c - -115122 [I][BadBleWorker] BLE Key timeout : 16 -115126 [D][BadBleWorker] line:DELAY 500 -115129 [I][BadBleWorker] BLE Key timeout : 16 -115243 [I][SavedStruct] Loading "/int/.desktop.settings" -115443 [I][SavedStruct] Loading "/int/.desktop.settings" -115537 [I][SavedStruct] Loading "/int/.desktop.settings" -115632 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -115643 [I][SavedStruct] Loading "/int/.desktop.settings" -115843 [I][SavedStruct] Loading "/int/.desktop.settings" -116037 [I][SavedStruct] Loading "/int/.desktop.settings" -116055 [I][SavedStruct] Loading "/int/.desktop.settings" -116243 [I][SavedStruct] Loading "/int/.desktop.settings" -116369 [I][BadBleWorker] BLE Key timeout : 16 -116372 [D][BadBleWorker] line:DELAY 200 -116374 [I][BadBleWorker] BLE Key timeout : 16 -116443 [I][SavedStruct] Loading "/int/.desktop.settings" -116537 [I][SavedStruct] Loading "/int/.desktop.settings" -116577 [D][BadBleWorker] line:ENTER -116579 [I][BadBleWorker] Special key pressed 28 - -116599 [I][BadBleWorker] BLE Key timeout : 16 -116603 [I][BadBleWorker] BLE Key timeout : 16 -116643 [I][SavedStruct] Loading "/int/.desktop.settings" -116843 [I][SavedStruct] Loading "/int/.desktop.settings" -117037 [I][SavedStruct] Loading "/int/.desktop.settings" -117056 [I][SavedStruct] Loading "/int/.desktop.settings" -117243 [I][SavedStruct] Loading "/int/.desktop.settings" -117443 [I][SavedStruct] Loading "/int/.desktop.settings" -117537 [I][SavedStruct] Loading "/int/.desktop.settings" -117643 [I][SavedStruct] Loading "/int/.desktop.settings" -117843 [I][SavedStruct] Loading "/int/.desktop.settings" -118037 [I][SavedStruct] Loading "/int/.desktop.settings" -118056 [I][SavedStruct] Loading "/int/.desktop.settings" -118243 [I][SavedStruct] Loading "/int/.desktop.settings" -118443 [I][SavedStruct] Loading "/int/.desktop.settings" -118537 [I][SavedStruct] Loading "/int/.desktop.settings" -118643 [I][SavedStruct] Loading "/int/.desktop.settings" -118843 [I][SavedStruct] Loading "/int/.desktop.settings" -119037 [I][SavedStruct] Loading "/int/.desktop.settings" -119056 [I][SavedStruct] Loading "/int/.desktop.settings" -119243 [I][SavedStruct] Loading "/int/.desktop.settings" -119443 [I][SavedStruct] Loading "/int/.desktop.settings" -119537 [I][SavedStruct] Loading "/int/.desktop.settings" -119643 [I][SavedStruct] Loading "/int/.desktop.settings" -119843 [I][SavedStruct] Loading "/int/.desktop.settings" -120037 [I][SavedStruct] Loading "/int/.desktop.settings" -120056 [I][SavedStruct] Loading "/int/.desktop.settings" -120243 [I][SavedStruct] Loading "/int/.desktop.settings" -120443 [I][SavedStruct] Loading "/int/.desktop.settings" -120537 [I][SavedStruct] Loading "/int/.desktop.settings" -120643 [I][SavedStruct] Loading "/int/.desktop.settings" -120843 [I][SavedStruct] Loading "/int/.desktop.settings" -121037 [I][SavedStruct] Loading "/int/.desktop.settings" -121056 [I][SavedStruct] Loading "/int/.desktop.settings" -121243 [I][SavedStruct] Loading "/int/.desktop.settings" -121443 [I][SavedStruct] Loading "/int/.desktop.settings" -121537 [I][SavedStruct] Loading "/int/.desktop.settings" -121643 [I][SavedStruct] Loading "/int/.desktop.settings" -121843 [I][SavedStruct] Loading "/int/.desktop.settings" -122037 [I][SavedStruct] Loading "/int/.desktop.settings" -122056 [I][SavedStruct] Loading "/int/.desktop.settings" -122243 [I][SavedStruct] Loading "/int/.desktop.settings" -122443 [I][SavedStruct] Loading "/int/.desktop.settings" -122537 [I][SavedStruct] Loading "/int/.desktop.settings" -122643 [I][SavedStruct] Loading "/int/.desktop.settings" -122843 [I][SavedStruct] Loading "/int/.desktop.settings" -123037 [I][SavedStruct] Loading "/int/.desktop.settings" -123056 [I][SavedStruct] Loading "/int/.desktop.settings" -123243 [I][SavedStruct] Loading "/int/.desktop.settings" -123443 [I][SavedStruct] Loading "/int/.desktop.settings" -123537 [I][SavedStruct] Loading "/int/.desktop.settings" -123643 [I][SavedStruct] Loading "/int/.desktop.settings" -123843 [I][SavedStruct] Loading "/int/.desktop.settings" -124037 [I][SavedStruct] Loading "/int/.desktop.settings" -124056 [I][SavedStruct] Loading "/int/.desktop.settings" -124243 [I][SavedStruct] Loading "/int/.desktop.settings" -124335 [I][BtGap] Stop advertising -124338 [D][BtGap] terminate success -124341 [E][BtGap] set_non_discoverable failed 12 -124345 [I][SavedStruct] Loading "/int/.desktop.settings" -124443 [I][SavedStruct] Loading "/int/.desktop.settings" -124483 [I][BtGap] Disconnect from client. Reason: 16 -124545 [I][SavedStruct] Loading "/int/.bt.settings" -124558 [I][SavedStruct] Loading "/int/.bt.keys" -124567 [I][FuriHalBt] Disconnect and stop advertising -124571 [I][FuriHalBt] Stop current profile services -124582 [I][FuriHalBt] Stop BLE related RTOS threads -124606 [I][FuriHalBt] Reset SHCI -124643 [I][SavedStruct] Loading "/int/.desktop.settings" -124720 [I][FuriHalBt] Start BT initialization -124725 [I][Core2] Core2 started -124727 [I][Core2] C2 boot completed, mode: Stack -124730 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -124732 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -124736 [I][Core2] Radio stack started -124738 [I][Core2] Flash activity control switched to SEM7 -124740 [I][BtGap] Advertising name: Keyboard -124742 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -124753 [E][BtGap] Message queue get error: -4 -124762 [D][BtBatterySvc] Updating power state characteristic -124768 [I][BtSrv] Bt App started -124770 [I][BtGap] Start advertising -124772 [I][BadBleWorker] End -124774 [I][SavedStruct] Loading "/int/.desktop.settings" -124785 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -124790 [I][BtGap] Rssi: 328 -124801 [D][BrowserWorker] Start -124803 [I][SavedStruct] Loading "/int/.desktop.settings" -124834 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -124836 [D][BrowserWorker] Load offset: 0 cnt: 50 -124880 [D][BtGap] Slave security initiated -124999 [I][BtGap] Pairing complete -125003 [I][BtSrv] Open RPC connection -125006 [D][RpcSrv] Session started -125008 [D][BtBatterySvc] Updating battery level characteristic -125025 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 -125028 [D][BrowserWorker] Load offset: 0 cnt: 50 -125146 [I][BtGap] Rx MTU size: 414 -125268 [D][BrowserWorker] End -125270 [I][SavedStruct] Loading "/int/.desktop.settings" -125315 [I][fap_loader_app] FAP app returned: 0 -125347 [I][LoaderSrv] Application stopped. Free heap: 118720 -125376 [I][AnimationStorage] Custom Manifest selected -126074 [I][AnimationManager] Select 'SPIRAL' animation -126078 [I][AnimationManager] Load animation 'SPIRAL' -126102 [I][SavedStruct] Loading "/int/.desktop.settings" -127063 [D][BtSerialSvc] Received 57 bytes -127066 [D][BtSerialSvc] Available buff size: 967 -127069 [D][RpcStorage] Read -127123 [D][RpcStorage] Stat -127183 [D][RpcStorage] Info -127243 [D][BtSerialSvc] Received 12 bytes -127245 [D][BtSerialSvc] Available buff size: 1012 -127247 [D][RpcStorage] Info -127301 [D][BtSerialSvc] Received 23 bytes -127303 [D][BtSerialSvc] Available buff size: 1001 -127306 [D][RpcSystem] SetDatetime -127359 [D][BtSerialSvc] Received 29 bytes -127361 [D][BtSerialSvc] Available buff size: 995 -127364 [D][RpcStorage] Stat -127446 [D][BtSerialSvc] Received 11 bytes -127448 [D][BtSerialSvc] Available buff size: 1013 -127451 [D][RpcStorage] List -127745 [D][BtSerialSvc] Received 18 bytes -127747 [D][BtSerialSvc] Available buff size: 1006 -127749 [D][RpcStorage] List -127983 [D][BtSerialSvc] Received 18 bytes -127985 [D][BtSerialSvc] Available buff size: 1006 -127988 [D][RpcStorage] List -128041 [D][BtSerialSvc] Received 15 bytes -128043 [D][BtSerialSvc] Available buff size: 1009 -128046 [D][RpcStorage] List -128219 [D][BtSerialSvc] Received 15 bytes -128222 [D][BtSerialSvc] Available buff size: 1009 -128225 [D][RpcStorage] List -128399 [D][BtSerialSvc] Received 20 bytes -128403 [D][BtSerialSvc] Available buff size: 1004 -128408 [D][RpcStorage] List -128576 [D][BtSerialSvc] Received 19 bytes -128578 [D][BtSerialSvc] Available buff size: 1005 -128580 [D][RpcStorage] List -128633 [D][BtSerialSvc] Received 27 bytes -128635 [D][BtSerialSvc] Available buff size: 997 -128639 [D][RpcStorage] Md5sum -129503 [D][BtSerialSvc] Received 37 bytes -129505 [D][BtSerialSvc] Available buff size: 987 -129512 [D][RpcStorage] Md5sum -129591 [D][BtSerialSvc] Received 38 bytes -129593 [D][BtSerialSvc] Available buff size: 986 -129600 [D][RpcStorage] Md5sum -129678 [D][BtSerialSvc] Received 37 bytes -129680 [D][BtSerialSvc] Available buff size: 987 -129683 [D][RpcStorage] Md5sum -129736 [D][BtSerialSvc] Received 38 bytes -129738 [D][BtSerialSvc] Available buff size: 986 -129741 [D][RpcStorage] Md5sum -129794 [D][BtSerialSvc] Received 36 bytes -129796 [D][BtSerialSvc] Available buff size: 988 -129799 [D][RpcStorage] Md5sum -130032 [D][BtSerialSvc] Received 36 bytes -130034 [D][BtSerialSvc] Available buff size: 988 -130037 [D][RpcStorage] Md5sum -130120 [D][BtSerialSvc] Received 36 bytes -130122 [D][BtSerialSvc] Available buff size: 988 -130125 [D][RpcStorage] Md5sum - ->: - _.-------.._ -, - .-"```"--..,,_/ /`-, -, \ - .:" /:/ /'\ \ ,_..., `. | | - / ,----/:/ /`\ _\~`_-"` _; - ' / /`"""'\ \ \.~`_-' ,-"'/ - | | | 0 | | .-' ,/` / - | ,..\ \ ,.-"` ,/` / - ; : `/`""\` ,/--==,/-----, - | `-...| -.___-Z:_______J...---; - : ` _-' - _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ -| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| -| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | -|_| |____||___||_| |_| |___||_|_\ \___||____||___| - -Welcome to Flipper Zero Command Line Interface! -Read Manual https://docs.flipperzero.one - -Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) - ->: log -Press CTRL+C to stop... -25524 [D][BrowserWorker] End -25537 [I][BtGap] Stop advertising -25541 [D][BtGap] set_non_discoverable success -25555 [I][SavedStruct] Loading "/int/.desktop.settings" -25706 [I][SavedStruct] Loading "/int/.desktop.settings" -25747 [I][FuriHalBt] Disconnect and stop advertising -25749 [I][FuriHalBt] Stop current profile services -25758 [I][FuriHalBt] Stop BLE related RTOS threads -25782 [I][FuriHalBt] Reset SHCI -25896 [I][FuriHalBt] Start BT initialization -25901 [I][Core2] Core2 started -25903 [I][Core2] C2 boot completed, mode: Stack -25906 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -25908 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -25912 [I][SavedStruct] Loading "/int/.desktop.settings" -25915 [I][Core2] Radio stack started -25921 [I][Core2] Flash activity control switched to SEM7 -25925 [I][BtGap] Advertising name: Keyboard -25928 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -25950 [D][BtBatterySvc] Updating power state characteristic -25956 [I][BtGap] Start advertising -25958 [I][SavedStruct] Loading "/int/.bt.settings" -25975 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -25984 [I][FuriHalBt] Disconnect and stop advertising -25986 [I][BtGap] Stop advertising -25989 [D][BtGap] set_non_discoverable success -25992 [I][FuriHalBt] Stop current profile services -26001 [I][FuriHalBt] Stop BLE related RTOS threads -26026 [I][FuriHalBt] Reset SHCI -26051 [I][SavedStruct] Loading "/int/.desktop.settings" -26106 [I][SavedStruct] Loading "/int/.desktop.settings" -26140 [I][FuriHalBt] Start BT initialization -26145 [I][Core2] Core2 started -26147 [I][Core2] C2 boot completed, mode: Stack -26150 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -26152 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -26156 [I][Core2] Radio stack started -26159 [I][Core2] Flash activity control switched to SEM7 -26161 [I][BtGap] Advertising name: Rumik1 -26164 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -26181 [D][BtBatterySvc] Updating power state characteristic -26190 [I][BtSrv] Bt App started -26192 [I][BtGap] Start advertising -26195 [I][BadBleWorker] Init -26197 [I][SavedStruct] Loading "/int/.desktop.settings" -26224 [I][BadBleWorker] BLE Key timeout : 16 -26306 [I][SavedStruct] Loading "/int/.desktop.settings" -26506 [I][SavedStruct] Loading "/int/.desktop.settings" -26551 [I][SavedStruct] Loading "/int/.desktop.settings" -26706 [I][SavedStruct] Loading "/int/.desktop.settings" -26909 [I][SavedStruct] Loading "/int/.desktop.settings" -27051 [I][SavedStruct] Loading "/int/.desktop.settings" -27106 [I][SavedStruct] Loading "/int/.desktop.settings" -27306 [I][SavedStruct] Loading "/int/.desktop.settings" -27506 [I][SavedStruct] Loading "/int/.desktop.settings" -27551 [I][SavedStruct] Loading "/int/.desktop.settings" -27706 [I][SavedStruct] Loading "/int/.desktop.settings" -27906 [I][SavedStruct] Loading "/int/.desktop.settings" -28051 [I][SavedStruct] Loading "/int/.desktop.settings" -28106 [I][SavedStruct] Loading "/int/.desktop.settings" -28306 [I][SavedStruct] Loading "/int/.desktop.settings" -28506 [I][SavedStruct] Loading "/int/.desktop.settings" -28551 [I][SavedStruct] Loading "/int/.desktop.settings" -28706 [I][SavedStruct] Loading "/int/.desktop.settings" -28906 [I][SavedStruct] Loading "/int/.desktop.settings" -29051 [I][SavedStruct] Loading "/int/.desktop.settings" -29106 [I][SavedStruct] Loading "/int/.desktop.settings" -29306 [I][SavedStruct] Loading "/int/.desktop.settings" -29506 [I][SavedStruct] Loading "/int/.desktop.settings" -29551 [I][SavedStruct] Loading "/int/.desktop.settings" -29706 [I][SavedStruct] Loading "/int/.desktop.settings" -29906 [I][SavedStruct] Loading "/int/.desktop.settings" -30051 [I][SavedStruct] Loading "/int/.desktop.settings" -30106 [I][SavedStruct] Loading "/int/.desktop.settings" -30306 [I][SavedStruct] Loading "/int/.desktop.settings" -30506 [I][SavedStruct] Loading "/int/.desktop.settings" -30551 [I][SavedStruct] Loading "/int/.desktop.settings" -30706 [I][SavedStruct] Loading "/int/.desktop.settings" -30906 [I][SavedStruct] Loading "/int/.desktop.settings" -31051 [I][SavedStruct] Loading "/int/.desktop.settings" -31106 [I][SavedStruct] Loading "/int/.desktop.settings" -31306 [I][SavedStruct] Loading "/int/.desktop.settings" -31506 [I][SavedStruct] Loading "/int/.desktop.settings" -31551 [I][SavedStruct] Loading "/int/.desktop.settings" -31706 [I][SavedStruct] Loading "/int/.desktop.settings" -31906 [I][SavedStruct] Loading "/int/.desktop.settings" -32051 [I][SavedStruct] Loading "/int/.desktop.settings" -32106 [I][SavedStruct] Loading "/int/.desktop.settings" -32306 [I][SavedStruct] Loading "/int/.desktop.settings" -32506 [I][SavedStruct] Loading "/int/.desktop.settings" -32551 [I][SavedStruct] Loading "/int/.desktop.settings" -32706 [I][SavedStruct] Loading "/int/.desktop.settings" -32906 [I][SavedStruct] Loading "/int/.desktop.settings" -33051 [I][SavedStruct] Loading "/int/.desktop.settings" -33106 [I][SavedStruct] Loading "/int/.desktop.settings" -33306 [I][SavedStruct] Loading "/int/.desktop.settings" -33506 [I][SavedStruct] Loading "/int/.desktop.settings" -33551 [I][SavedStruct] Loading "/int/.desktop.settings" -33706 [I][SavedStruct] Loading "/int/.desktop.settings" -33906 [I][SavedStruct] Loading "/int/.desktop.settings" -34051 [I][SavedStruct] Loading "/int/.desktop.settings" -34106 [I][SavedStruct] Loading "/int/.desktop.settings" -34306 [I][SavedStruct] Loading "/int/.desktop.settings" -34506 [I][SavedStruct] Loading "/int/.desktop.settings" -34551 [I][SavedStruct] Loading "/int/.desktop.settings" -34706 [I][SavedStruct] Loading "/int/.desktop.settings" -34906 [I][SavedStruct] Loading "/int/.desktop.settings" -35051 [I][SavedStruct] Loading "/int/.desktop.settings" -35106 [I][SavedStruct] Loading "/int/.desktop.settings" -35306 [I][SavedStruct] Loading "/int/.desktop.settings" -35506 [I][SavedStruct] Loading "/int/.desktop.settings" -35551 [I][SavedStruct] Loading "/int/.desktop.settings" -35706 [I][SavedStruct] Loading "/int/.desktop.settings" -35906 [I][SavedStruct] Loading "/int/.desktop.settings" -36051 [I][SavedStruct] Loading "/int/.desktop.settings" -36106 [I][SavedStruct] Loading "/int/.desktop.settings" -36306 [I][SavedStruct] Loading "/int/.desktop.settings" -36506 [I][SavedStruct] Loading "/int/.desktop.settings" -36551 [I][SavedStruct] Loading "/int/.desktop.settings" -36706 [I][SavedStruct] Loading "/int/.desktop.settings" -36906 [I][SavedStruct] Loading "/int/.desktop.settings" -37051 [I][SavedStruct] Loading "/int/.desktop.settings" -37106 [I][SavedStruct] Loading "/int/.desktop.settings" -37306 [I][SavedStruct] Loading "/int/.desktop.settings" -37506 [I][SavedStruct] Loading "/int/.desktop.settings" -37551 [I][SavedStruct] Loading "/int/.desktop.settings" -37706 [I][SavedStruct] Loading "/int/.desktop.settings" -37906 [I][SavedStruct] Loading "/int/.desktop.settings" -38053 [I][SavedStruct] Loading "/int/.desktop.settings" -38106 [I][SavedStruct] Loading "/int/.desktop.settings" -38306 [I][SavedStruct] Loading "/int/.desktop.settings" -38506 [I][SavedStruct] Loading "/int/.desktop.settings" -38551 [I][SavedStruct] Loading "/int/.desktop.settings" -38706 [I][SavedStruct] Loading "/int/.desktop.settings" -38906 [I][SavedStruct] Loading "/int/.desktop.settings" -39051 [I][SavedStruct] Loading "/int/.desktop.settings" -39106 [I][SavedStruct] Loading "/int/.desktop.settings" -39256 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -39258 [I][BtGap] Rssi: 334 -39306 [I][SavedStruct] Loading "/int/.desktop.settings" -39344 [D][BtGap] Slave security initiated -39435 [I][BtGap] Rx MTU size: 414 -39506 [I][SavedStruct] Loading "/int/.desktop.settings" -39551 [I][SavedStruct] Loading "/int/.desktop.settings" -39706 [I][SavedStruct] Loading "/int/.desktop.settings" -39906 [I][SavedStruct] Loading "/int/.desktop.settings" -39941 [D][BtGap] Bond lost event. Start rebonding -40051 [I][SavedStruct] Loading "/int/.desktop.settings" -40106 [I][SavedStruct] Loading "/int/.desktop.settings" -40119 [I][BtGap] Verify numeric comparison: 448118 -41993 [I][SavedStruct] Loading "/int/.desktop.settings" -42014 [I][SavedStruct] Loading "/int/.desktop.settings" -42051 [I][SavedStruct] Loading "/int/.desktop.settings" -42106 [I][SavedStruct] Loading "/int/.desktop.settings" -42306 [I][SavedStruct] Loading "/int/.desktop.settings" -42453 [I][BtKeyStorage] Base address: 200301E0. Start update address: 20030938. Size changed: 4 -42456 [I][SavedStruct] Saving "/ext/apps/Tools/.bt_hid.keys" -42458 [I][BtGap] Pairing complete -42506 [I][BtKeyStorage] Base address: 200301E0. Start update address: 20030938. Size changed: 88 -42508 [I][SavedStruct] Saving "/ext/apps/Tools/.bt_hid.keys" -42540 [D][BtGap] RSSI: -81 -42542 [D][BtBatterySvc] Updating battery level characteristic -42544 [I][SavedStruct] Loading "/int/.desktop.settings" -42547 [D][BtGap] RSSI: -81 -42551 [I][BadBleWorker] BLE Key timeout : 33 -42569 [I][SavedStruct] Loading "/int/.desktop.settings" -42706 [I][SavedStruct] Loading "/int/.desktop.settings" -42906 [I][SavedStruct] Loading "/int/.desktop.settings" -43068 [I][SavedStruct] Loading "/int/.desktop.settings" -43106 [I][SavedStruct] Loading "/int/.desktop.settings" -43306 [I][SavedStruct] Loading "/int/.desktop.settings" -43506 [I][SavedStruct] Loading "/int/.desktop.settings" -43514 [I][BtGap] Connection parameters event complete -43518 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -43522 [W][BtGap] Unsupported connection interval. Request connection parameters update -43528 [I][BtGap] Rssi: 358 -43568 [I][SavedStruct] Loading "/int/.desktop.settings" -43570 [D][BtGap] Connection parameters accepted -43706 [I][SavedStruct] Loading "/int/.desktop.settings" -43731 [I][BtGap] Connection parameters event complete -43735 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -43737 [I][BtGap] Rssi: 337 -43906 [I][SavedStruct] Loading "/int/.desktop.settings" -44068 [I][SavedStruct] Loading "/int/.desktop.settings" -44106 [I][SavedStruct] Loading "/int/.desktop.settings" -44306 [I][SavedStruct] Loading "/int/.desktop.settings" -44506 [I][SavedStruct] Loading "/int/.desktop.settings" -44568 [I][SavedStruct] Loading "/int/.desktop.settings" -44706 [I][SavedStruct] Loading "/int/.desktop.settings" -44906 [I][SavedStruct] Loading "/int/.desktop.settings" -45068 [I][SavedStruct] Loading "/int/.desktop.settings" -45106 [I][SavedStruct] Loading "/int/.desktop.settings" -45306 [I][SavedStruct] Loading "/int/.desktop.settings" -45337 [D][DolphinState] icounter 1325, butthurt 0 -45340 [D][BtGap] RSSI: -92 -45341 [I][BadBleWorker] BLE Key timeout : 16 -45346 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -45348 [D][BtGap] RSSI: -92 -45350 [I][BadBleWorker] BLE Key timeout : 16 -45352 [D][BadBleWorker] line:DELAY 1000 -45354 [D][BtGap] RSSI: -92 -45355 [I][BadBleWorker] BLE Key timeout : 16 -45506 [I][SavedStruct] Loading "/int/.desktop.settings" -45706 [I][SavedStruct] Loading "/int/.desktop.settings" -45838 [I][SavedStruct] Loading "/int/.desktop.settings" -45906 [I][SavedStruct] Loading "/int/.desktop.settings" -46106 [I][SavedStruct] Loading "/int/.desktop.settings" -46306 [I][SavedStruct] Loading "/int/.desktop.settings" -46338 [I][SavedStruct] Loading "/int/.desktop.settings" -46357 [D][BadBleWorker] line:GUI SPACE -46359 [I][BadBleWorker] Special key pressed 82c - -46380 [D][BtGap] RSSI: -96 -46382 [I][BadBleWorker] BLE Key timeout : 16 -46385 [D][BadBleWorker] line:DELAY 500 -46388 [D][BtGap] RSSI: -96 -46390 [I][BadBleWorker] BLE Key timeout : 16 -46506 [I][SavedStruct] Loading "/int/.desktop.settings" -46706 [I][SavedStruct] Loading "/int/.desktop.settings" -46838 [I][SavedStruct] Loading "/int/.desktop.settings" -46892 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -46906 [I][SavedStruct] Loading "/int/.desktop.settings" -47106 [I][SavedStruct] Loading "/int/.desktop.settings" -47306 [I][SavedStruct] Loading "/int/.desktop.settings" -47308 [E][BtHid] Failed updating report characteristic: 100 -47313 [E][BtHid] Failed updating report characteristic: 100 -47334 [E][BtHid] Failed updating report characteristic: 100 -47338 [I][SavedStruct] Loading "/int/.desktop.settings" -47340 [E][BtHid] Failed updating report characteristic: 100 -47469 [E][BtHid] Failed updating report characteristic: 100 -47472 [E][BtHid] Failed updating report characteristic: 100 -47491 [E][BtHid] Failed updating report characteristic: 100 -47494 [E][BtHid] Failed updating report characteristic: 100 -47506 [I][SavedStruct] Loading "/int/.desktop.settings" -47513 [E][BtHid] Failed updating report characteristic: 100 -47516 [E][BtHid] Failed updating report characteristic: 100 -47537 [E][BtHid] Failed updating report characteristic: 100 -47540 [E][BtHid] Failed updating report characteristic: 100 -47559 [E][BtHid] Failed updating report characteristic: 100 -47562 [E][BtHid] Failed updating report characteristic: 100 -47581 [E][BtHid] Failed updating report characteristic: 100 -47584 [E][BtHid] Failed updating report characteristic: 100 -47603 [E][BtHid] Failed updating report characteristic: 100 -47606 [E][BtHid] Failed updating report characteristic: 100 -47625 [E][BtHid] Failed updating report characteristic: 100 -47628 [E][BtHid] Failed updating report characteristic: 100 -47682 [D][BtGap] RSSI: -97 -47684 [I][BadBleWorker] BLE Key timeout : 16 -47686 [D][BadBleWorker] line:DELAY 200 -47688 [D][BtGap] RSSI: -97 -47690 [I][BadBleWorker] BLE Key timeout : 16 -47706 [I][SavedStruct] Loading "/int/.desktop.settings" -47838 [I][SavedStruct] Loading "/int/.desktop.settings" -47892 [D][BadBleWorker] line:ENTER -47894 [I][BadBleWorker] Special key pressed 28 - -47906 [I][SavedStruct] Loading "/int/.desktop.settings" -47916 [D][BtGap] RSSI: -91 -47918 [I][BadBleWorker] BLE Key timeout : 16 -47922 [D][BadBleWorker] line:GUI SPACE -47924 [I][BadBleWorker] Special key pressed 82c - -47948 [D][BtGap] RSSI: -91 -47950 [I][BadBleWorker] BLE Key timeout : 16 -47953 [D][BadBleWorker] line:DELAY 500 -47956 [D][BtGap] RSSI: -91 -47958 [I][BadBleWorker] BLE Key timeout : 16 -48106 [I][SavedStruct] Loading "/int/.desktop.settings" -48306 [I][SavedStruct] Loading "/int/.desktop.settings" -48338 [I][SavedStruct] Loading "/int/.desktop.settings" -48460 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -48506 [I][SavedStruct] Loading "/int/.desktop.settings" -48706 [I][SavedStruct] Loading "/int/.desktop.settings" -48787 [E][BtHid] Failed updating report characteristic: 100 -48790 [E][BtHid] Failed updating report characteristic: 100 -48809 [E][BtHid] Failed updating report characteristic: 100 -48812 [E][BtHid] Failed updating report characteristic: 100 -48831 [E][BtHid] Failed updating report characteristic: 100 -48834 [E][BtHid] Failed updating report characteristic: 100 -48838 [I][SavedStruct] Loading "/int/.desktop.settings" -48854 [E][BtHid] Failed updating report characteristic: 100 -48859 [E][BtHid] Failed updating report characteristic: 100 -48878 [E][BtHid] Failed updating report characteristic: 100 -48881 [E][BtHid] Failed updating report characteristic: 100 -48900 [E][BtHid] Failed updating report characteristic: 100 -48903 [E][BtHid] Failed updating report characteristic: 100 -48907 [I][SavedStruct] Loading "/int/.desktop.settings" -48923 [E][BtHid] Failed updating report characteristic: 100 -48928 [E][BtHid] Failed updating report characteristic: 100 -48947 [E][BtHid] Failed updating report characteristic: 100 -48950 [E][BtHid] Failed updating report characteristic: 100 -48969 [E][BtHid] Failed updating report characteristic: 100 -48972 [E][BtHid] Failed updating report characteristic: 100 -48991 [E][BtHid] Failed updating report characteristic: 100 -48994 [E][BtHid] Failed updating report characteristic: 100 -49103 [E][BtHid] Failed updating report characteristic: 100 -49106 [I][SavedStruct] Loading "/int/.desktop.settings" -49123 [E][BtHid] Failed updating report characteristic: 100 -49127 [E][BtHid] Failed updating report characteristic: 100 -49146 [E][BtHid] Failed updating report characteristic: 100 -49149 [E][BtHid] Failed updating report characteristic: 100 -49168 [E][BtHid] Failed updating report characteristic: 100 -49171 [E][BtHid] Failed updating report characteristic: 100 -49190 [E][BtHid] Failed updating report characteristic: 100 -49193 [E][BtHid] Failed updating report characteristic: 100 -49213 [E][BtHid] Failed updating report characteristic: 100 -49218 [E][BtHid] Failed updating report characteristic: 100 -49238 [E][BtHid] Failed updating report characteristic: 100 -49241 [E][BtHid] Failed updating report characteristic: 100 -49260 [E][BtHid] Failed updating report characteristic: 100 -49280 [D][BtGap] RSSI: -90 -49282 [I][BadBleWorker] BLE Key timeout : 16 -49285 [D][BadBleWorker] line:DELAY 200 -49288 [D][BtGap] RSSI: -90 -49290 [I][BadBleWorker] BLE Key timeout : 16 -49306 [I][SavedStruct] Loading "/int/.desktop.settings" -49338 [I][SavedStruct] Loading "/int/.desktop.settings" -49492 [D][BadBleWorker] line:ENTER -49494 [I][BadBleWorker] Special key pressed 28 - -49506 [I][SavedStruct] Loading "/int/.desktop.settings" -49516 [D][BtGap] RSSI: -96 -49518 [I][BadBleWorker] BLE Key timeout : 16 -49527 [D][BadBleWorker] line:GUI SPACE -49529 [I][BadBleWorker] Special key pressed 82c - -49550 [D][BtGap] RSSI: -97 -49552 [I][BadBleWorker] BLE Key timeout : 16 -49555 [D][BadBleWorker] line:DELAY 500 -49557 [D][BtGap] RSSI: -97 -49558 [I][BadBleWorker] BLE Key timeout : 16 -49706 [I][SavedStruct] Loading "/int/.desktop.settings" -49838 [I][SavedStruct] Loading "/int/.desktop.settings" -49906 [I][SavedStruct] Loading "/int/.desktop.settings" -50060 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -50106 [I][SavedStruct] Loading "/int/.desktop.settings" -50306 [I][SavedStruct] Loading "/int/.desktop.settings" -50338 [I][SavedStruct] Loading "/int/.desktop.settings" -50506 [I][SavedStruct] Loading "/int/.desktop.settings" -50706 [I][SavedStruct] Loading "/int/.desktop.settings" -50796 [D][BtGap] RSSI: -91 -50798 [I][BadBleWorker] BLE Key timeout : 16 -50801 [D][BadBleWorker] line:DELAY 200 -50804 [D][BtGap] RSSI: -91 -50806 [I][BadBleWorker] BLE Key timeout : 16 -50838 [I][SavedStruct] Loading "/int/.desktop.settings" -50906 [I][SavedStruct] Loading "/int/.desktop.settings" -51008 [D][BadBleWorker] line:ENTER -51010 [I][BadBleWorker] Special key pressed 28 - -51029 [D][BtGap] RSSI: -95 -51031 [I][BadBleWorker] BLE Key timeout : 16 -51034 [D][BadBleWorker] line:GUI SPACE -51036 [I][BadBleWorker] Special key pressed 82c - -51056 [D][BtGap] RSSI: -85 -51058 [I][BadBleWorker] BLE Key timeout : 33 -51061 [D][BadBleWorker] line:DELAY 500 -51064 [D][BtGap] RSSI: -85 -51066 [I][BadBleWorker] BLE Key timeout : 33 -51106 [I][SavedStruct] Loading "/int/.desktop.settings" -51306 [I][SavedStruct] Loading "/int/.desktop.settings" -51338 [I][SavedStruct] Loading "/int/.desktop.settings" -51506 [I][SavedStruct] Loading "/int/.desktop.settings" -51568 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -51706 [I][SavedStruct] Loading "/int/.desktop.settings" -51838 [I][SavedStruct] Loading "/int/.desktop.settings" -51906 [I][SavedStruct] Loading "/int/.desktop.settings" -52106 [I][SavedStruct] Loading "/int/.desktop.settings" -52306 [I][SavedStruct] Loading "/int/.desktop.settings" -52338 [I][SavedStruct] Loading "/int/.desktop.settings" -52506 [I][SavedStruct] Loading "/int/.desktop.settings" -52706 [I][SavedStruct] Loading "/int/.desktop.settings" -52838 [I][SavedStruct] Loading "/int/.desktop.settings" -52906 [I][SavedStruct] Loading "/int/.desktop.settings" -53007 [D][BtGap] RSSI: -79 -53009 [I][BadBleWorker] BLE Key timeout : 33 -53012 [D][BadBleWorker] line:DELAY 200 -53014 [D][BtGap] RSSI: -79 -53016 [I][BadBleWorker] BLE Key timeout : 33 -53106 [I][SavedStruct] Loading "/int/.desktop.settings" -53218 [D][BadBleWorker] line:ENTER -53220 [I][BadBleWorker] Special key pressed 28 - -53257 [D][BtGap] RSSI: -80 -53259 [I][BadBleWorker] BLE Key timeout : 33 -53263 [D][BtGap] RSSI: -80 -53265 [I][BadBleWorker] BLE Key timeout : 33 -53306 [I][SavedStruct] Loading "/int/.desktop.settings" -53338 [I][SavedStruct] Loading "/int/.desktop.settings" -53506 [I][SavedStruct] Loading "/int/.desktop.settings" -53706 [I][SavedStruct] Loading "/int/.desktop.settings" -53838 [I][SavedStruct] Loading "/int/.desktop.settings" -53906 [I][SavedStruct] Loading "/int/.desktop.settings" -54106 [I][SavedStruct] Loading "/int/.desktop.settings" -54306 [I][SavedStruct] Loading "/int/.desktop.settings" -54338 [I][SavedStruct] Loading "/int/.desktop.settings" -54506 [I][SavedStruct] Loading "/int/.desktop.settings" -54706 [I][SavedStruct] Loading "/int/.desktop.settings" -54838 [I][SavedStruct] Loading "/int/.desktop.settings" -54906 [I][SavedStruct] Loading "/int/.desktop.settings" -55106 [I][SavedStruct] Loading "/int/.desktop.settings" -55306 [I][SavedStruct] Loading "/int/.desktop.settings" -55338 [I][SavedStruct] Loading "/int/.desktop.settings" -55506 [I][SavedStruct] Loading "/int/.desktop.settings" -55706 [I][SavedStruct] Loading "/int/.desktop.settings" -55838 [I][SavedStruct] Loading "/int/.desktop.settings" -55906 [I][SavedStruct] Loading "/int/.desktop.settings" -56106 [I][SavedStruct] Loading "/int/.desktop.settings" -56316 [I][SavedStruct] Loading "/int/.desktop.settings" -56338 [I][SavedStruct] Loading "/int/.desktop.settings" -56506 [I][SavedStruct] Loading "/int/.desktop.settings" -56706 [I][SavedStruct] Loading "/int/.desktop.settings" -56838 [I][SavedStruct] Loading "/int/.desktop.settings" -56906 [I][SavedStruct] Loading "/int/.desktop.settings" -57106 [I][SavedStruct] Loading "/int/.desktop.settings" -57306 [I][SavedStruct] Loading "/int/.desktop.settings" -57338 [I][SavedStruct] Loading "/int/.desktop.settings" -57506 [I][SavedStruct] Loading "/int/.desktop.settings" -57706 [I][SavedStruct] Loading "/int/.desktop.settings" -57838 [I][SavedStruct] Loading "/int/.desktop.settings" -57906 [I][SavedStruct] Loading "/int/.desktop.settings" -58083 [I][BtGap] Stop advertising -58086 [D][BtGap] terminate success -58089 [E][BtGap] set_non_discoverable failed 12 -58093 [I][SavedStruct] Loading "/int/.desktop.settings" -58112 [I][SavedStruct] Loading "/int/.desktop.settings" -58131 [I][BtGap] Disconnect from client. Reason: 16 -58293 [I][SavedStruct] Loading "/int/.bt.settings" -58307 [I][SavedStruct] Loading "/int/.bt.keys" -58317 [I][FuriHalBt] Disconnect and stop advertising -58320 [I][FuriHalBt] Stop current profile services -58324 [I][SavedStruct] Loading "/int/.desktop.settings" -58346 [I][FuriHalBt] Stop BLE related RTOS threads -58374 [I][FuriHalBt] Reset SHCI -58488 [I][FuriHalBt] Start BT initialization -58493 [I][Core2] Core2 started -58495 [I][Core2] C2 boot completed, mode: Stack -58498 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -58500 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -58504 [I][Core2] Radio stack started -58507 [I][Core2] Flash activity control switched to SEM7 -58511 [I][BtGap] Advertising name: Keyboard -58514 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -58518 [I][SavedStruct] Loading "/int/.desktop.settings" -58541 [D][BtBatterySvc] Updating power state characteristic -58547 [I][BtSrv] Bt App started -58548 [I][BtGap] Start advertising -58550 [I][BadBleWorker] End -58552 [I][SavedStruct] Loading "/int/.desktop.settings" -58573 [I][SavedStruct] Loading "/int/.desktop.settings" -58575 [D][BrowserWorker] Start -58608 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -58610 [D][BrowserWorker] Load offset: 0 cnt: 50 -58724 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 -58727 [D][BrowserWorker] Load offset: 0 cnt: 50 -59009 [D][BrowserWorker] End -59011 [I][SavedStruct] Loading "/int/.desktop.settings" -59055 [I][fap_loader_app] FAP app returned: 0 -59091 [I][LoaderSrv] Application stopped. Free heap: 127088 -59120 [I][AnimationStorage] Custom Manifest selected -59411 [I][AnimationManager] Select 'HANDS' animation -59415 [I][AnimationManager] Load animation 'HANDS' -59443 [I][SavedStruct] Loading "/int/.desktop.settings" -75339 [I][Dolphin] Flush stats -75341 [I][SavedStruct] Saving "/int/.dolphin.state" -75351 [D][StorageInt] Device erase: page 9, translated page: d6 -75439 [D][StorageInt] Device sync: skipping -75441 [I][DolphinState] State saved -77873 [I][AnimationStorage] Custom Manifest selected -78353 [I][AnimationManager] Select 'DEDSEC_AD' animation -108394 [I][AnimationStorage] Custom Manifest selected -108758 [I][AnimationManager] Select 'DEDSEC_ASCII' animation -118697 [D][BtGap] set_non_discoverable success -138798 [I][AnimationStorage] Custom Manifest selected -139612 [I][AnimationManager] Select 'MARCUS' animation -169649 [I][AnimationStorage] Custom Manifest selected -170049 [I][AnimationManager] Select 'HANDS' animation -178699 [D][BtGap] set_non_discoverable success -200094 [I][AnimationStorage] Custom Manifest selected -200963 [I][AnimationManager] Select 'SKULL' animation -231003 [I][AnimationStorage] Custom Manifest selected -231753 [I][AnimationManager] Select 'GUNS_CAR' animation -238701 [D][BtGap] set_non_discoverable success -261795 [I][AnimationStorage] Custom Manifest selected -262627 [I][AnimationManager] Select 'DEDSEC_LOGO' animation -292669 [I][AnimationStorage] Custom Manifest selected -293615 [I][AnimationManager] Select 'LOGO_WD2' animation -298703 [D][BtGap] set_non_discoverable success -323656 [I][AnimationStorage] Custom Manifest selected -324243 [I][AnimationManager] Select 'DEDSEC_OLD' animation -354285 [I][AnimationStorage] Custom Manifest selected -355074 [I][AnimationManager] Select 'SPIRAL' animation -358705 [D][BtGap] set_non_discoverable success -385114 [I][AnimationStorage] Custom Manifest selected -385501 [I][AnimationManager] Select 'HANDS' animation -415546 [I][AnimationStorage] Custom Manifest selected -416332 [I][AnimationManager] Select 'SPIRAL' animation -418707 [D][BtGap] set_non_discoverable success -446371 [I][AnimationStorage] Custom Manifest selected -446861 [I][AnimationManager] Select 'DEDSEC_AD' animation -476903 [I][AnimationStorage] Custom Manifest selected -477735 [I][AnimationManager] Select 'DEDSEC_LOGO' animation -478709 [D][BtGap] set_non_discoverable success -507777 [I][AnimationStorage] Custom Manifest selected -508256 [I][AnimationManager] Select 'DEDSEC_AD' animation -538299 [I][AnimationStorage] Custom Manifest selected -539085 [I][AnimationManager] Select 'SPIRAL' animation -539117 [D][BtGap] set_non_discoverable success -569126 [I][AnimationStorage] Custom Manifest selected -569876 [I][AnimationManager] Select 'GUNS_CAR' animation -599120 [D][BtGap] set_non_discoverable success -599918 [I][AnimationStorage] Custom Manifest selected -600830 [I][AnimationManager] Select 'REAPER_ALT' animation -630871 [I][AnimationStorage] Custom Manifest selected -631392 [I][AnimationManager] Select 'DEDSEC_TALK' animation -659122 [D][BtGap] set_non_discoverable success -661435 [I][AnimationStorage] Custom Manifest selected -662238 [I][AnimationManager] Select 'MARCUS' animation -690919 [I][LoaderSrv] Starting: Applications -690925 [I][AnimationManager] Unload animation 'MARCUS' -690948 [D][BrowserWorker] Start -690965 [D][BrowserWorker] Enter folder: /ext/apps items: 6 idx: -1 -690967 [D][BrowserWorker] Load offset: 0 cnt: 50 -717407 [D][BrowserWorker] Enter folder: /ext/apps/Tools items: 23 idx: -1 -717410 [D][BrowserWorker] Load offset: 0 cnt: 50 -718007 [D][BrowserWorker] Exit to: /ext/apps items: 6 idx: 5 -718011 [D][BrowserWorker] Load offset: 0 cnt: 50 -719124 [D][BtGap] set_non_discoverable success -719398 [D][BrowserWorker] Enter folder: /ext/apps/Main items: 8 idx: -1 -719400 [D][BrowserWorker] Load offset: 0 cnt: 50 -720667 [D][BrowserWorker] End -720679 [I][fap_loader_app] FAP Loader is loading /ext/apps/Main/bad_ble.fap -720722 [I][fap_loader_app] FAP Loader is mapping -721451 [I][elf] Total size of loaded sections: 10460 -721453 [I][fap_loader_app] Loaded in 774ms -721455 [I][fap_loader_app] FAP Loader is starting app -721483 [D][BrowserWorker] Start -721498 [D][BrowserWorker] Enter folder: /any/BadUsb items: 10 idx: -1 -721502 [D][BrowserWorker] Load offset: 0 cnt: 50 -724277 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 -724280 [D][BrowserWorker] Load offset: 0 cnt: 50 -737153 [D][BrowserWorker] End -737166 [I][BtGap] Stop advertising -737170 [D][BtGap] set_non_discoverable success -737185 [I][SavedStruct] Loading "/int/.desktop.settings" -737308 [I][SavedStruct] Loading "/int/.desktop.settings" -737327 [I][SavedStruct] Loading "/int/.desktop.settings" -737376 [I][FuriHalBt] Disconnect and stop advertising -737380 [I][FuriHalBt] Stop current profile services -737395 [I][FuriHalBt] Stop BLE related RTOS threads -737419 [I][FuriHalBt] Reset SHCI -737527 [I][SavedStruct] Loading "/int/.desktop.settings" -737533 [I][FuriHalBt] Start BT initialization -737540 [I][Core2] Core2 started -737542 [I][Core2] C2 boot completed, mode: Stack -737546 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -737550 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -737557 [I][Core2] Radio stack started -737559 [I][Core2] Flash activity control switched to SEM7 -737562 [I][BtGap] Advertising name: Keyboard -737565 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -737582 [D][BtBatterySvc] Updating power state characteristic -737587 [I][BtGap] Start advertising -737590 [I][SavedStruct] Loading "/int/.bt.settings" -737607 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -737616 [I][FuriHalBt] Disconnect and stop advertising -737618 [I][BtGap] Stop advertising -737621 [D][BtGap] set_non_discoverable success -737623 [I][FuriHalBt] Stop current profile services -737633 [I][FuriHalBt] Stop BLE related RTOS threads -737641 [I][SavedStruct] Loading "/int/.desktop.settings" -737658 [I][FuriHalBt] Reset SHCI -737683 [I][SavedStruct] Loading "/int/.desktop.settings" -737727 [I][SavedStruct] Loading "/int/.desktop.settings" -737772 [I][FuriHalBt] Start BT initialization -737777 [I][Core2] Core2 started -737779 [I][Core2] C2 boot completed, mode: Stack -737782 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -737784 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -737788 [I][Core2] Radio stack started -737790 [I][Core2] Flash activity control switched to SEM7 -737792 [I][BtGap] Advertising name: Rumik1 -737794 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -737810 [D][BtBatterySvc] Updating power state characteristic -737820 [I][BtSrv] Bt App started -737822 [I][BtGap] Start advertising -737824 [I][BadBleWorker] Init -737828 [I][SavedStruct] Loading "/int/.desktop.settings" -737834 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -737839 [I][BtGap] Rssi: 296 -737863 [D][BtGap] RSSI: -43 -737865 [I][BadBleWorker] BLE Key timeout : 41 -737921 [D][BtGap] Slave security initiated -737927 [I][SavedStruct] Loading "/int/.desktop.settings" -737974 [I][SavedStruct] Loading "/int/.desktop.settings" -738039 [I][BtGap] Pairing complete -738042 [D][BtGap] RSSI: -47 -738044 [D][BtBatterySvc] Updating battery level characteristic -738048 [D][BtGap] RSSI: -47 -738050 [I][BadBleWorker] BLE Key timeout : 41 -738127 [I][SavedStruct] Loading "/int/.desktop.settings" -738183 [I][SavedStruct] Loading "/int/.desktop.settings" -738216 [I][BtGap] Rx MTU size: 414 -738307 [I][SavedStruct] Loading "/int/.desktop.settings" -738327 [I][SavedStruct] Loading "/int/.desktop.settings" -738527 [I][SavedStruct] Loading "/int/.desktop.settings" -738640 [I][SavedStruct] Loading "/int/.desktop.settings" -738683 [I][SavedStruct] Loading "/int/.desktop.settings" -738727 [I][SavedStruct] Loading "/int/.desktop.settings" -738810 [I][BtGap] Connection parameters event complete -738813 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -738815 [W][BtGap] Unsupported connection interval. Request connection parameters update -738819 [I][BtGap] Rssi: 311 -738849 [D][BtGap] Connection parameters accepted -738927 [I][SavedStruct] Loading "/int/.desktop.settings" -738973 [I][SavedStruct] Loading "/int/.desktop.settings" -739010 [I][BtGap] Connection parameters event complete -739012 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -739015 [I][BtGap] Rssi: 328 -739127 [I][SavedStruct] Loading "/int/.desktop.settings" -739183 [I][SavedStruct] Loading "/int/.desktop.settings" -739306 [I][SavedStruct] Loading "/int/.desktop.settings" -739327 [I][SavedStruct] Loading "/int/.desktop.settings" -739527 [I][SavedStruct] Loading "/int/.desktop.settings" -739639 [I][SavedStruct] Loading "/int/.desktop.settings" -739683 [I][SavedStruct] Loading "/int/.desktop.settings" -739727 [I][SavedStruct] Loading "/int/.desktop.settings" -739927 [I][SavedStruct] Loading "/int/.desktop.settings" -739972 [I][SavedStruct] Loading "/int/.desktop.settings" -740127 [I][SavedStruct] Loading "/int/.desktop.settings" -740183 [I][SavedStruct] Loading "/int/.desktop.settings" -740305 [I][SavedStruct] Loading "/int/.desktop.settings" -740327 [I][SavedStruct] Loading "/int/.desktop.settings" -740527 [I][SavedStruct] Loading "/int/.desktop.settings" -740638 [I][SavedStruct] Loading "/int/.desktop.settings" -740683 [I][SavedStruct] Loading "/int/.desktop.settings" -740727 [I][SavedStruct] Loading "/int/.desktop.settings" -740927 [I][SavedStruct] Loading "/int/.desktop.settings" -740971 [I][SavedStruct] Loading "/int/.desktop.settings" -741127 [I][SavedStruct] Loading "/int/.desktop.settings" -741183 [I][SavedStruct] Loading "/int/.desktop.settings" -741304 [I][SavedStruct] Loading "/int/.desktop.settings" -741327 [I][SavedStruct] Loading "/int/.desktop.settings" -741527 [I][SavedStruct] Loading "/int/.desktop.settings" -741637 [I][SavedStruct] Loading "/int/.desktop.settings" -741683 [I][SavedStruct] Loading "/int/.desktop.settings" -741727 [I][SavedStruct] Loading "/int/.desktop.settings" -741927 [I][SavedStruct] Loading "/int/.desktop.settings" -741970 [I][SavedStruct] Loading "/int/.desktop.settings" -742127 [I][SavedStruct] Loading "/int/.desktop.settings" -742183 [I][SavedStruct] Loading "/int/.desktop.settings" -742303 [I][SavedStruct] Loading "/int/.desktop.settings" -742327 [I][SavedStruct] Loading "/int/.desktop.settings" -742527 [I][SavedStruct] Loading "/int/.desktop.settings" -742636 [I][SavedStruct] Loading "/int/.desktop.settings" -742727 [I][SavedStruct] Loading "/int/.desktop.settings" -742927 [I][SavedStruct] Loading "/int/.desktop.settings" -742969 [I][SavedStruct] Loading "/int/.desktop.settings" -743127 [I][SavedStruct] Loading "/int/.desktop.settings" -743302 [I][SavedStruct] Loading "/int/.desktop.settings" -743327 [I][SavedStruct] Loading "/int/.desktop.settings" -743382 [I][SavedStruct] Loading "/int/.desktop.settings" -743527 [I][SavedStruct] Loading "/int/.desktop.settings" -743635 [I][SavedStruct] Loading "/int/.desktop.settings" -743727 [I][SavedStruct] Loading "/int/.desktop.settings" -743882 [I][SavedStruct] Loading "/int/.desktop.settings" -743927 [I][SavedStruct] Loading "/int/.desktop.settings" -743968 [I][SavedStruct] Loading "/int/.desktop.settings" -744127 [I][SavedStruct] Loading "/int/.desktop.settings" -744301 [I][SavedStruct] Loading "/int/.desktop.settings" -744327 [I][SavedStruct] Loading "/int/.desktop.settings" -744382 [I][SavedStruct] Loading "/int/.desktop.settings" -744527 [I][SavedStruct] Loading "/int/.desktop.settings" -744634 [I][SavedStruct] Loading "/int/.desktop.settings" -744727 [I][SavedStruct] Loading "/int/.desktop.settings" -744882 [I][SavedStruct] Loading "/int/.desktop.settings" -744927 [I][SavedStruct] Loading "/int/.desktop.settings" -744967 [I][SavedStruct] Loading "/int/.desktop.settings" -745127 [I][SavedStruct] Loading "/int/.desktop.settings" -745300 [I][SavedStruct] Loading "/int/.desktop.settings" -745327 [I][SavedStruct] Loading "/int/.desktop.settings" -745382 [I][SavedStruct] Loading "/int/.desktop.settings" -745527 [I][SavedStruct] Loading "/int/.desktop.settings" -745633 [I][SavedStruct] Loading "/int/.desktop.settings" -745727 [I][SavedStruct] Loading "/int/.desktop.settings" -745882 [I][SavedStruct] Loading "/int/.desktop.settings" -745927 [I][SavedStruct] Loading "/int/.desktop.settings" -745966 [I][SavedStruct] Loading "/int/.desktop.settings" -746127 [I][SavedStruct] Loading "/int/.desktop.settings" -746299 [I][SavedStruct] Loading "/int/.desktop.settings" -746327 [I][SavedStruct] Loading "/int/.desktop.settings" -746382 [I][SavedStruct] Loading "/int/.desktop.settings" -746527 [I][SavedStruct] Loading "/int/.desktop.settings" -746632 [I][SavedStruct] Loading "/int/.desktop.settings" -746727 [I][SavedStruct] Loading "/int/.desktop.settings" -746882 [I][SavedStruct] Loading "/int/.desktop.settings" -746927 [I][SavedStruct] Loading "/int/.desktop.settings" -746965 [I][SavedStruct] Loading "/int/.desktop.settings" -747127 [I][SavedStruct] Loading "/int/.desktop.settings" -747298 [I][SavedStruct] Loading "/int/.desktop.settings" -747327 [I][SavedStruct] Loading "/int/.desktop.settings" -747453 [D][DolphinState] icounter 1325, butthurt 0 -747456 [D][BtGap] RSSI: -65 -747458 [I][BadBleWorker] BLE Key timeout : 37 -747462 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -747465 [D][BtGap] RSSI: -65 -747467 [I][BadBleWorker] BLE Key timeout : 37 -747470 [D][BadBleWorker] line:DELAY 1000 -747473 [D][BtGap] RSSI: -65 -747475 [I][BadBleWorker] BLE Key timeout : 37 -747527 [I][SavedStruct] Loading "/int/.desktop.settings" -747631 [I][SavedStruct] Loading "/int/.desktop.settings" -747727 [I][SavedStruct] Loading "/int/.desktop.settings" -747927 [I][SavedStruct] Loading "/int/.desktop.settings" -747954 [I][SavedStruct] Loading "/int/.desktop.settings" -747972 [I][SavedStruct] Loading "/int/.desktop.settings" -748127 [I][SavedStruct] Loading "/int/.desktop.settings" -748297 [I][SavedStruct] Loading "/int/.desktop.settings" -748327 [I][SavedStruct] Loading "/int/.desktop.settings" -748454 [I][SavedStruct] Loading "/int/.desktop.settings" -748477 [D][BadBleWorker] line:GUI SPACE -748479 [I][BadBleWorker] Special key pressed 82c - -748520 [D][BtGap] RSSI: -77 -748523 [I][BadBleWorker] BLE Key timeout : 33 -748531 [D][BadBleWorker] line:DELAY 500 -748535 [D][BtGap] RSSI: -77 -748537 [I][BadBleWorker] BLE Key timeout : 33 -748543 [I][SavedStruct] Loading "/int/.desktop.settings" -748630 [I][SavedStruct] Loading "/int/.desktop.settings" -748727 [I][SavedStruct] Loading "/int/.desktop.settings" -748927 [I][SavedStruct] Loading "/int/.desktop.settings" -748954 [I][SavedStruct] Loading "/int/.desktop.settings" -748972 [I][SavedStruct] Loading "/int/.desktop.settings" -749041 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -749127 [I][SavedStruct] Loading "/int/.desktop.settings" -749296 [I][SavedStruct] Loading "/int/.desktop.settings" -749327 [I][SavedStruct] Loading "/int/.desktop.settings" -749454 [I][SavedStruct] Loading "/int/.desktop.settings" -749527 [I][SavedStruct] Loading "/int/.desktop.settings" -749629 [I][SavedStruct] Loading "/int/.desktop.settings" -749727 [I][SavedStruct] Loading "/int/.desktop.settings" -749927 [I][SavedStruct] Loading "/int/.desktop.settings" -749954 [I][SavedStruct] Loading "/int/.desktop.settings" -749972 [I][SavedStruct] Loading "/int/.desktop.settings" -750127 [I][SavedStruct] Loading "/int/.desktop.settings" -750295 [I][SavedStruct] Loading "/int/.desktop.settings" -750327 [I][SavedStruct] Loading "/int/.desktop.settings" -750454 [I][SavedStruct] Loading "/int/.desktop.settings" -750479 [D][BtGap] RSSI: -76 -750481 [I][BadBleWorker] BLE Key timeout : 33 -750484 [D][BadBleWorker] line:DELAY 200 -750487 [D][BtGap] RSSI: -76 -750489 [I][BadBleWorker] BLE Key timeout : 33 -750527 [I][SavedStruct] Loading "/int/.desktop.settings" -750628 [I][SavedStruct] Loading "/int/.desktop.settings" -750692 [D][BadBleWorker] line:ENTER -750694 [I][BadBleWorker] Special key pressed 28 - -750727 [I][SavedStruct] Loading "/int/.desktop.settings" -750733 [D][BtGap] RSSI: -63 -750735 [I][BadBleWorker] BLE Key timeout : 37 -750738 [D][BadBleWorker] line:GUI SPACE -750740 [I][BadBleWorker] Special key pressed 82c - -750782 [D][BtGap] RSSI: -62 -750784 [I][BadBleWorker] BLE Key timeout : 37 -750788 [D][BadBleWorker] line:DELAY 500 -750791 [D][BtGap] RSSI: -62 -750793 [I][BadBleWorker] BLE Key timeout : 37 -750927 [I][SavedStruct] Loading "/int/.desktop.settings" -750954 [I][SavedStruct] Loading "/int/.desktop.settings" -750972 [I][SavedStruct] Loading "/int/.desktop.settings" -751127 [I][SavedStruct] Loading "/int/.desktop.settings" -751294 [I][SavedStruct] Loading "/int/.desktop.settings" -751304 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -751327 [I][SavedStruct] Loading "/int/.desktop.settings" -751454 [I][SavedStruct] Loading "/int/.desktop.settings" -751527 [I][SavedStruct] Loading "/int/.desktop.settings" -751627 [I][SavedStruct] Loading "/int/.desktop.settings" -751727 [I][SavedStruct] Loading "/int/.desktop.settings" -751927 [I][SavedStruct] Loading "/int/.desktop.settings" -751954 [I][SavedStruct] Loading "/int/.desktop.settings" -751972 [I][SavedStruct] Loading "/int/.desktop.settings" -752127 [I][SavedStruct] Loading "/int/.desktop.settings" -752293 [I][SavedStruct] Loading "/int/.desktop.settings" -752327 [I][SavedStruct] Loading "/int/.desktop.settings" -752454 [I][SavedStruct] Loading "/int/.desktop.settings" -752527 [I][SavedStruct] Loading "/int/.desktop.settings" -752626 [I][SavedStruct] Loading "/int/.desktop.settings" -752727 [I][SavedStruct] Loading "/int/.desktop.settings" -752908 [D][BtGap] RSSI: -54 -752910 [I][BadBleWorker] BLE Key timeout : 37 -752914 [D][BadBleWorker] line:DELAY 200 -752917 [D][BtGap] RSSI: -54 -752919 [I][BadBleWorker] BLE Key timeout : 37 -752927 [I][SavedStruct] Loading "/int/.desktop.settings" -752954 [I][SavedStruct] Loading "/int/.desktop.settings" -752972 [I][SavedStruct] Loading "/int/.desktop.settings" -753122 [D][BadBleWorker] line:ENTER -753124 [I][BadBleWorker] Special key pressed 28 - -753127 [I][SavedStruct] Loading "/int/.desktop.settings" -753166 [D][BtGap] RSSI: -54 -753168 [I][BadBleWorker] BLE Key timeout : 37 -753172 [D][BadBleWorker] line:GUI SPACE -753174 [I][BadBleWorker] Special key pressed 82c - -753215 [D][BtGap] RSSI: -55 -753217 [I][BadBleWorker] BLE Key timeout : 37 -753220 [D][BadBleWorker] line:DELAY 500 -753224 [D][BtGap] RSSI: -55 -753226 [I][BadBleWorker] BLE Key timeout : 37 -753292 [I][SavedStruct] Loading "/int/.desktop.settings" -753327 [I][SavedStruct] Loading "/int/.desktop.settings" -753527 [I][SavedStruct] Loading "/int/.desktop.settings" -753625 [I][SavedStruct] Loading "/int/.desktop.settings" -753727 [I][SavedStruct] Loading "/int/.desktop.settings" -753927 [I][SavedStruct] Loading "/int/.desktop.settings" -753958 [I][SavedStruct] Loading "/int/.desktop.settings" -753998 [I][SavedStruct] Loading "/int/.desktop.settings" -754127 [I][SavedStruct] Loading "/int/.desktop.settings" -754146 [I][BtGap] Stop advertising -754149 [D][BtGap] terminate success -754152 [E][BtGap] set_non_discoverable failed 12 -754156 [I][SavedStruct] Loading "/int/.desktop.settings" -754275 [I][BtGap] Disconnect from client. Reason: 16 -754291 [I][SavedStruct] Loading "/int/.desktop.settings" -754327 [I][SavedStruct] Loading "/int/.desktop.settings" -754356 [I][SavedStruct] Loading "/int/.bt.settings" -754370 [I][SavedStruct] Loading "/int/.bt.keys" -754380 [I][FuriHalBt] Disconnect and stop advertising -754382 [I][FuriHalBt] Stop current profile services -754393 [I][FuriHalBt] Stop BLE related RTOS threads -754417 [I][FuriHalBt] Reset SHCI -754527 [I][SavedStruct] Loading "/int/.desktop.settings" -754531 [I][FuriHalBt] Start BT initialization -754538 [I][Core2] Core2 started -754541 [I][Core2] C2 boot completed, mode: Stack -754544 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -754547 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -754554 [I][Core2] Radio stack started -754559 [I][Core2] Flash activity control switched to SEM7 -754563 [I][BtGap] Advertising name: Keyboard -754567 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -754585 [D][BtBatterySvc] Updating power state characteristic -754591 [I][BtSrv] Bt App started -754592 [I][BtGap] Start advertising -754596 [I][BadBleWorker] End -754599 [I][SavedStruct] Loading "/int/.desktop.settings" -754630 [I][SavedStruct] Loading "/int/.desktop.settings" -754634 [D][BrowserWorker] Start -754661 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -754664 [D][BrowserWorker] Load offset: 0 cnt: 50 - - _.-------.._ -, - .-"```"--..,,_/ /`-, -, \ - .:" /:/ /'\ \ ,_..., `. | | - / ,----/:/ /`\ _\~`_-"` _; - ' / /`"""'\ \ \.~`_-' ,-"'/ - | | | 0 | | .-' ,/` / - | ,..\ \ ,.-"` ,/` / - ; : `/`""\` ,/--==,/-----, - | `-...| -.___-Z:_______J...---; - : ` _-' - _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ -| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| -| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | -|_| |____||___||_| |_| |___||_|_\ \___||____||___| - -Welcome to Flipper Zero Command Line Interface! -Read Manual https://docs.flipperzero.one - -Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) - ->: log -Press CTRL+C to stop... -988327 [I][SavedStruct] Loading "/int/.desktop.settings" -988390 [I][SavedStruct] Loading "/int/.desktop.settings" -988527 [I][SavedStruct] Loading "/int/.desktop.settings" -988555 [I][SavedStruct] Loading "/int/.desktop.settings" -988723 [I][SavedStruct] Loading "/int/.desktop.settings" -988742 [I][SavedStruct] Loading "/int/.desktop.settings" -988927 [I][SavedStruct] Loading "/int/.desktop.settings" -989055 [I][SavedStruct] Loading "/int/.desktop.settings" -989074 [I][SavedStruct] Loading "/int/.desktop.settings" -989127 [I][SavedStruct] Loading "/int/.desktop.settings" -989327 [I][SavedStruct] Loading "/int/.desktop.settings" -989389 [I][SavedStruct] Loading "/int/.desktop.settings" -989527 [I][SavedStruct] Loading "/int/.desktop.settings" -989555 [I][SavedStruct] Loading "/int/.desktop.settings" -989722 [I][SavedStruct] Loading "/int/.desktop.settings" -989741 [I][SavedStruct] Loading "/int/.desktop.settings" -989927 [I][SavedStruct] Loading "/int/.desktop.settings" -990055 [I][SavedStruct] Loading "/int/.desktop.settings" -990074 [I][SavedStruct] Loading "/int/.desktop.settings" -990127 [I][SavedStruct] Loading "/int/.desktop.settings" -990327 [I][SavedStruct] Loading "/int/.desktop.settings" -990388 [I][SavedStruct] Loading "/int/.desktop.settings" -990527 [I][SavedStruct] Loading "/int/.desktop.settings" -990558 [I][BtGap] Stop advertising -990561 [D][BtGap] terminate success -990564 [E][BtGap] set_non_discoverable failed 12 -990568 [I][SavedStruct] Loading "/int/.desktop.settings" -990720 [I][BtGap] Disconnect from client. Reason: 16 -990723 [I][SavedStruct] Loading "/int/.desktop.settings" -990743 [I][SavedStruct] Loading "/int/.desktop.settings" -990768 [I][SavedStruct] Loading "/int/.bt.settings" -990782 [I][SavedStruct] Loading "/int/.bt.keys" -990792 [I][FuriHalBt] Disconnect and stop advertising -990794 [I][FuriHalBt] Stop current profile services -990805 [I][FuriHalBt] Stop BLE related RTOS threads -990829 [I][FuriHalBt] Reset SHCI -990927 [I][SavedStruct] Loading "/int/.desktop.settings" -990943 [I][FuriHalBt] Start BT initialization -990949 [I][Core2] Core2 started -990951 [I][Core2] C2 boot completed, mode: Stack -990954 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -990956 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -990960 [I][Core2] Radio stack started -990962 [I][Core2] Flash activity control switched to SEM7 -990964 [I][BtGap] Advertising name: Keyboard -990966 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -990983 [D][BtBatterySvc] Updating power state characteristic -990989 [I][BtSrv] Bt App started -990991 [I][BtGap] Start advertising -990993 [I][BadBleWorker] End -990995 [I][SavedStruct] Loading "/int/.desktop.settings" -991016 [I][SavedStruct] Loading "/int/.desktop.settings" -991020 [D][BrowserWorker] Start -991046 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -991048 [D][BrowserWorker] Load offset: 0 cnt: 50 -995286 [D][BrowserWorker] End -995288 [I][SavedStruct] Loading "/int/.desktop.settings" -995311 [I][BtGap] Stop advertising -995315 [D][BtGap] set_non_discoverable success -995321 [I][SavedStruct] Loading "/int/.desktop.settings" -995342 [I][SavedStruct] Loading "/int/.desktop.settings" -995360 [I][SavedStruct] Loading "/int/.desktop.settings" -995383 [I][SavedStruct] Loading "/int/.desktop.settings" -995520 [I][FuriHalBt] Disconnect and stop advertising -995522 [I][FuriHalBt] Stop current profile services -995527 [I][SavedStruct] Loading "/int/.desktop.settings" -995539 [I][FuriHalBt] Stop BLE related RTOS threads -995565 [I][FuriHalBt] Reset SHCI -995679 [I][FuriHalBt] Start BT initialization -995684 [I][Core2] Core2 started -995686 [I][Core2] C2 boot completed, mode: Stack -995689 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -995691 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -995695 [I][Core2] Radio stack started -995697 [I][Core2] Flash activity control switched to SEM7 -995699 [I][BtGap] Advertising name: Keyboard -995701 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -995712 [E][BtGap] Message queue get error: -4 -995716 [I][SavedStruct] Loading "/int/.desktop.settings" -995728 [D][BtBatterySvc] Updating power state characteristic -995738 [I][BtGap] Start advertising -995741 [I][SavedStruct] Loading "/int/.bt.settings" -995745 [I][SavedStruct] Loading "/int/.desktop.settings" -995773 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -995786 [I][FuriHalBt] Disconnect and stop advertising -995789 [I][BtGap] Stop advertising -995791 [D][BtGap] set_non_discoverable success -995794 [I][FuriHalBt] Stop current profile services -995805 [I][FuriHalBt] Stop BLE related RTOS threads -995830 [I][FuriHalBt] Reset SHCI -995927 [I][SavedStruct] Loading "/int/.desktop.settings" -995944 [I][FuriHalBt] Start BT initialization -995949 [I][Core2] Core2 started -995951 [I][Core2] C2 boot completed, mode: Stack -995954 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -995956 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -995960 [I][Core2] Radio stack started -995962 [I][Core2] Flash activity control switched to SEM7 -995964 [I][BtGap] Advertising name: Rumik1 -995966 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -995984 [D][BtBatterySvc] Updating power state characteristic -995993 [I][BtSrv] Bt App started -995994 [I][BtGap] Start advertising -995998 [I][BadBleWorker] Init -996000 [I][SavedStruct] Loading "/int/.desktop.settings" -996023 [I][BadBleWorker] BLE Key timeout : 16 -996027 [I][BtGap] Stop advertising -996030 [D][BtGap] set_non_discoverable success -996034 [I][SavedStruct] Loading "/int/.desktop.settings" -996052 [I][SavedStruct] Loading "/int/.desktop.settings" -996127 [I][SavedStruct] Loading "/int/.desktop.settings" -996234 [I][SavedStruct] Loading "/int/.bt.settings" -996248 [I][SavedStruct] Loading "/int/.bt.keys" -996258 [I][FuriHalBt] Disconnect and stop advertising -996260 [I][FuriHalBt] Stop current profile services -996271 [I][FuriHalBt] Stop BLE related RTOS threads -996296 [I][FuriHalBt] Reset SHCI -996327 [I][SavedStruct] Loading "/int/.desktop.settings" -996382 [I][SavedStruct] Loading "/int/.desktop.settings" -996410 [I][FuriHalBt] Start BT initialization -996415 [I][Core2] Core2 started -996417 [I][Core2] C2 boot completed, mode: Stack -996420 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -996422 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -996426 [I][Core2] Radio stack started -996428 [I][Core2] Flash activity control switched to SEM7 -996430 [I][BtGap] Advertising name: Keyboard -996432 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -996449 [D][BtBatterySvc] Updating power state characteristic -996456 [I][BtSrv] Bt App started -996458 [I][BtGap] Start advertising -996461 [I][BadBleWorker] End -996464 [I][SavedStruct] Loading "/int/.desktop.settings" -996482 [I][SavedStruct] Loading "/int/.desktop.settings" -996485 [D][BrowserWorker] Start -996510 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 6 -996512 [D][BrowserWorker] Load offset: 0 cnt: 50 -996800 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 -996803 [D][BrowserWorker] Load offset: 0 cnt: 50 -999551 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 -999553 [D][BrowserWorker] Load offset: 0 cnt: 50 -1001657 [D][BrowserWorker] End -1001660 [I][SavedStruct] Loading "/int/.desktop.settings" -1001680 [I][BtGap] Stop advertising -1001685 [D][BtGap] set_non_discoverable success -1001692 [I][SavedStruct] Loading "/int/.desktop.settings" -1001710 [I][SavedStruct] Loading "/int/.desktop.settings" -1001726 [I][SavedStruct] Loading "/int/.desktop.settings" -1001744 [I][SavedStruct] Loading "/int/.desktop.settings" -1001890 [I][FuriHalBt] Disconnect and stop advertising -1001892 [I][FuriHalBt] Stop current profile services -1001902 [I][FuriHalBt] Stop BLE related RTOS threads -1001926 [I][FuriHalBt] Reset SHCI -1001929 [I][SavedStruct] Loading "/int/.desktop.settings" -1002040 [I][FuriHalBt] Start BT initialization -1002044 [I][SavedStruct] Loading "/int/.desktop.settings" -1002046 [I][Core2] Core2 started -1002049 [I][Core2] C2 boot completed, mode: Stack -1002053 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -1002057 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -1002063 [I][Core2] Radio stack started -1002066 [I][Core2] Flash activity control switched to SEM7 -1002070 [I][BtGap] Advertising name: Keyboard -1002074 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -1002095 [D][BtBatterySvc] Updating power state characteristic -1002102 [I][BtGap] Start advertising -1002104 [I][SavedStruct] Loading "/int/.bt.settings" -1002121 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -1002130 [I][FuriHalBt] Disconnect and stop advertising -1002132 [I][BtGap] Stop advertising -1002136 [D][BtGap] set_non_discoverable success -1002139 [I][SavedStruct] Loading "/int/.desktop.settings" -1002141 [I][FuriHalBt] Stop current profile services -1002158 [I][FuriHalBt] Stop BLE related RTOS threads -1002183 [I][FuriHalBt] Reset SHCI -1002209 [I][SavedStruct] Loading "/int/.desktop.settings" -1002297 [I][FuriHalBt] Start BT initialization -1002302 [I][Core2] Core2 started -1002305 [I][Core2] C2 boot completed, mode: Stack -1002308 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -1002310 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -1002314 [I][Core2] Radio stack started -1002316 [I][Core2] Flash activity control switched to SEM7 -1002318 [I][BtGap] Advertising name: Rumik1 -1002321 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -1002327 [I][SavedStruct] Loading "/int/.desktop.settings" -1002348 [D][BtBatterySvc] Updating power state characteristic -1002357 [I][BtSrv] Bt App started -1002359 [I][BtGap] Start advertising -1002362 [I][BadBleWorker] Init -1002364 [I][SavedStruct] Loading "/int/.desktop.settings" -1002393 [I][BadBleWorker] BLE Key timeout : 16 -1002396 [I][SavedStruct] Loading "/int/.desktop.settings" -1002527 [I][SavedStruct] Loading "/int/.desktop.settings" -1002709 [I][SavedStruct] Loading "/int/.desktop.settings" -1002729 [I][SavedStruct] Loading "/int/.desktop.settings" -1002750 [I][SavedStruct] Loading "/int/.desktop.settings" -1002927 [I][SavedStruct] Loading "/int/.desktop.settings" -1002929 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -1002934 [I][BtGap] Rssi: 315 -1003036 [D][BtGap] Slave security initiated -1003042 [I][SavedStruct] Loading "/int/.desktop.settings" -1003127 [I][SavedStruct] Loading "/int/.desktop.settings" -1003155 [I][BtGap] Pairing complete -1003158 [D][BtGap] RSSI: -55 -1003160 [D][BtBatterySvc] Updating battery level characteristic -1003163 [D][BtGap] RSSI: -55 -1003165 [I][BadBleWorker] BLE Key timeout : 37 -1003209 [I][SavedStruct] Loading "/int/.desktop.settings" -1003327 [I][SavedStruct] Loading "/int/.desktop.settings" -1003331 [I][BtGap] Rx MTU size: 414 -1003375 [I][SavedStruct] Loading "/int/.desktop.settings" -1003527 [I][SavedStruct] Loading "/int/.desktop.settings" -1003708 [I][SavedStruct] Loading "/int/.desktop.settings" -1003727 [I][SavedStruct] Loading "/int/.desktop.settings" -1003746 [I][SavedStruct] Loading "/int/.desktop.settings" -1003894 [I][BtGap] Connection parameters event complete -1003896 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -1003899 [W][BtGap] Unsupported connection interval. Request connection parameters update -1003903 [I][BtGap] Rssi: 314 -1003927 [I][SavedStruct] Loading "/int/.desktop.settings" -1003933 [D][BtGap] Connection parameters accepted -1004041 [I][SavedStruct] Loading "/int/.desktop.settings" -1004095 [I][BtGap] Connection parameters event complete -1004097 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -1004100 [I][BtGap] Rssi: 317 -1004127 [I][SavedStruct] Loading "/int/.desktop.settings" -1004225 [I][SavedStruct] Loading "/int/.desktop.settings" -1004327 [I][SavedStruct] Loading "/int/.desktop.settings" -1004374 [I][SavedStruct] Loading "/int/.desktop.settings" -1004527 [I][SavedStruct] Loading "/int/.desktop.settings" -1004707 [I][SavedStruct] Loading "/int/.desktop.settings" -1004726 [I][SavedStruct] Loading "/int/.desktop.settings" -1004745 [I][SavedStruct] Loading "/int/.desktop.settings" -1004927 [I][SavedStruct] Loading "/int/.desktop.settings" -1005040 [I][SavedStruct] Loading "/int/.desktop.settings" -1005127 [I][SavedStruct] Loading "/int/.desktop.settings" -1005225 [I][SavedStruct] Loading "/int/.desktop.settings" -1005327 [I][SavedStruct] Loading "/int/.desktop.settings" -1005373 [I][SavedStruct] Loading "/int/.desktop.settings" -1005527 [I][SavedStruct] Loading "/int/.desktop.settings" -1005706 [I][SavedStruct] Loading "/int/.desktop.settings" -1005727 [I][SavedStruct] Loading "/int/.desktop.settings" -1005800 [D][DolphinState] icounter 1325, butthurt 0 -1005803 [D][BtGap] RSSI: -57 -1005805 [I][BadBleWorker] BLE Key timeout : 37 -1005809 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -1005812 [D][BtGap] RSSI: -57 -1005814 [I][BadBleWorker] BLE Key timeout : 37 -1005817 [D][BadBleWorker] line:DELAY 1000 -1005820 [D][BtGap] RSSI: -57 -1005822 [I][BadBleWorker] BLE Key timeout : 37 -1005927 [I][SavedStruct] Loading "/int/.desktop.settings" -1006039 [I][SavedStruct] Loading "/int/.desktop.settings" -1006127 [I][SavedStruct] Loading "/int/.desktop.settings" -1006301 [I][SavedStruct] Loading "/int/.desktop.settings" -1006327 [I][SavedStruct] Loading "/int/.desktop.settings" -1006372 [I][SavedStruct] Loading "/int/.desktop.settings" -1006527 [I][SavedStruct] Loading "/int/.desktop.settings" -1006705 [I][SavedStruct] Loading "/int/.desktop.settings" -1006727 [I][SavedStruct] Loading "/int/.desktop.settings" -1006801 [I][SavedStruct] Loading "/int/.desktop.settings" -1006824 [D][BadBleWorker] line:GUI SPACE -1006826 [I][BadBleWorker] Special key pressed 82c - -1006867 [D][BtGap] RSSI: -62 -1006869 [I][BadBleWorker] BLE Key timeout : 37 -1006873 [D][BadBleWorker] line:DELAY 500 -1006875 [D][BtGap] RSSI: -62 -1006877 [I][BadBleWorker] BLE Key timeout : 37 -1006927 [I][SavedStruct] Loading "/int/.desktop.settings" -1007039 [I][SavedStruct] Loading "/int/.desktop.settings" -1007127 [I][SavedStruct] Loading "/int/.desktop.settings" -1007301 [I][SavedStruct] Loading "/int/.desktop.settings" -1007327 [I][SavedStruct] Loading "/int/.desktop.settings" -1007371 [I][SavedStruct] Loading "/int/.desktop.settings" -1007388 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -1007527 [I][SavedStruct] Loading "/int/.desktop.settings" -1007704 [I][SavedStruct] Loading "/int/.desktop.settings" -1007727 [I][SavedStruct] Loading "/int/.desktop.settings" -1007801 [I][SavedStruct] Loading "/int/.desktop.settings" -1007927 [I][SavedStruct] Loading "/int/.desktop.settings" -1008037 [I][SavedStruct] Loading "/int/.desktop.settings" -1008127 [I][SavedStruct] Loading "/int/.desktop.settings" -1008301 [I][SavedStruct] Loading "/int/.desktop.settings" -1008327 [I][SavedStruct] Loading "/int/.desktop.settings" -1008370 [I][SavedStruct] Loading "/int/.desktop.settings" -1008527 [I][SavedStruct] Loading "/int/.desktop.settings" -1008703 [I][SavedStruct] Loading "/int/.desktop.settings" -1008727 [I][SavedStruct] Loading "/int/.desktop.settings" -1008801 [I][SavedStruct] Loading "/int/.desktop.settings" -1008927 [I][SavedStruct] Loading "/int/.desktop.settings" -1008996 [D][BtGap] RSSI: -79 -1008997 [I][BadBleWorker] BLE Key timeout : 33 -1008999 [D][BadBleWorker] line:DELAY 200 -1009001 [D][BtGap] RSSI: -74 -1009004 [I][BadBleWorker] BLE Key timeout : 33 -1009036 [I][SavedStruct] Loading "/int/.desktop.settings" -1009127 [I][SavedStruct] Loading "/int/.desktop.settings" -1009207 [D][BadBleWorker] line:ENTER -1009209 [I][BadBleWorker] Special key pressed 28 - -1009246 [D][BtGap] RSSI: -79 -1009248 [I][BadBleWorker] BLE Key timeout : 33 -1009252 [D][BadBleWorker] line:GUI SPACE -1009254 [I][BadBleWorker] Special key pressed 82c - -1009291 [D][BtGap] RSSI: -75 -1009293 [I][BadBleWorker] BLE Key timeout : 33 -1009297 [D][BadBleWorker] line:DELAY 500 -1009299 [D][BtGap] RSSI: -75 -1009302 [I][BadBleWorker] BLE Key timeout : 33 -1009305 [I][SavedStruct] Loading "/int/.desktop.settings" -1009327 [I][SavedStruct] Loading "/int/.desktop.settings" -1009369 [I][SavedStruct] Loading "/int/.desktop.settings" -1009527 [I][SavedStruct] Loading "/int/.desktop.settings" -1009702 [I][SavedStruct] Loading "/int/.desktop.settings" -1009727 [I][SavedStruct] Loading "/int/.desktop.settings" -1009801 [I][SavedStruct] Loading "/int/.desktop.settings" -1009813 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -1009927 [I][SavedStruct] Loading "/int/.desktop.settings" -1010035 [I][SavedStruct] Loading "/int/.desktop.settings" -1010127 [I][SavedStruct] Loading "/int/.desktop.settings" -1010301 [I][SavedStruct] Loading "/int/.desktop.settings" -1010327 [I][SavedStruct] Loading "/int/.desktop.settings" -1010368 [I][SavedStruct] Loading "/int/.desktop.settings" -1010527 [I][SavedStruct] Loading "/int/.desktop.settings" -1010701 [I][SavedStruct] Loading "/int/.desktop.settings" -1010727 [I][SavedStruct] Loading "/int/.desktop.settings" -1010801 [I][SavedStruct] Loading "/int/.desktop.settings" -1010927 [I][SavedStruct] Loading "/int/.desktop.settings" -1011034 [I][SavedStruct] Loading "/int/.desktop.settings" -1011127 [I][SavedStruct] Loading "/int/.desktop.settings" -1011254 [D][BtGap] RSSI: -85 -1011256 [I][BadBleWorker] BLE Key timeout : 33 -1011260 [D][BadBleWorker] line:DELAY 200 -1011262 [D][BtGap] RSSI: -85 -1011264 [I][BadBleWorker] BLE Key timeout : 33 -1011301 [I][SavedStruct] Loading "/int/.desktop.settings" -1011327 [I][SavedStruct] Loading "/int/.desktop.settings" -1011367 [I][SavedStruct] Loading "/int/.desktop.settings" -1011466 [D][BadBleWorker] line:ENTER -1011468 [I][BadBleWorker] Special key pressed 28 - -1011505 [D][BtGap] RSSI: -85 -1011507 [I][BadBleWorker] BLE Key timeout : 33 -1011511 [D][BadBleWorker] line:GUI SPACE -1011513 [I][BadBleWorker] Special key pressed 82c - -1011527 [I][SavedStruct] Loading "/int/.desktop.settings" -1011549 [D][BtGap] RSSI: -85 -1011551 [I][BadBleWorker] BLE Key timeout : 33 -1011555 [D][BadBleWorker] line:DELAY 500 -1011558 [D][BtGap] RSSI: -85 -1011560 [I][BadBleWorker] BLE Key timeout : 33 -1011700 [I][SavedStruct] Loading "/int/.desktop.settings" -1011727 [I][SavedStruct] Loading "/int/.desktop.settings" -1011801 [I][SavedStruct] Loading "/int/.desktop.settings" -1011927 [I][SavedStruct] Loading "/int/.desktop.settings" -1012033 [I][SavedStruct] Loading "/int/.desktop.settings" -1012062 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -1012127 [I][SavedStruct] Loading "/int/.desktop.settings" -1012301 [I][SavedStruct] Loading "/int/.desktop.settings" -1012327 [I][SavedStruct] Loading "/int/.desktop.settings" -1012366 [I][SavedStruct] Loading "/int/.desktop.settings" -1012527 [I][SavedStruct] Loading "/int/.desktop.settings" -1012699 [I][SavedStruct] Loading "/int/.desktop.settings" -1012727 [I][SavedStruct] Loading "/int/.desktop.settings" -1012801 [I][SavedStruct] Loading "/int/.desktop.settings" -1012927 [I][SavedStruct] Loading "/int/.desktop.settings" -1013032 [I][SavedStruct] Loading "/int/.desktop.settings" -1013127 [I][SavedStruct] Loading "/int/.desktop.settings" -1013301 [I][SavedStruct] Loading "/int/.desktop.settings" -1013327 [I][SavedStruct] Loading "/int/.desktop.settings" -1013365 [I][SavedStruct] Loading "/int/.desktop.settings" -1013505 [D][BtGap] RSSI: -91 -1013507 [I][BadBleWorker] BLE Key timeout : 16 -1013511 [D][BadBleWorker] line:DELAY 200 -1013513 [D][BtGap] RSSI: -91 -1013515 [I][BadBleWorker] BLE Key timeout : 16 -1013527 [I][SavedStruct] Loading "/int/.desktop.settings" -1013698 [I][SavedStruct] Loading "/int/.desktop.settings" -1013717 [D][BadBleWorker] line:ENTER -1013719 [I][BadBleWorker] Special key pressed 28 - -1013727 [I][SavedStruct] Loading "/int/.desktop.settings" -1013741 [D][BtGap] RSSI: -99 -1013743 [I][BadBleWorker] BLE Key timeout : 16 -1013749 [D][BadBleWorker] line:GUI SPACE -1013751 [I][BadBleWorker] Special key pressed 82c - -1013770 [D][BtGap] RSSI: -99 -1013772 [I][BadBleWorker] BLE Key timeout : 16 -1013776 [D][BadBleWorker] line:DELAY 500 -1013778 [D][BtGap] RSSI: -99 -1013780 [I][BadBleWorker] BLE Key timeout : 16 -1013801 [I][SavedStruct] Loading "/int/.desktop.settings" -1013927 [I][SavedStruct] Loading "/int/.desktop.settings" -1014031 [I][SavedStruct] Loading "/int/.desktop.settings" -1014134 [I][SavedStruct] Loading "/int/.desktop.settings" -1014282 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -1014301 [I][SavedStruct] Loading "/int/.desktop.settings" -1014327 [I][SavedStruct] Loading "/int/.desktop.settings" -1014364 [I][SavedStruct] Loading "/int/.desktop.settings" -1014527 [I][SavedStruct] Loading "/int/.desktop.settings" -1014593 [E][BtHid] Failed updating report characteristic: 100 -1014596 [E][BtHid] Failed updating report characteristic: 100 -1014616 [E][BtHid] Failed updating report characteristic: 100 -1014619 [E][BtHid] Failed updating report characteristic: 100 -1014640 [E][BtHid] Failed updating report characteristic: 100 -1014643 [E][BtHid] Failed updating report characteristic: 100 -1014664 [E][BtHid] Failed updating report characteristic: 100 -1014667 [E][BtHid] Failed updating report characteristic: 100 -1014688 [E][BtHid] Failed updating report characteristic: 100 -1014691 [E][BtHid] Failed updating report characteristic: 100 -1014697 [I][SavedStruct] Loading "/int/.desktop.settings" -1014712 [E][BtHid] Failed updating report characteristic: 100 -1014717 [E][BtHid] Failed updating report characteristic: 100 -1014727 [I][SavedStruct] Loading "/int/.desktop.settings" -1014739 [E][BtHid] Failed updating report characteristic: 100 -1014744 [E][BtHid] Failed updating report characteristic: 100 -1014765 [E][BtHid] Failed updating report characteristic: 100 -1014768 [E][BtHid] Failed updating report characteristic: 100 -1014789 [E][BtHid] Failed updating report characteristic: 100 -1014792 [E][BtHid] Failed updating report characteristic: 100 -1014801 [I][SavedStruct] Loading "/int/.desktop.settings" -1014814 [E][BtHid] Failed updating report characteristic: 100 -1014819 [E][BtHid] Failed updating report characteristic: 100 -1014840 [E][BtHid] Failed updating report characteristic: 100 -1014843 [E][BtHid] Failed updating report characteristic: 100 -1014862 [E][BtHid] Failed updating report characteristic: 100 -1014865 [E][BtHid] Failed updating report characteristic: 100 -1014885 [E][BtHid] Failed updating report characteristic: 100 -1014888 [E][BtHid] Failed updating report characteristic: 100 -1014907 [E][BtHid] Failed updating report characteristic: 100 -1014910 [E][BtHid] Failed updating report characteristic: 100 -1014927 [I][SavedStruct] Loading "/int/.desktop.settings" -1014931 [E][BtHid] Failed updating report characteristic: 100 -1014936 [E][BtHid] Failed updating report characteristic: 100 -1014957 [E][BtHid] Failed updating report characteristic: 100 -1014960 [E][BtHid] Failed updating report characteristic: 100 -1014981 [E][BtHid] Failed updating report characteristic: 100 -1014984 [E][BtHid] Failed updating report characteristic: 100 -1015004 [E][BtHid] Failed updating report characteristic: 100 -1015007 [E][BtHid] Failed updating report characteristic: 100 -1015027 [E][BtHid] Failed updating report characteristic: 100 -1015031 [E][BtHid] Failed updating report characteristic: 100 -1015033 [I][SavedStruct] Loading "/int/.desktop.settings" -1015051 [E][BtHid] Failed updating report characteristic: 100 -1015055 [E][BtHid] Failed updating report characteristic: 100 -1015075 [E][BtHid] Failed updating report characteristic: 100 -1015078 [E][BtHid] Failed updating report characteristic: 100 -1015099 [E][BtHid] Failed updating report characteristic: 100 -1015102 [E][BtHid] Failed updating report characteristic: 100 -1015121 [E][BtHid] Failed updating report characteristic: 100 -1015124 [E][BtHid] Failed updating report characteristic: 100 -1015129 [I][SavedStruct] Loading "/int/.desktop.settings" -1015146 [E][BtHid] Failed updating report characteristic: 100 -1015153 [E][BtHid] Failed updating report characteristic: 100 -1015174 [E][BtHid] Failed updating report characteristic: 100 -1015177 [D][BtGap] RSSI: -99 -1015179 [I][BadBleWorker] BLE Key timeout : 16 -1015181 [D][BadBleWorker] line:DELAY 200 -1015183 [D][BtGap] RSSI: -99 -1015184 [I][BadBleWorker] BLE Key timeout : 16 -1015301 [I][SavedStruct] Loading "/int/.desktop.settings" -1015327 [I][SavedStruct] Loading "/int/.desktop.settings" -1015363 [I][SavedStruct] Loading "/int/.desktop.settings" -1015386 [D][BadBleWorker] line:ENTER -1015388 [I][BadBleWorker] Special key pressed 28 - -1015391 [E][BtHid] Failed updating report characteristic: 100 -1015410 [D][BtGap] RSSI: -89 -1015412 [I][BadBleWorker] BLE Key timeout : 16 -1015416 [D][BtGap] RSSI: -89 -1015418 [I][BadBleWorker] BLE Key timeout : 16 -1015527 [I][SavedStruct] Loading "/int/.desktop.settings" -1015696 [I][SavedStruct] Loading "/int/.desktop.settings" -1015727 [I][SavedStruct] Loading "/int/.desktop.settings" -1015801 [I][SavedStruct] Loading "/int/.desktop.settings" -1015927 [I][SavedStruct] Loading "/int/.desktop.settings" -1016029 [I][SavedStruct] Loading "/int/.desktop.settings" -1016127 [I][SavedStruct] Loading "/int/.desktop.settings" -1016301 [I][SavedStruct] Loading "/int/.desktop.settings" -1016327 [I][SavedStruct] Loading "/int/.desktop.settings" -1016362 [I][SavedStruct] Loading "/int/.desktop.settings" -1016527 [I][SavedStruct] Loading "/int/.desktop.settings" -1016695 [I][SavedStruct] Loading "/int/.desktop.settings" -1016727 [I][SavedStruct] Loading "/int/.desktop.settings" -1016801 [I][SavedStruct] Loading "/int/.desktop.settings" -1016927 [I][SavedStruct] Loading "/int/.desktop.settings" -1017028 [I][SavedStruct] Loading "/int/.desktop.settings" -1017127 [I][SavedStruct] Loading "/int/.desktop.settings" -1017301 [I][SavedStruct] Loading "/int/.desktop.settings" -1017327 [I][SavedStruct] Loading "/int/.desktop.settings" -1017361 [I][SavedStruct] Loading "/int/.desktop.settings" -1017527 [I][SavedStruct] Loading "/int/.desktop.settings" -1017694 [I][SavedStruct] Loading "/int/.desktop.settings" -1017727 [I][SavedStruct] Loading "/int/.desktop.settings" -1017801 [I][SavedStruct] Loading "/int/.desktop.settings" -1017927 [I][SavedStruct] Loading "/int/.desktop.settings" -1018027 [I][SavedStruct] Loading "/int/.desktop.settings" -1018127 [I][SavedStruct] Loading "/int/.desktop.settings" -1018301 [I][SavedStruct] Loading "/int/.desktop.settings" -1018327 [I][SavedStruct] Loading "/int/.desktop.settings" -1018360 [I][SavedStruct] Loading "/int/.desktop.settings" -1018527 [I][SavedStruct] Loading "/int/.desktop.settings" -1018693 [I][SavedStruct] Loading "/int/.desktop.settings" -1018727 [I][SavedStruct] Loading "/int/.desktop.settings" -1018801 [I][SavedStruct] Loading "/int/.desktop.settings" -1018927 [I][SavedStruct] Loading "/int/.desktop.settings" -1019026 [I][SavedStruct] Loading "/int/.desktop.settings" -1019127 [I][SavedStruct] Loading "/int/.desktop.settings" -1019301 [I][SavedStruct] Loading "/int/.desktop.settings" -1019327 [I][SavedStruct] Loading "/int/.desktop.settings" -1019359 [I][SavedStruct] Loading "/int/.desktop.settings" -1019527 [I][SavedStruct] Loading "/int/.desktop.settings" -1019692 [I][SavedStruct] Loading "/int/.desktop.settings" -1019727 [I][SavedStruct] Loading "/int/.desktop.settings" -1019801 [I][SavedStruct] Loading "/int/.desktop.settings" -1019927 [I][SavedStruct] Loading "/int/.desktop.settings" -1020025 [I][SavedStruct] Loading "/int/.desktop.settings" -1020127 [I][SavedStruct] Loading "/int/.desktop.settings" -1020301 [I][SavedStruct] Loading "/int/.desktop.settings" -1020327 [I][SavedStruct] Loading "/int/.desktop.settings" -1020358 [I][SavedStruct] Loading "/int/.desktop.settings" -1020527 [I][SavedStruct] Loading "/int/.desktop.settings" -1020691 [I][SavedStruct] Loading "/int/.desktop.settings" -1020727 [I][SavedStruct] Loading "/int/.desktop.settings" -1020801 [I][SavedStruct] Loading "/int/.desktop.settings" -1020927 [I][SavedStruct] Loading "/int/.desktop.settings" -1021024 [I][SavedStruct] Loading "/int/.desktop.settings" -1021127 [I][SavedStruct] Loading "/int/.desktop.settings" -1021301 [I][SavedStruct] Loading "/int/.desktop.settings" -1021327 [I][SavedStruct] Loading "/int/.desktop.settings" -1021357 [I][SavedStruct] Loading "/int/.desktop.settings" -1021527 [I][SavedStruct] Loading "/int/.desktop.settings" -1021690 [I][SavedStruct] Loading "/int/.desktop.settings" -1021727 [I][SavedStruct] Loading "/int/.desktop.settings" -1021801 [I][SavedStruct] Loading "/int/.desktop.settings" -1021927 [I][SavedStruct] Loading "/int/.desktop.settings" -1022023 [I][SavedStruct] Loading "/int/.desktop.settings" -1022127 [I][SavedStruct] Loading "/int/.desktop.settings" -1022301 [I][SavedStruct] Loading "/int/.desktop.settings" -1022327 [I][SavedStruct] Loading "/int/.desktop.settings" -1022356 [I][SavedStruct] Loading "/int/.desktop.settings" - ->: - _.-------.._ -, - .-"```"--..,,_/ /`-, -, \ - .:" /:/ /'\ \ ,_..., `. | | - / ,----/:/ /`\ _\~`_-"` _; - ' / /`"""'\ \ \.~`_-' ,-"'/ - | | | 0 | | .-' ,/` / - | ,..\ \ ,.-"` ,/` / - ; : `/`""\` ,/--==,/-----, - | `-...| -.___-Z:_______J...---; - : ` _-' - _L_ _ ___ ___ ___ ___ ____--"`___ _ ___ -| __|| | |_ _|| _ \| _ \| __|| _ \ / __|| | |_ _| -| _| | |__ | | | _/| _/| _| | / | (__ | |__ | | -|_| |____||___||_| |_| |___||_|_\ \___||____||___| - -Welcome to Flipper Zero Command Line Interface! -Read Manual https://docs.flipperzero.one - -Firmware version: dev 0.74.3 (XFW-0040 built on 26-01-2023) - ->: log -Press CTRL+C to stop... -27492 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: -1 -27494 [D][BrowserWorker] Load offset: 0 cnt: 50 -28939 [D][BrowserWorker] End -28952 [I][BtGap] Stop advertising -28956 [D][BtGap] set_non_discoverable success -28970 [I][SavedStruct] Loading "/int/.desktop.settings" -29075 [I][SavedStruct] Loading "/int/.desktop.settings" -29162 [I][FuriHalBt] Disconnect and stop advertising -29164 [I][FuriHalBt] Stop current profile services -29173 [I][FuriHalBt] Stop BLE related RTOS threads -29197 [I][FuriHalBt] Reset SHCI -29275 [I][SavedStruct] Loading "/int/.desktop.settings" -29311 [I][FuriHalBt] Start BT initialization -29316 [I][Core2] Core2 started -29318 [I][Core2] C2 boot completed, mode: Stack -29321 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -29323 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -29327 [I][Core2] Radio stack started -29330 [I][Core2] Flash activity control switched to SEM7 -29332 [I][BtGap] Advertising name: Keyboard -29334 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -29350 [D][BtBatterySvc] Updating power state characteristic -29356 [I][BtGap] Start advertising -29358 [I][SavedStruct] Loading "/int/.bt.settings" -29376 [I][SavedStruct] Loading "/ext/apps/Tools/.bt_hid.keys" -29385 [I][FuriHalBt] Disconnect and stop advertising -29387 [I][BtGap] Stop advertising -29390 [D][BtGap] set_non_discoverable success -29392 [I][FuriHalBt] Stop current profile services -29402 [I][FuriHalBt] Stop BLE related RTOS threads -29427 [I][FuriHalBt] Reset SHCI -29466 [I][SavedStruct] Loading "/int/.desktop.settings" -29482 [I][SavedStruct] Loading "/int/.desktop.settings" -29541 [I][FuriHalBt] Start BT initialization -29546 [I][Core2] Core2 started -29548 [I][Core2] C2 boot completed, mode: Stack -29551 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -29553 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -29557 [I][Core2] Radio stack started -29560 [I][Core2] Flash activity control switched to SEM7 -29562 [I][BtGap] Advertising name: Rumik1 -29565 [I][BtGap] MAC @ : 6C:7A:D9:AC:57:72 -29582 [D][BtBatterySvc] Updating power state characteristic -29591 [I][BtSrv] Bt App started -29593 [I][BtGap] Start advertising -29596 [I][BadBleWorker] Init -29598 [I][SavedStruct] Loading "/int/.desktop.settings" -29625 [I][BadBleWorker] BLE Key timeout : 16 -29675 [I][SavedStruct] Loading "/int/.desktop.settings" -29875 [I][SavedStruct] Loading "/int/.desktop.settings" -29966 [I][SavedStruct] Loading "/int/.desktop.settings" -30075 [I][SavedStruct] Loading "/int/.desktop.settings" -30275 [I][SavedStruct] Loading "/int/.desktop.settings" -30466 [I][SavedStruct] Loading "/int/.desktop.settings" -30486 [I][SavedStruct] Loading "/int/.desktop.settings" -30675 [I][SavedStruct] Loading "/int/.desktop.settings" -30726 [I][BtGap] Connection parameters: Connection Interval: 24 (30 ms), Slave Latency: 0, Supervision Timeout: 72 -30839 [D][BtGap] Slave security initiated -30875 [I][SavedStruct] Loading "/int/.desktop.settings" -30966 [I][SavedStruct] Loading "/int/.desktop.settings" -31018 [I][BtGap] Pairing complete -31021 [D][BtGap] RSSI: -47 -31023 [D][BtBatterySvc] Updating battery level characteristic -31026 [D][BtGap] RSSI: -47 -31028 [I][BadBleWorker] BLE Key timeout : 33 -31075 [I][SavedStruct] Loading "/int/.desktop.settings" -31136 [I][BtGap] Rx MTU size: 414 -31275 [I][SavedStruct] Loading "/int/.desktop.settings" -31466 [I][SavedStruct] Loading "/int/.desktop.settings" -31484 [I][SavedStruct] Loading "/int/.desktop.settings" -31675 [I][SavedStruct] Loading "/int/.desktop.settings" -31702 [I][BtGap] Connection parameters event complete -31704 [I][BtGap] Connection parameters: Connection Interval: 12 (15 ms), Slave Latency: 4, Supervision Timeout: 100 -31706 [W][BtGap] Unsupported connection interval. Request connection parameters update -31739 [D][BtGap] Connection parameters accepted -31875 [I][SavedStruct] Loading "/int/.desktop.settings" -31902 [I][BtGap] Connection parameters event complete -31906 [I][BtGap] Connection parameters: Connection Interval: 36 (45 ms), Slave Latency: 4, Supervision Timeout: 100 -31968 [I][SavedStruct] Loading "/int/.desktop.settings" -32075 [I][SavedStruct] Loading "/int/.desktop.settings" -32275 [I][SavedStruct] Loading "/int/.desktop.settings" -32466 [I][SavedStruct] Loading "/int/.desktop.settings" -32484 [I][SavedStruct] Loading "/int/.desktop.settings" -32675 [I][SavedStruct] Loading "/int/.desktop.settings" -32875 [I][SavedStruct] Loading "/int/.desktop.settings" -32966 [I][SavedStruct] Loading "/int/.desktop.settings" -33075 [I][SavedStruct] Loading "/int/.desktop.settings" -33275 [I][SavedStruct] Loading "/int/.desktop.settings" -33466 [I][SavedStruct] Loading "/int/.desktop.settings" -33484 [I][SavedStruct] Loading "/int/.desktop.settings" -33675 [I][SavedStruct] Loading "/int/.desktop.settings" -33875 [I][SavedStruct] Loading "/int/.desktop.settings" -33966 [I][SavedStruct] Loading "/int/.desktop.settings" -34075 [I][SavedStruct] Loading "/int/.desktop.settings" -34275 [I][SavedStruct] Loading "/int/.desktop.settings" -34466 [I][SavedStruct] Loading "/int/.desktop.settings" -34484 [I][SavedStruct] Loading "/int/.desktop.settings" -34675 [I][SavedStruct] Loading "/int/.desktop.settings" -34875 [I][SavedStruct] Loading "/int/.desktop.settings" -34966 [I][SavedStruct] Loading "/int/.desktop.settings" -35075 [I][SavedStruct] Loading "/int/.desktop.settings" -35275 [I][SavedStruct] Loading "/int/.desktop.settings" -35359 [D][DolphinState] icounter 1325, butthurt 0 -35362 [D][BtGap] RSSI: -53 -35363 [I][BadBleWorker] BLE Key timeout : 33 -35367 [D][BadBleWorker] line:REM Troll scrip to open vx-underground on an iphone -35370 [D][BtGap] RSSI: -53 -35371 [I][BadBleWorker] BLE Key timeout : 33 -35373 [D][BadBleWorker] line:DELAY 1000 -35375 [D][BtGap] RSSI: -53 -35376 [I][BadBleWorker] BLE Key timeout : 33 -35475 [I][SavedStruct] Loading "/int/.desktop.settings" -35675 [I][SavedStruct] Loading "/int/.desktop.settings" -35860 [I][SavedStruct] Loading "/int/.desktop.settings" -35878 [I][SavedStruct] Loading "/int/.desktop.settings" -36075 [I][SavedStruct] Loading "/int/.desktop.settings" -36275 [I][SavedStruct] Loading "/int/.desktop.settings" -36360 [I][SavedStruct] Loading "/int/.desktop.settings" -36378 [D][BadBleWorker] line:GUI SPACE -36380 [I][BadBleWorker] Special key pressed 82c - -36418 [D][BtGap] RSSI: -57 -36420 [I][BadBleWorker] BLE Key timeout : 33 -36423 [D][BadBleWorker] line:DELAY 500 -36426 [D][BtGap] RSSI: -57 -36428 [I][BadBleWorker] BLE Key timeout : 33 -36475 [I][SavedStruct] Loading "/int/.desktop.settings" -36675 [I][SavedStruct] Loading "/int/.desktop.settings" -36860 [I][SavedStruct] Loading "/int/.desktop.settings" -36878 [I][SavedStruct] Loading "/int/.desktop.settings" -36930 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -37075 [I][SavedStruct] Loading "/int/.desktop.settings" -37275 [I][SavedStruct] Loading "/int/.desktop.settings" -37360 [I][SavedStruct] Loading "/int/.desktop.settings" -37475 [I][SavedStruct] Loading "/int/.desktop.settings" -37675 [I][SavedStruct] Loading "/int/.desktop.settings" -37860 [I][SavedStruct] Loading "/int/.desktop.settings" -37878 [I][SavedStruct] Loading "/int/.desktop.settings" -38075 [I][SavedStruct] Loading "/int/.desktop.settings" -38275 [I][SavedStruct] Loading "/int/.desktop.settings" -38360 [I][SavedStruct] Loading "/int/.desktop.settings" -38370 [D][BtGap] RSSI: -71 -38372 [I][BadBleWorker] BLE Key timeout : 37 -38376 [D][BadBleWorker] line:DELAY 200 -38380 [D][BtGap] RSSI: -74 -38382 [I][BadBleWorker] BLE Key timeout : 37 -38475 [I][SavedStruct] Loading "/int/.desktop.settings" -38586 [D][BadBleWorker] line:ENTER -38588 [I][BadBleWorker] Special key pressed 28 - -38629 [D][BtGap] RSSI: -88 -38631 [I][BadBleWorker] BLE Key timeout : 41 -38633 [D][BadBleWorker] line:GUI SPACE -38635 [I][BadBleWorker] Special key pressed 82c - -38675 [I][SavedStruct] Loading "/int/.desktop.settings" -38682 [D][BtGap] RSSI: -71 -38684 [I][BadBleWorker] BLE Key timeout : 37 -38688 [D][BadBleWorker] line:DELAY 500 -38692 [D][BtGap] RSSI: -71 -38694 [I][BadBleWorker] BLE Key timeout : 37 -38860 [I][SavedStruct] Loading "/int/.desktop.settings" -38878 [I][SavedStruct] Loading "/int/.desktop.settings" -39075 [I][SavedStruct] Loading "/int/.desktop.settings" -39198 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -39275 [I][SavedStruct] Loading "/int/.desktop.settings" -39360 [I][SavedStruct] Loading "/int/.desktop.settings" -39475 [I][SavedStruct] Loading "/int/.desktop.settings" -39675 [I][SavedStruct] Loading "/int/.desktop.settings" -39860 [I][SavedStruct] Loading "/int/.desktop.settings" -39878 [I][SavedStruct] Loading "/int/.desktop.settings" -40086 [I][SavedStruct] Loading "/int/.desktop.settings" -40275 [I][SavedStruct] Loading "/int/.desktop.settings" -40360 [I][SavedStruct] Loading "/int/.desktop.settings" -40475 [I][SavedStruct] Loading "/int/.desktop.settings" -40675 [I][SavedStruct] Loading "/int/.desktop.settings" -40803 [D][BtGap] RSSI: -98 -40805 [I][BadBleWorker] BLE Key timeout : 41 -40809 [D][BadBleWorker] line:DELAY 200 -40812 [D][BtGap] RSSI: -98 -40814 [I][BadBleWorker] BLE Key timeout : 41 -40860 [I][SavedStruct] Loading "/int/.desktop.settings" -40878 [I][SavedStruct] Loading "/int/.desktop.settings" -41017 [D][BadBleWorker] line:ENTER -41019 [I][BadBleWorker] Special key pressed 28 - -41063 [D][BtGap] RSSI: -89 -41065 [I][BadBleWorker] BLE Key timeout : 41 -41068 [D][BadBleWorker] line:GUI SPACE -41070 [I][BadBleWorker] Special key pressed 82c - -41075 [I][SavedStruct] Loading "/int/.desktop.settings" -41115 [D][BtGap] RSSI: -91 -41117 [I][BadBleWorker] BLE Key timeout : 41 -41119 [D][BadBleWorker] line:DELAY 500 -41121 [D][BtGap] RSSI: -91 -41122 [I][BadBleWorker] BLE Key timeout : 41 -41275 [I][SavedStruct] Loading "/int/.desktop.settings" -41360 [I][SavedStruct] Loading "/int/.desktop.settings" -41475 [I][SavedStruct] Loading "/int/.desktop.settings" -41624 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -41675 [I][SavedStruct] Loading "/int/.desktop.settings" -41860 [I][SavedStruct] Loading "/int/.desktop.settings" -41878 [I][SavedStruct] Loading "/int/.desktop.settings" -42075 [I][SavedStruct] Loading "/int/.desktop.settings" -42275 [I][SavedStruct] Loading "/int/.desktop.settings" -42360 [I][SavedStruct] Loading "/int/.desktop.settings" -42475 [I][SavedStruct] Loading "/int/.desktop.settings" -42675 [I][SavedStruct] Loading "/int/.desktop.settings" -42860 [I][SavedStruct] Loading "/int/.desktop.settings" -42878 [I][SavedStruct] Loading "/int/.desktop.settings" -43075 [I][SavedStruct] Loading "/int/.desktop.settings" -43275 [I][SavedStruct] Loading "/int/.desktop.settings" -43360 [I][SavedStruct] Loading "/int/.desktop.settings" -43389 [D][BtGap] RSSI: -85 -43391 [I][BadBleWorker] BLE Key timeout : 41 -43394 [D][BadBleWorker] line:DELAY 200 -43397 [D][BtGap] RSSI: -85 -43399 [I][BadBleWorker] BLE Key timeout : 41 -43475 [I][SavedStruct] Loading "/int/.desktop.settings" -43601 [D][BadBleWorker] line:ENTER -43603 [I][BadBleWorker] Special key pressed 28 - -43647 [D][BtGap] RSSI: -86 -43649 [I][BadBleWorker] BLE Key timeout : 41 -43652 [D][BadBleWorker] line:GUI SPACE -43654 [I][BadBleWorker] Special key pressed 82c - -43675 [I][SavedStruct] Loading "/int/.desktop.settings" -43699 [D][BtGap] RSSI: -91 -43701 [I][BadBleWorker] BLE Key timeout : 41 -43704 [D][BadBleWorker] line:DELAY 500 -43706 [D][BtGap] RSSI: -91 -43708 [I][BadBleWorker] BLE Key timeout : 41 -43860 [I][SavedStruct] Loading "/int/.desktop.settings" -43878 [I][SavedStruct] Loading "/int/.desktop.settings" -44075 [I][SavedStruct] Loading "/int/.desktop.settings" -44211 [D][BadBleWorker] line:STRING https://www.vx=underground.org/index.html -44275 [I][SavedStruct] Loading "/int/.desktop.settings" -44360 [I][SavedStruct] Loading "/int/.desktop.settings" -44475 [I][SavedStruct] Loading "/int/.desktop.settings" -44675 [I][SavedStruct] Loading "/int/.desktop.settings" -44860 [I][SavedStruct] Loading "/int/.desktop.settings" -44878 [I][SavedStruct] Loading "/int/.desktop.settings" -45075 [I][SavedStruct] Loading "/int/.desktop.settings" -45275 [I][SavedStruct] Loading "/int/.desktop.settings" -45360 [I][SavedStruct] Loading "/int/.desktop.settings" -45475 [I][SavedStruct] Loading "/int/.desktop.settings" -45675 [I][SavedStruct] Loading "/int/.desktop.settings" -45860 [I][SavedStruct] Loading "/int/.desktop.settings" -45878 [I][SavedStruct] Loading "/int/.desktop.settings" -45978 [D][BtGap] RSSI: -89 -45980 [I][BadBleWorker] BLE Key timeout : 41 -45983 [D][BadBleWorker] line:DELAY 200 -45986 [D][BtGap] RSSI: -86 -45987 [I][BadBleWorker] BLE Key timeout : 41 -46075 [I][SavedStruct] Loading "/int/.desktop.settings" -46190 [D][BadBleWorker] line:ENTER -46192 [I][BadBleWorker] Special key pressed 28 - -46237 [D][BtGap] RSSI: -97 -46239 [I][BadBleWorker] BLE Key timeout : 41 -46242 [D][BtGap] RSSI: -97 -46244 [I][BadBleWorker] BLE Key timeout : 41 -46275 [I][SavedStruct] Loading "/int/.desktop.settings" -46360 [I][SavedStruct] Loading "/int/.desktop.settings" -46475 [I][SavedStruct] Loading "/int/.desktop.settings" -46675 [I][SavedStruct] Loading "/int/.desktop.settings" -46860 [I][SavedStruct] Loading "/int/.desktop.settings" -46879 [I][SavedStruct] Loading "/int/.desktop.settings" -47075 [I][SavedStruct] Loading "/int/.desktop.settings" -47275 [I][SavedStruct] Loading "/int/.desktop.settings" -47360 [I][SavedStruct] Loading "/int/.desktop.settings" -47475 [I][SavedStruct] Loading "/int/.desktop.settings" -47675 [I][SavedStruct] Loading "/int/.desktop.settings" -47860 [I][SavedStruct] Loading "/int/.desktop.settings" -47879 [I][SavedStruct] Loading "/int/.desktop.settings" -48075 [I][SavedStruct] Loading "/int/.desktop.settings" -48275 [I][SavedStruct] Loading "/int/.desktop.settings" -48360 [I][SavedStruct] Loading "/int/.desktop.settings" -48475 [I][SavedStruct] Loading "/int/.desktop.settings" -48675 [I][SavedStruct] Loading "/int/.desktop.settings" -48860 [I][SavedStruct] Loading "/int/.desktop.settings" -48879 [I][SavedStruct] Loading "/int/.desktop.settings" -49075 [I][SavedStruct] Loading "/int/.desktop.settings" -49275 [I][SavedStruct] Loading "/int/.desktop.settings" -49360 [I][SavedStruct] Loading "/int/.desktop.settings" -49475 [I][SavedStruct] Loading "/int/.desktop.settings" -49675 [I][SavedStruct] Loading "/int/.desktop.settings" -49860 [I][SavedStruct] Loading "/int/.desktop.settings" -49879 [I][SavedStruct] Loading "/int/.desktop.settings" -50075 [I][SavedStruct] Loading "/int/.desktop.settings" -50275 [I][SavedStruct] Loading "/int/.desktop.settings" -50360 [I][SavedStruct] Loading "/int/.desktop.settings" -50475 [I][SavedStruct] Loading "/int/.desktop.settings" -50675 [I][SavedStruct] Loading "/int/.desktop.settings" -50860 [I][SavedStruct] Loading "/int/.desktop.settings" -50879 [I][SavedStruct] Loading "/int/.desktop.settings" -51075 [I][SavedStruct] Loading "/int/.desktop.settings" -51275 [I][SavedStruct] Loading "/int/.desktop.settings" -51360 [I][SavedStruct] Loading "/int/.desktop.settings" -51475 [I][SavedStruct] Loading "/int/.desktop.settings" -51675 [I][SavedStruct] Loading "/int/.desktop.settings" -51860 [I][SavedStruct] Loading "/int/.desktop.settings" -51879 [I][SavedStruct] Loading "/int/.desktop.settings" -52075 [I][SavedStruct] Loading "/int/.desktop.settings" -52275 [I][SavedStruct] Loading "/int/.desktop.settings" -52360 [I][SavedStruct] Loading "/int/.desktop.settings" -52475 [I][SavedStruct] Loading "/int/.desktop.settings" -52675 [I][SavedStruct] Loading "/int/.desktop.settings" -52860 [I][SavedStruct] Loading "/int/.desktop.settings" -52879 [I][SavedStruct] Loading "/int/.desktop.settings" -53075 [I][SavedStruct] Loading "/int/.desktop.settings" -53275 [I][SavedStruct] Loading "/int/.desktop.settings" -53360 [I][SavedStruct] Loading "/int/.desktop.settings" -53475 [I][SavedStruct] Loading "/int/.desktop.settings" -53675 [I][SavedStruct] Loading "/int/.desktop.settings" -53860 [I][SavedStruct] Loading "/int/.desktop.settings" -53879 [I][SavedStruct] Loading "/int/.desktop.settings" -54075 [I][SavedStruct] Loading "/int/.desktop.settings" -54101 [I][BtGap] Stop advertising -54104 [D][BtGap] terminate success -54107 [E][BtGap] set_non_discoverable failed 12 -54111 [I][SavedStruct] Loading "/int/.desktop.settings" -54275 [I][SavedStruct] Loading "/int/.desktop.settings" -54311 [I][SavedStruct] Loading "/int/.bt.settings" -54324 [I][SavedStruct] Loading "/int/.bt.keys" -54335 [I][FuriHalBt] Disconnect and stop advertising -54337 [I][FuriHalBt] Stop current profile services -54339 [I][BtGap] Disconnect from client. Reason: 16 -54349 [I][FuriHalBt] Stop BLE related RTOS threads -54373 [I][FuriHalBt] Reset SHCI -54475 [I][SavedStruct] Loading "/int/.desktop.settings" -54487 [I][FuriHalBt] Start BT initialization -54494 [I][Core2] Core2 started -54496 [I][Core2] C2 boot completed, mode: Stack -54500 [I][Core2] Core2: FUS: 1.2.0, mem 16/0, flash 6 pages -54504 [I][Core2] Core2: Stack: 1.13.3, branch 0, reltype 2, stacktype 3, flash 30 pages -54508 [I][Core2] Radio stack started -54511 [I][Core2] Flash activity control switched to SEM7 -54513 [I][BtGap] Advertising name: Keyboard -54516 [I][BtGap] MAC @ : 35:F2:47:26:E1:80 -54534 [D][BtBatterySvc] Updating power state characteristic -54540 [I][BtSrv] Bt App started -54542 [I][BtGap] Start advertising -54545 [I][BadBleWorker] End -54547 [I][SavedStruct] Loading "/int/.desktop.settings" -54568 [I][SavedStruct] Loading "/int/.desktop.settings" -54570 [D][BrowserWorker] Start -54598 [D][BrowserWorker] Enter folder: /any/BadUsb/fun items: 9 idx: 4 -54600 [D][BrowserWorker] Load offset: 0 cnt: 50 -54676 [D][BrowserWorker] Exit to: /any/BadUsb items: 10 idx: -1 -54678 [D][BrowserWorker] Load offset: 0 cnt: 50 -54888 [D][BrowserWorker] End -54890 [I][SavedStruct] Loading "/int/.desktop.settings" -54946 [I][fap_loader_app] FAP app returned: 0 -54982 [I][LoaderSrv] Application stopped. Free heap: 140272 -55011 [I][AnimationStorage] Custom Manifest selected -55299 [I][AnimationManager] Select 'HANDS' animation -55303 [I][AnimationManager] Load animation 'HANDS' -55331 [I][SavedStruct] Loading "/int/.desktop.settings" -65361 [I][Dolphin] Flush stats -65363 [I][SavedStruct] Saving "/int/.dolphin.state" -65373 [D][StorageInt] Device erase: page 2, translated page: cf -65468 [D][StorageInt] Device sync: skipping -65473 [I][DolphinState] State saved -72183 [I][AnimationStorage] Custom Manifest selected -73040 [I][AnimationManager] Select 'SKULL' animation -103081 [I][AnimationStorage] Custom Manifest selected -104016 [I][AnimationManager] Select 'LOGO_WD2' animation -114771 [D][BtGap] set_non_discoverable success -134057 [I][AnimationStorage] Custom Manifest selected -134395 [I][AnimationManager] Select 'MUMMY' animation -164438 [I][AnimationStorage] Custom Manifest selected -164953 [I][AnimationManager] Select 'DEDSEC_TALK' animation -174773 [D][BtGap] set_non_discoverable success -194994 [I][AnimationStorage] Custom Manifest selected -195897 [I][AnimationManager] Select 'REAPER_ALT' animation -225938 [I][AnimationStorage] Custom Manifest selected -226509 [I][AnimationManager] Select 'DEDSEC_OLD' animation -234775 [D][BtGap] set_non_discoverable success -256552 [I][AnimationStorage] Custom Manifest selected -256819 [I][AnimationManager] Select 'JOIN_US' animation -286859 [I][AnimationStorage] Custom Manifest selected -287374 [I][AnimationManager] Select 'DEDSEC_TALK' animation -294777 [D][BtGap] set_non_discoverable success -317415 [I][AnimationStorage] Custom Manifest selected -318273 [I][AnimationManager] Select 'SKULL' animation -348316 [I][AnimationStorage] Custom Manifest selected -349083 [I][AnimationManager] Select 'SPIRAL' animation -354779 [D][BtGap] set_non_discoverable success -379124 [I][AnimationStorage] Custom Manifest selected -379787 [I][AnimationManager] Select 'SKULL_SPIN' animation -409828 [I][AnimationStorage] Custom Manifest selected -410316 [I][AnimationManager] Select 'DEDSEC_AD' animation -414781 [D][BtGap] set_non_discoverable success -440358 [I][AnimationStorage] Custom Manifest selected -440698 [I][AnimationManager] Select 'MUMMY' animation -470740 [I][AnimationStorage] Custom Manifest selected -471138 [I][AnimationManager] Select 'HANDS' animation -474783 [D][BtGap] set_non_discoverable success -501180 [I][AnimationStorage] Custom Manifest selected -502243 [I][AnimationManager] Select 'DEDSEC_ANIM' animation -532285 [I][AnimationStorage] Custom Manifest selected -532547 [I][AnimationManager] Select 'BOTTY_CALL' animation -534785 [D][BtGap] set_non_discoverable success -562592 [I][AnimationStorage] Custom Manifest selected -563449 [I][AnimationManager] Select 'SKULL' animation -593489 [I][AnimationStorage] Custom Manifest selected -594414 [I][AnimationManager] Select 'LOGO_WD2' animation -594787 [D][BtGap] set_non_discoverable success -624465 [I][AnimationStorage] Custom Manifest selected -624658 [I][AnimationManager] Select 'thank_you_128x64' animation -654700 [I][AnimationStorage] Custom Manifest selected -655440 [I][AnimationManager] Select 'GUNS_CAR' animation -655471 [D][BtGap] set_non_discoverable success -685480 [I][AnimationStorage] Custom Manifest selected -686535 [I][AnimationManager] Select 'DEDSEC_ANIM' animation -715480 [D][BtGap] set_non_discoverable success -716577 [I][AnimationStorage] Custom Manifest selected -717479 [I][AnimationManager] Select 'REAPER_ALT' animation -747521 [I][AnimationStorage] Custom Manifest selected -748026 [I][AnimationManager] Select 'DEDSEC_TALK' animation -775484 [D][BtGap] set_non_discoverable success -778069 [I][AnimationStorage] Custom Manifest selected -779065 [I][AnimationManager] Select 'REAPER' animation -809108 [I][AnimationStorage] Custom Manifest selected -809625 [I][AnimationManager] Select 'DEDSEC_TALK' animation -835486 [D][BtGap] set_non_discoverable success -839669 [I][AnimationStorage] Custom Manifest selected -840573 [I][AnimationManager] Select 'REAPER_ALT' animation -870614 [I][AnimationStorage] Custom Manifest selected -871392 [I][AnimationManager] Select 'SPIRAL' animation -895488 [D][BtGap] set_non_discoverable success -901433 [I][AnimationStorage] Custom Manifest selected -902487 [I][AnimationManager] Select 'DEDSEC_ANIM' animation -932529 [I][AnimationStorage] Custom Manifest selected -932843 [I][AnimationManager] Select 'FINGER' animation From 1407551d9878b969a962700643b6073b09ac62a1 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 27 Jan 2023 12:58:24 +0100 Subject: [PATCH 033/231] Update mifare_classic.h --- lib/nfc/protocols/mifare_classic.h | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index f5b73c98b..3c57b202d 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -23,7 +23,6 @@ typedef enum { MfClassicTypeMini, MfClassicType1k, MfClassicType4k, - MfClassicTypeMini, } MfClassicType; typedef enum { From 255a4643a2d15ec7fc78f63041b834532a406be3 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 27 Jan 2023 13:14:46 +0100 Subject: [PATCH 034/231] Delete lint_python.yml --- .github/workflows/lint_python.yml | 32 ------------------------------- 1 file changed, 32 deletions(-) delete mode 100644 .github/workflows/lint_python.yml diff --git a/.github/workflows/lint_python.yml b/.github/workflows/lint_python.yml deleted file mode 100644 index 4b92e0e90..000000000 --- a/.github/workflows/lint_python.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'Python Lint' - -on: - push: - branches: - - dev - - "release*" - tags: - - '*' - pull_request: - -env: - SET_GH_OUTPUT: 1 - -jobs: - lint_python: - runs-on: ubuntu-latest - steps: - - name: 'Decontaminate previous build leftovers' - run: | - if [ -d .git ]; then - git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" - fi - - - name: 'Checkout code' - uses: actions/checkout@v3 - with: - fetch-depth: 0 - ref: ${{ github.event.pull_request.head.sha }} - - - name: 'Check code formatting' - run: ./fbt lint_py From 75a01459f578c0f99782d9cad98e62715ca5f463 Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 27 Jan 2023 19:37:49 +0100 Subject: [PATCH 035/231] some fix + api_symbols correct export --- firmware/targets/f7/api_symbols.csv | 30 ++++++++++++++--------------- firmware/targets/f7/ble_glue/gap.c | 5 ++++- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a5ce0b489..cc6c7d1c6 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -571,14 +571,14 @@ Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* -Function,?,bt_get_profile_adv_name,const char*,Bt* -Function,?,bt_get_profile_mac_address,const uint8_t*,Bt* +Function,+,bt_get_profile_adv_name,const char*,Bt* +Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" -Function,?,bt_remote_rssi,_Bool,"Bt*, BtRssi*" -Function,?,bt_set_profile,_Bool,"Bt*, BtProfile" -Function,?,bt_set_profile_adv_name,void,"Bt*, const char*, ..." -Function,?,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" +Function,+,bt_remote_rssi,_Bool,"Bt*, BtRssi*" +Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_profile_adv_name,void,"Bt*, const char*, ..." +Function,+,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* Function,+,buffered_file_stream_close,_Bool,Stream* @@ -608,7 +608,7 @@ Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, B Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* -Function,-,canvas_commit,void,Canvas* +Function,+,canvas_commit,void,Canvas* Function,+,canvas_current_font_height,uint8_t,Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" @@ -637,7 +637,7 @@ Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" Function,+,canvas_height,uint8_t,Canvas* Function,-,canvas_init,Canvas*, Function,+,canvas_invert_color,void,Canvas* -Function,-,canvas_reset,void,Canvas* +Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" Function,+,canvas_set_font,void,"Canvas*, Font" @@ -1000,17 +1000,17 @@ Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, voi Function,+,furi_hal_bt_clear_white_list,_Bool, Function,+,furi_hal_bt_dump_state,void,FuriString* Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode -Function,?,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t* +Function,+,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t* Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" -Function,?,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile -Function,?,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, -Function,?,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t +Function,+,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release_all,_Bool, @@ -1037,8 +1037,8 @@ Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" -Function,?,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" -Function,?,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" +Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" +Function,+furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" Function,+,furi_hal_bt_start_advertising,void, Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" @@ -1580,7 +1580,7 @@ Function,-,gamma,double,double Function,-,gamma_r,double,"double, int*" Function,-,gammaf,float,float Function,-,gammaf_r,float,"float, int*" -Function,?,gap_get_remote_conn_rssi,uint32_t,int8_t* +Function,+,gap_get_remote_conn_rssi,uint32_t,int8_t* Function,-,gap_get_state,GapState, Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" Function,-,gap_start_advertising,void, diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 725a69dfb..668509218 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -54,6 +54,9 @@ static const uint8_t gap_erk[16] = static Gap* gap = NULL; +/** function for updating rssi informations in global Gap object + * +*/ static inline void fetch_rssi() { uint8_t ret_rssi = 127; if(hci_read_rssi(gap->service.connection_handle, &ret_rssi) == BLE_STATUS_SUCCESS) { @@ -61,7 +64,7 @@ static inline void fetch_rssi() { gap->time_rssi_sample = furi_get_tick(); return; } - FURI_LOG_E(TAG, "Failed to read RSSI"); + FURI_LOG_D(TAG, "Failed to read RSSI"); } static void gap_advertise_start(GapState new_state); From f1101a07cc61dfe9f902e33f55bc7a109c782035 Mon Sep 17 00:00:00 2001 From: Clara K Date: Fri, 27 Jan 2023 19:52:20 +0100 Subject: [PATCH 036/231] Update ReadMe.md --- ReadMe.md | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 0a0d2adda..58123126d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,7 +1,7 @@

XFW - Xtreme Firmware for the Flipper Zero

- +

[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme#Known-bugs) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) @@ -13,7 +13,7 @@ This firmware is a complete overhaul of the [Official Firmware](https://github.c

What makes it special?

-I have spent many hours perfecting this code even further, and getting the most out of it. +We have spent many hours perfecting this code even further, and getting the most out of it. The goal of this Firmware is to regularly bring out amazing updates based on what the community wants, with an actual understanding of whats going on. Fixing bugs that are regularly talked about, removing unstable / broken applications (.FAP) and actually using the level system that just sits abandoned everywhere else.

@@ -28,6 +28,27 @@ The goal of this Firmware is to regularly bring out amazing updates based on wha - Up2Date: This firmware receives updates from a few repositories, not just from its Upstream. If there are functional, but yet un-merged Pull requests on another flipper firmware that are good, they will be in here! +----- +
+

Xtreme Settings:

+ + +We wrote a powerful yet easy-to-use application specifically for our Firmware, that gives you easy-access to all the fancy things we implemented: + + + +Base Graphics: Change the fallback assets used. Its either SFW (default) or NSFW +
Asset Pack: Allows you to easily customize your firmware, more on that below +
Anim Speed: Speed in which the animations play +
Cycle Anims: Duration of how long animations are played before switching to next +
Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;) +
Battery style: Classic Firmware battery style toggle, just at a more convenient place +
XP Level: Changes your Flippers level +
SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned +
SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper + +
+ -----

Animations / Asset Packs:

@@ -58,9 +79,14 @@ After installing the packs to Flipper, hit the Arrow UP button on t

Levels:

-This firmware contains some NSFW animations to bring a twist to the boring community, and added a fun leveling-system around them, that you can easily add to your own `Asset Packs`. +This Firmware has 30 levels, not just the basic 3 the official one has. -The idle_animations are tied to the level system. Each level you reach, unlocks a new animation. The higher your level, the more lewd it will become. Rumors have it, I'm to be found in at least one of those too +With this new system in place, it allows for some cool stuff like locking animations behind a certain level. This can be done fairly easy: The idle_animations are tied to the level system. Specifically, the `Min level` variable of your manifest file is used here. Each level you reach, unlocks a new animation. The higher your level, the more animations people can see. + +
+Our example + +In our case, this is used with the NSFW animations. Dont worry, these are disabled by default because I know not everyone likes to see my / anime tits and thats fine. Anyways.. each level gives a brand new background animation, they also become more and more lewd over time. (Funfact for those reading.. thats why the repository has this warning. Github doesnt like my tits :c) | Level | Animations | | ------------- | ------------- | @@ -68,7 +94,7 @@ The idle_animations are tied to the level system. Each level you reach, unlocks | 11-20 | Some tits, maybe an ass | | 21-30 | Fully NSFW, graphic scenes | -By default, SFW mode is selected, but if you want to enable all of the above simply hit `Arrow UP` from the main menu, select `Xtreme Settings` and change to NSFW graphics. +
-----
From c0b366438664c29f1837aff8caf9c51369d1f103 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 27 Jan 2023 23:14:40 +0000 Subject: [PATCH 037/231] Add credits for WatchDogs pack --- assets/dolphin/custom/ReadMe.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 assets/dolphin/custom/ReadMe.md diff --git a/assets/dolphin/custom/ReadMe.md b/assets/dolphin/custom/ReadMe.md new file mode 100644 index 000000000..359cad154 --- /dev/null +++ b/assets/dolphin/custom/ReadMe.md @@ -0,0 +1,6 @@ +# Pre-included Asset Packs + +Includes a WatchDogs asset pack by default. Credits: +- [WrenchAtHome](https://github.com/wrenchathome) for some [pdesktop anims](https://github.com/wrenchathome/flip0anims) +- [u/Cheroon](https://www.reddit.com/user/Cheroon/) for the [passport portait](https://www.reddit.com/r/watch_dogs/comments/50n046/pixel_art_wrench_mask_gif/) +- [WillyJL](https://github.com/Willy-JL) for other assets, icons and animations From 35473ffafef370f1873457aa63609688122b32c2 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 28 Jan 2023 06:53:55 +0000 Subject: [PATCH 038/231] Move most Bad BLE code to Bad USB --- applications/main/bad_ble/application.fam | 18 - applications/main/bad_ble/bad_ble_app.c | 194 ------- applications/main/bad_ble/bad_ble_app.h | 13 - applications/main/bad_ble/bad_ble_app_i.h | 63 -- .../main/bad_ble/bad_ble_custom_event.h | 7 - applications/main/bad_ble/bad_ble_script.c | 537 +++++++++--------- applications/main/bad_ble/bad_ble_script.h | 49 -- .../main/bad_ble/bad_ble_settings_filename.h | 3 - applications/main/bad_ble/badusb_10px.png | Bin 576 -> 0 bytes .../bad_ble/images/ActiveConnection_50x64.png | Bin 3842 -> 0 bytes .../main/bad_ble/images/Clock_18x18.png | Bin 1083 -> 0 bytes .../main/bad_ble/images/Error_18x18.png | Bin 1083 -> 0 bytes .../main/bad_ble/images/EviSmile1_18x21.png | Bin 3645 -> 0 bytes .../main/bad_ble/images/EviSmile2_18x21.png | Bin 3649 -> 0 bytes .../main/bad_ble/images/EviWaiting1_18x21.png | Bin 13020 -> 0 bytes .../main/bad_ble/images/EviWaiting2_18x21.png | Bin 12913 -> 0 bytes .../main/bad_ble/images/Percent_10x14.png | Bin 3624 -> 0 bytes .../main/bad_ble/images/SDQuestion_35x43.png | Bin 1950 -> 0 bytes .../main/bad_ble/images/Smile_18x18.png | Bin 1080 -> 0 bytes .../main/bad_ble/images/UsbTree_48x22.png | Bin 3653 -> 0 bytes .../main/bad_ble/images/badusb_10px.png | Bin 576 -> 0 bytes .../main/bad_ble/images/keyboard_10px.png | Bin 147 -> 0 bytes .../main/bad_ble/scenes/bad_ble_scene.c | 30 - .../main/bad_ble/scenes/bad_ble_scene.h | 29 - .../bad_ble/scenes/bad_ble_scene_config.c | 72 --- .../bad_ble/scenes/bad_ble_scene_config.h | 7 - .../scenes/bad_ble_scene_config_layout.c | 47 -- .../bad_ble/scenes/bad_ble_scene_config_mac.c | 57 -- .../scenes/bad_ble_scene_config_name.c | 46 -- .../main/bad_ble/scenes/bad_ble_scene_error.c | 65 --- .../scenes/bad_ble_scene_file_select.c | 51 -- .../main/bad_ble/scenes/bad_ble_scene_work.c | 54 -- applications/main/bad_ble/switch_ble.py | 24 - .../main/bad_ble/views/bad_ble_view.c | 221 ------- .../main/bad_ble/views/bad_ble_view.h | 21 - applications/main/bad_usb/bad_usb_app.c | 56 +- applications/main/bad_usb/bad_usb_app.h | 2 + applications/main/bad_usb/bad_usb_app_i.h | 30 +- applications/main/bad_usb/bad_usb_script.c | 82 ++- applications/main/bad_usb/bad_usb_script.h | 3 +- .../bad_usb/scenes/bad_usb_scene_config.c | 53 -- .../bad_usb/scenes/bad_usb_scene_config.h | 5 +- .../bad_usb/scenes/bad_usb_scene_config_bt.c | 81 +++ .../bad_usb/scenes/bad_usb_scene_config_mac.c | 57 ++ .../scenes/bad_usb_scene_config_name.c | 46 ++ .../bad_usb/scenes/bad_usb_scene_config_usb.c | 69 +++ .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 - .../scenes/bad_usb_scene_file_select.c | 2 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 6 +- firmware/targets/f7/api_symbols.csv | 5 +- 50 files changed, 694 insertions(+), 1415 deletions(-) delete mode 100644 applications/main/bad_ble/application.fam delete mode 100644 applications/main/bad_ble/bad_ble_app.c delete mode 100644 applications/main/bad_ble/bad_ble_app.h delete mode 100644 applications/main/bad_ble/bad_ble_app_i.h delete mode 100644 applications/main/bad_ble/bad_ble_custom_event.h delete mode 100644 applications/main/bad_ble/bad_ble_script.h delete mode 100644 applications/main/bad_ble/bad_ble_settings_filename.h delete mode 100644 applications/main/bad_ble/badusb_10px.png delete mode 100644 applications/main/bad_ble/images/ActiveConnection_50x64.png delete mode 100644 applications/main/bad_ble/images/Clock_18x18.png delete mode 100644 applications/main/bad_ble/images/Error_18x18.png delete mode 100644 applications/main/bad_ble/images/EviSmile1_18x21.png delete mode 100644 applications/main/bad_ble/images/EviSmile2_18x21.png delete mode 100644 applications/main/bad_ble/images/EviWaiting1_18x21.png delete mode 100644 applications/main/bad_ble/images/EviWaiting2_18x21.png delete mode 100644 applications/main/bad_ble/images/Percent_10x14.png delete mode 100644 applications/main/bad_ble/images/SDQuestion_35x43.png delete mode 100644 applications/main/bad_ble/images/Smile_18x18.png delete mode 100644 applications/main/bad_ble/images/UsbTree_48x22.png delete mode 100644 applications/main/bad_ble/images/badusb_10px.png delete mode 100644 applications/main/bad_ble/images/keyboard_10px.png delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene.h delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config.h delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_config_name.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_error.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_file_select.c delete mode 100644 applications/main/bad_ble/scenes/bad_ble_scene_work.c delete mode 100644 applications/main/bad_ble/switch_ble.py delete mode 100644 applications/main/bad_ble/views/bad_ble_view.c delete mode 100644 applications/main/bad_ble/views/bad_ble_view.h delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_name.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c diff --git a/applications/main/bad_ble/application.fam b/applications/main/bad_ble/application.fam deleted file mode 100644 index ac4106eac..000000000 --- a/applications/main/bad_ble/application.fam +++ /dev/null @@ -1,18 +0,0 @@ -App( - appid="bad_ble", - name="Bad BLE", - apptype=FlipperAppType.EXTERNAL, - entry_point="bad_ble_app", - cdefines=["APP_BAD_BLE"], - requires=[ - "gui", - "dialogs", - ], - stack_size=2 * 1024, - # icon="A_BadUsb_14", - order=70, - fap_category="Main", - fap_icon="badusb_10px.png", - fap_icon_assets="images", - fap_libs=["assets"], -) diff --git a/applications/main/bad_ble/bad_ble_app.c b/applications/main/bad_ble/bad_ble_app.c deleted file mode 100644 index c5a7e72d0..000000000 --- a/applications/main/bad_ble/bad_ble_app.c +++ /dev/null @@ -1,194 +0,0 @@ -#include "bad_ble_app_i.h" -#include "bad_ble_settings_filename.h" -#include -#include -#include -#include - -#include -#include - -#define BAD_BLE_SETTINGS_PATH BAD_BLE_APP_BASE_FOLDER "/" BAD_BLE_SETTINGS_FILE_NAME - -static bool bad_ble_app_custom_event_callback(void* context, uint32_t event) { - furi_assert(context); - BadBleApp* app = context; - return scene_manager_handle_custom_event(app->scene_manager, event); -} - -static bool bad_ble_app_back_event_callback(void* context) { - furi_assert(context); - BadBleApp* app = context; - return scene_manager_handle_back_event(app->scene_manager); -} - -static void bad_ble_app_tick_event_callback(void* context) { - furi_assert(context); - BadBleApp* app = context; - scene_manager_handle_tick_event(app->scene_manager); -} - -static void bad_ble_load_settings(BadBleApp* app) { - File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { - char chr; - while((storage_file_read(settings_file, &chr, 1) == 1) && - !storage_file_eof(settings_file) && !isspace(chr)) { - furi_string_push_back(app->keyboard_layout, chr); - } - } - storage_file_close(settings_file); - storage_file_free(settings_file); -} - -static void bad_ble_save_settings(BadBleApp* app) { - File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_BLE_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { - storage_file_write( - settings_file, - furi_string_get_cstr(app->keyboard_layout), - furi_string_size(app->keyboard_layout)); - storage_file_write(settings_file, "\n", 1); - } - storage_file_close(settings_file); - storage_file_free(settings_file); -} - -void bad_ble_set_name(BadBleApp* app, const char* fmt, ...) { - furi_assert(app); - - va_list args; - va_start(args, fmt); - - vsnprintf(app->name, BAD_BLE_ADV_NAME_MAX_LEN, fmt, args); - - va_end(args); -} - -BadBleApp* bad_ble_app_alloc(char* arg) { - BadBleApp* app = malloc(sizeof(BadBleApp)); - - app->bad_ble_script = NULL; - - app->file_path = furi_string_alloc(); - app->keyboard_layout = furi_string_alloc(); - if(arg && strlen(arg)) { - furi_string_set(app->file_path, arg); - } - - bad_ble_load_settings(app); - - app->gui = furi_record_open(RECORD_GUI); - app->notifications = furi_record_open(RECORD_NOTIFICATION); - app->dialogs = furi_record_open(RECORD_DIALOGS); - - app->view_dispatcher = view_dispatcher_alloc(); - view_dispatcher_enable_queue(app->view_dispatcher); - - app->scene_manager = scene_manager_alloc(&bad_ble_scene_handlers, app); - - view_dispatcher_set_event_callback_context(app->view_dispatcher, app); - view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_ble_app_tick_event_callback, 500); - view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_ble_app_custom_event_callback); - view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_ble_app_back_event_callback); - - Bt* bt = furi_record_open(RECORD_BT); - app->bt = bt; - const char* adv_name = bt_get_profile_adv_name(bt); - memcpy(app->name, adv_name, BAD_BLE_ADV_NAME_MAX_LEN); - - const uint8_t* mac_addr = bt_get_profile_mac_address(bt); - memcpy(app->mac, mac_addr, BAD_BLE_MAC_ADDRESS_LEN); - - // Custom Widget - app->widget = widget_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBleAppViewError, widget_get_view(app->widget)); - - app->submenu = submenu_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBleAppViewConfig, submenu_get_view(app->submenu)); - - app->bad_ble_view = bad_ble_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBleAppViewWork, bad_ble_get_view(app->bad_ble_view)); - - app->text_input = text_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBleAppViewConfigName, text_input_get_view(app->text_input)); - - app->byte_input = byte_input_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, BadBleAppViewConfigMac, byte_input_get_view(app->byte_input)); - - view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); - - if(!furi_string_empty(app->file_path)) { - app->bad_ble_script = bad_ble_script_open(app->file_path, bt); - bad_ble_script_set_keyboard_layout(app->bad_ble_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadBleSceneWork); - } else { - furi_string_set(app->file_path, BAD_BLE_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadBleSceneFileSelect); - } - - return app; -} - -void bad_ble_app_free(BadBleApp* app) { - furi_assert(app); - - if(app->bad_ble_script) { - bad_ble_script_close(app->bad_ble_script); - app->bad_ble_script = NULL; - } - - // Views - view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewWork); - bad_ble_free(app->bad_ble_view); - - // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewError); - widget_free(app->widget); - - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfig); - submenu_free(app->submenu); - - // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfigName); - text_input_free(app->text_input); - - // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadBleAppViewConfigMac); - byte_input_free(app->byte_input); - - // View dispatcher - view_dispatcher_free(app->view_dispatcher); - scene_manager_free(app->scene_manager); - - // Close records - furi_record_close(RECORD_GUI); - furi_record_close(RECORD_NOTIFICATION); - furi_record_close(RECORD_DIALOGS); - furi_record_close(RECORD_BT); - - bad_ble_save_settings(app); - - furi_string_free(app->file_path); - furi_string_free(app->keyboard_layout); - - free(app); -} - -int32_t bad_ble_app(void* p) { - BadBleApp* bad_ble_app = bad_ble_app_alloc((char*)p); - - view_dispatcher_run(bad_ble_app->view_dispatcher); - - bad_ble_app_free(bad_ble_app); - return 0; -} diff --git a/applications/main/bad_ble/bad_ble_app.h b/applications/main/bad_ble/bad_ble_app.h deleted file mode 100644 index ff681e849..000000000 --- a/applications/main/bad_ble/bad_ble_app.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct BadBleApp BadBleApp; - -void bad_ble_set_name(BadBleApp* app, const char* fmt, ...); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/bad_ble/bad_ble_app_i.h b/applications/main/bad_ble/bad_ble_app_i.h deleted file mode 100644 index fd2405c51..000000000 --- a/applications/main/bad_ble/bad_ble_app_i.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "bad_ble_app.h" -#include "scenes/bad_ble_scene.h" -#include "bad_ble_script.h" -#include "bad_ble_custom_event.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "views/bad_ble_view.h" - -#define BAD_BLE_APP_BASE_FOLDER ANY_PATH("BadUsb") -#define BAD_BLE_APP_PATH_LAYOUT_FOLDER BAD_BLE_APP_BASE_FOLDER "/layouts" -#define BAD_BLE_APP_SCRIPT_EXTENSION ".txt" -#define BAD_BLE_APP_LAYOUT_EXTENSION ".kl" - -#define BAD_BLE_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro -#define BAD_BLE_ADV_NAME_MAX_LEN 18 - -typedef enum { - BadBleAppErrorNoFiles, - BadBleAppErrorCloseRpc, -} BadBleAppError; - -struct BadBleApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - Submenu* submenu; - - TextInput* text_input; - ByteInput* byte_input; - uint8_t mac[BAD_BLE_MAC_ADDRESS_LEN]; - char name[BAD_BLE_ADV_NAME_MAX_LEN + 1]; - - BadBleAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadBle* bad_ble_view; - BadBleScript* bad_ble_script; - - Bt* bt; -}; - -typedef enum { - BadBleAppViewError, - BadBleAppViewWork, - BadBleAppViewConfig, - BadBleAppViewConfigMac, - BadBleAppViewConfigName -} BadBleAppView; \ No newline at end of file diff --git a/applications/main/bad_ble/bad_ble_custom_event.h b/applications/main/bad_ble/bad_ble_custom_event.h deleted file mode 100644 index ba31411d0..000000000 --- a/applications/main/bad_ble/bad_ble_custom_event.h +++ /dev/null @@ -1,7 +0,0 @@ - - -typedef enum BadBleCustomEvent { - BadBleAppCustomEventTextEditResult, - BadBleAppCustomEventByteInputDone, - BadBleCustomEventErrorBack -} BadBleCustomEvent; \ No newline at end of file diff --git a/applications/main/bad_ble/bad_ble_script.c b/applications/main/bad_ble/bad_ble_script.c index 7a14c4498..a969df7dd 100644 --- a/applications/main/bad_ble/bad_ble_script.c +++ b/applications/main/bad_ble/bad_ble_script.c @@ -1,198 +1,199 @@ -#include -#include -#include -#include -#include -#include -#include -#include "bad_ble_script.h" -#include +// #include +// #include +// #include +// #include +// #include +// #include -#include +// #include +// #include "bad_ble_script.h" +// #include -#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") +// #include -#define TAG "BadBle" -#define WORKER_TAG TAG "Worker" -#define FILE_BUFFER_LEN 16 +// #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") -#define SCRIPT_STATE_ERROR (-1) -#define SCRIPT_STATE_END (-2) -#define SCRIPT_STATE_NEXT_LINE (-3) +// #define TAG "BadBle" +// #define WORKER_TAG TAG "Worker" +// #define FILE_BUFFER_LEN 16 -#define BADBLE_ASCII_TO_KEY(script, x) \ - (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) +// #define SCRIPT_STATE_ERROR (-1) +// #define SCRIPT_STATE_END (-2) +// #define SCRIPT_STATE_NEXT_LINE (-3) -typedef enum { - WorkerEvtToggle = (1 << 0), - WorkerEvtEnd = (1 << 1), - WorkerEvtConnect = (1 << 2), - WorkerEvtDisconnect = (1 << 3), -} WorkerEvtFlags; +// #define BADBLE_ASCII_TO_KEY(script, x) \ +// (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) -typedef enum { - LevelRssi122_100, - LevelRssi99_80, - LevelRssi79_60, - LevelRssi59_40, - LevelRssi39_0, - LevelRssiNum, - LevelRssiError = 0xFF, -} LevelRssiRange; +// typedef enum { +// WorkerEvtToggle = (1 << 0), +// WorkerEvtEnd = (1 << 1), +// WorkerEvtConnect = (1 << 2), +// WorkerEvtDisconnect = (1 << 3), +// } WorkerEvtFlags; -/** - * Delays for waiting between HID key press and key release -*/ -const uint8_t bt_hid_delays[LevelRssiNum] = { - 30, // LevelRssi122_100 - 25, // LevelRssi99_80 - 20, // LevelRssi79_60 - 17, // LevelRssi59_40 - 14, // LevelRssi39_0 -}; +// typedef enum { +// LevelRssi122_100, +// LevelRssi99_80, +// LevelRssi79_60, +// LevelRssi59_40, +// LevelRssi39_0, +// LevelRssiNum, +// LevelRssiError = 0xFF, +// } LevelRssiRange; -struct BadBleScript { - BadBleState st; - FuriString* file_path; - uint32_t defdelay; - uint16_t layout[128]; - FuriThread* thread; - uint8_t file_buf[FILE_BUFFER_LEN + 1]; - uint8_t buf_start; - uint8_t buf_len; - bool file_end; - FuriString* line; +// /** +// * Delays for waiting between HID key press and key release +// */ +// const uint8_t bt_hid_delays[LevelRssiNum] = { +// 30, // LevelRssi122_100 +// 25, // LevelRssi99_80 +// 20, // LevelRssi79_60 +// 17, // LevelRssi59_40 +// 14, // LevelRssi39_0 +// }; - FuriString* line_prev; - uint32_t repeat_cnt; +// struct BadBleScript { - File* debug_file; - Bt* bt; -}; +// BadBleState st; +// FuriString* file_path; +// uint32_t defdelay; +// uint16_t layout[128]; +// FuriThread* thread; +// uint8_t file_buf[FILE_BUFFER_LEN + 1]; +// uint8_t buf_start; +// uint8_t buf_len; +// bool file_end; +// FuriString* line; -typedef struct { - char* name; - uint16_t keycode; -} DuckyKey; +// FuriString* line_prev; +// uint32_t repeat_cnt; -static const DuckyKey ducky_keys[] = { - {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, - {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, - {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, - {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, - {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, +// Bt* bt; +// }; - {"CTRL", KEY_MOD_LEFT_CTRL}, - {"CONTROL", KEY_MOD_LEFT_CTRL}, - {"SHIFT", KEY_MOD_LEFT_SHIFT}, - {"ALT", KEY_MOD_LEFT_ALT}, - {"GUI", KEY_MOD_LEFT_GUI}, - {"WINDOWS", KEY_MOD_LEFT_GUI}, +// typedef struct { +// char* name; +// uint16_t keycode; +// } DuckyKey; - {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, - {"DOWN", HID_KEYBOARD_DOWN_ARROW}, - {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, - {"LEFT", HID_KEYBOARD_LEFT_ARROW}, - {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, - {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, - {"UPARROW", HID_KEYBOARD_UP_ARROW}, - {"UP", HID_KEYBOARD_UP_ARROW}, +// static const DuckyKey ducky_keys[] = { +// {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, +// {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, +// {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, +// {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, +// {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - {"ENTER", HID_KEYBOARD_RETURN}, - {"BREAK", HID_KEYBOARD_PAUSE}, - {"PAUSE", HID_KEYBOARD_PAUSE}, - {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, - {"DELETE", HID_KEYBOARD_DELETE}, - {"BACKSPACE", HID_KEYPAD_BACKSPACE}, - {"END", HID_KEYBOARD_END}, - {"ESC", HID_KEYBOARD_ESCAPE}, - {"ESCAPE", HID_KEYBOARD_ESCAPE}, - {"HOME", HID_KEYBOARD_HOME}, - {"INSERT", HID_KEYBOARD_INSERT}, - {"NUMLOCK", HID_KEYPAD_NUMLOCK}, - {"PAGEUP", HID_KEYBOARD_PAGE_UP}, - {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, - {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, - {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, - {"SPACE", HID_KEYBOARD_SPACEBAR}, - {"TAB", HID_KEYBOARD_TAB}, - {"MENU", HID_KEYBOARD_APPLICATION}, - {"APP", HID_KEYBOARD_APPLICATION}, +// {"CTRL", KEY_MOD_LEFT_CTRL}, +// {"CONTROL", KEY_MOD_LEFT_CTRL}, +// {"SHIFT", KEY_MOD_LEFT_SHIFT}, +// {"ALT", KEY_MOD_LEFT_ALT}, +// {"GUI", KEY_MOD_LEFT_GUI}, +// {"WINDOWS", KEY_MOD_LEFT_GUI}, - {"F1", HID_KEYBOARD_F1}, - {"F2", HID_KEYBOARD_F2}, - {"F3", HID_KEYBOARD_F3}, - {"F4", HID_KEYBOARD_F4}, - {"F5", HID_KEYBOARD_F5}, - {"F6", HID_KEYBOARD_F6}, - {"F7", HID_KEYBOARD_F7}, - {"F8", HID_KEYBOARD_F8}, - {"F9", HID_KEYBOARD_F9}, - {"F10", HID_KEYBOARD_F10}, - {"F11", HID_KEYBOARD_F11}, - {"F12", HID_KEYBOARD_F12}, -}; +// {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, +// {"DOWN", HID_KEYBOARD_DOWN_ARROW}, +// {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, +// {"LEFT", HID_KEYBOARD_LEFT_ARROW}, +// {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, +// {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, +// {"UPARROW", HID_KEYBOARD_UP_ARROW}, +// {"UP", HID_KEYBOARD_UP_ARROW}, -static const char ducky_cmd_comment[] = {"REM"}; -static const char ducky_cmd_id[] = {"ID"}; -static const char ducky_cmd_delay[] = {"DELAY "}; -static const char ducky_cmd_string[] = {"STRING "}; -static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; -static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; -static const char ducky_cmd_repeat[] = {"REPEAT "}; -static const char ducky_cmd_sysrq[] = {"SYSRQ "}; +// {"ENTER", HID_KEYBOARD_RETURN}, +// {"BREAK", HID_KEYBOARD_PAUSE}, +// {"PAUSE", HID_KEYBOARD_PAUSE}, +// {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, +// {"DELETE", HID_KEYBOARD_DELETE}, +// {"BACKSPACE", HID_KEYPAD_BACKSPACE}, +// {"END", HID_KEYBOARD_END}, +// {"ESC", HID_KEYBOARD_ESCAPE}, +// {"ESCAPE", HID_KEYBOARD_ESCAPE}, +// {"HOME", HID_KEYBOARD_HOME}, +// {"INSERT", HID_KEYBOARD_INSERT}, +// {"NUMLOCK", HID_KEYPAD_NUMLOCK}, +// {"PAGEUP", HID_KEYBOARD_PAGE_UP}, +// {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, +// {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, +// {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, +// {"SPACE", HID_KEYBOARD_SPACEBAR}, +// {"TAB", HID_KEYBOARD_TAB}, +// {"MENU", HID_KEYBOARD_APPLICATION}, +// {"APP", HID_KEYBOARD_APPLICATION}, -static const char ducky_cmd_altchar[] = {"ALTCHAR "}; -static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; -static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; +// {"F1", HID_KEYBOARD_F1}, +// {"F2", HID_KEYBOARD_F2}, +// {"F3", HID_KEYBOARD_F3}, +// {"F4", HID_KEYBOARD_F4}, +// {"F5", HID_KEYBOARD_F5}, +// {"F6", HID_KEYBOARD_F6}, +// {"F7", HID_KEYBOARD_F7}, +// {"F8", HID_KEYBOARD_F8}, +// {"F9", HID_KEYBOARD_F9}, +// {"F10", HID_KEYBOARD_F10}, +// {"F11", HID_KEYBOARD_F11}, +// {"F12", HID_KEYBOARD_F12}, +// }; -static const char ducky_cmd_lang[] = {"DUCKY_LANG"}; +// static const char ducky_cmd_comment[] = {"REM"}; +// static const char ducky_cmd_id[] = {"ID"}; +// static const char ducky_cmd_delay[] = {"DELAY "}; +// static const char ducky_cmd_string[] = {"STRING "}; +// static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; +// static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; +// static const char ducky_cmd_repeat[] = {"REPEAT "}; +// static const char ducky_cmd_sysrq[] = {"SYSRQ "}; -static const uint8_t numpad_keys[10] = { - HID_KEYPAD_0, - HID_KEYPAD_1, - HID_KEYPAD_2, - HID_KEYPAD_3, - HID_KEYPAD_4, - HID_KEYPAD_5, - HID_KEYPAD_6, - HID_KEYPAD_7, - HID_KEYPAD_8, - HID_KEYPAD_9, -}; +// static const char ducky_cmd_altchar[] = {"ALTCHAR "}; +// static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; +// static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; -uint8_t bt_timeout = 0; +// static const char ducky_cmd_lang[] = {"DUCKY_LANG"}; -static LevelRssiRange bt_remote_rssi_range(Bt* bt) { - BtRssi rssi_data = {0}; +// static const uint8_t numpad_keys[10] = { +// HID_KEYPAD_0, +// HID_KEYPAD_1, +// HID_KEYPAD_2, +// HID_KEYPAD_3, +// HID_KEYPAD_4, +// HID_KEYPAD_5, +// HID_KEYPAD_6, +// HID_KEYPAD_7, +// HID_KEYPAD_8, +// HID_KEYPAD_9, +// }; - if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; +// uint8_t bt_timeout = 0; - if(rssi_data.rssi <= 39) - return LevelRssi39_0; - else if(rssi_data.rssi <= 59) - return LevelRssi59_40; - else if(rssi_data.rssi <= 79) - return LevelRssi79_60; - else if(rssi_data.rssi <= 99) - return LevelRssi99_80; - else if(rssi_data.rssi <= 122) - return LevelRssi122_100; +// static LevelRssiRange bt_remote_rssi_range(Bt* bt) { +// BtRssi rssi_data = {0}; - return LevelRssiError; -} +// if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; -static inline void update_bt_timeout(Bt* bt) { - LevelRssiRange r = bt_remote_rssi_range(bt); - if(r < LevelRssiNum) { - bt_timeout = bt_hid_delays[r]; - } -} +// if(rssi_data.rssi <= 39) +// return LevelRssi39_0; +// else if(rssi_data.rssi <= 59) +// return LevelRssi59_40; +// else if(rssi_data.rssi <= 79) +// return LevelRssi79_60; +// else if(rssi_data.rssi <= 99) +// return LevelRssi99_80; +// else if(rssi_data.rssi <= 122) +// return LevelRssi122_100; + +// return LevelRssiError; +// } + +// static inline void update_bt_timeout(Bt* bt) { +// LevelRssiRange r = bt_remote_rssi_range(bt); +// if(r < LevelRssiNum) { +// bt_timeout = bt_hid_delays[r]; +// } +// } /** * @brief Wait until there are enough free slots in the keyboard buffer - * - * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) + * + * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) */ static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { uint32_t start = furi_get_tick(); @@ -206,26 +207,26 @@ static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t } } -static bool ducky_get_number(const char* param, uint32_t* val) { - uint32_t value = 0; - if(sscanf(param, "%lu", &value) == 1) { - *val = value; - return true; - } - return false; -} +// static bool ducky_get_number(const char* param, uint32_t* val) { +// uint32_t value = 0; +// if(sscanf(param, "%lu", &value) == 1) { +// *val = value; +// return true; +// } +// return false; +// } -static uint32_t ducky_get_command_len(const char* line) { - uint32_t len = strlen(line); - for(uint32_t i = 0; i < len; i++) { - if(line[i] == ' ') return i; - } - return 0; -} +// static uint32_t ducky_get_command_len(const char* line) { +// uint32_t len = strlen(line); +// for(uint32_t i = 0; i < len; i++) { +// if(line[i] == ' ') return i; +// } +// return 0; +// } -static bool ducky_is_line_end(const char chr) { - return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -} +// static bool ducky_is_line_end(const char chr) { +// return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); +// } static void ducky_numlock_on() { if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { @@ -269,25 +270,25 @@ static bool ducky_altchar(const char* charcode) { return state; } -static bool ducky_altstring(const char* param) { - uint32_t i = 0; - bool state = false; +// static bool ducky_altstring(const char* param) { +// uint32_t i = 0; +// bool state = false; - while(param[i] != '\0') { - if((param[i] < ' ') || (param[i] > '~')) { - i++; - continue; // Skip non-printable chars - } +// while(param[i] != '\0') { +// if((param[i] < ' ') || (param[i] > '~')) { +// i++; +// continue; // Skip non-printable chars +// } - char temp_str[4]; - snprintf(temp_str, 4, "%u", param[i]); +// char temp_str[4]; +// snprintf(temp_str, 4, "%u", param[i]); - state = ducky_altchar(temp_str); - if(state == false) break; - i++; - } - return state; -} +// state = ducky_altchar(temp_str); +// if(state == false) break; +// i++; +// } +// return state; +// } static bool ducky_string(BadBleScript* bad_ble, const char* param) { uint32_t i = 0; @@ -305,19 +306,19 @@ static bool ducky_string(BadBleScript* bad_ble, const char* param) { return true; } -static uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) { - for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { - size_t key_cmd_len = strlen(ducky_keys[i].name); - if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && - (ducky_is_line_end(param[key_cmd_len]))) { - return ducky_keys[i].keycode; - } - } - if((accept_chars) && (strlen(param) > 0)) { - return (BADBLE_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF); - } - return 0; -} +// static uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) { +// for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { +// size_t key_cmd_len = strlen(ducky_keys[i].name); +// if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && +// (ducky_is_line_end(param[key_cmd_len]))) { +// return ducky_keys[i].keycode; +// } +// } +// if((accept_chars) && (strlen(param) > 0)) { +// return (BADBLE_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF); +// } +// return 0; +// } static int32_t ducky_parse_line(BadBleScript* bad_ble, FuriString* line, char* error, size_t error_len) { @@ -749,70 +750,70 @@ static int32_t bad_ble_worker(void* context) { return 0; } -static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) { - furi_assert(bad_ble); - memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout)); - memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout))); -} +// static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) { +// furi_assert(bad_ble); +// memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout)); +// memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout))); +// } -BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt) { - furi_assert(file_path); +// BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt) { +// furi_assert(file_path); - BadBleScript* bad_ble = malloc(sizeof(BadBleScript)); - bad_ble->file_path = furi_string_alloc(); - furi_string_set(bad_ble->file_path, file_path); - bad_ble_script_set_default_keyboard_layout(bad_ble); +// BadBleScript* bad_ble = malloc(sizeof(BadBleScript)); +// bad_ble->file_path = furi_string_alloc(); +// furi_string_set(bad_ble->file_path, file_path); +// bad_ble_script_set_default_keyboard_layout(bad_ble); - bad_ble->st.state = BadBleStateInit; - bad_ble->st.error[0] = '\0'; +// bad_ble->st.state = BadBleStateInit; +// bad_ble->st.error[0] = '\0'; - bad_ble->bt = bt; +// bad_ble->bt = bt; - bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble); - furi_thread_start(bad_ble->thread); - return bad_ble; -} //-V773 +// bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble); +// furi_thread_start(bad_ble->thread); +// return bad_ble; +// } //-V773 -void bad_ble_script_close(BadBleScript* bad_ble) { - furi_assert(bad_ble); - furi_record_close(RECORD_STORAGE); - furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd); - furi_thread_join(bad_ble->thread); - furi_thread_free(bad_ble->thread); - furi_string_free(bad_ble->file_path); - free(bad_ble); -} +// void bad_ble_script_close(BadBleScript* bad_ble) { +// furi_assert(bad_ble); +// furi_record_close(RECORD_STORAGE); +// furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd); +// furi_thread_join(bad_ble->thread); +// furi_thread_free(bad_ble->thread); +// furi_string_free(bad_ble->file_path); +// free(bad_ble); +// } -void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) { - furi_assert(bad_ble); +// void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) { +// furi_assert(bad_ble); - if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) { - // do not update keyboard layout while a script is running - return; - } +// if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) { +// // do not update keyboard layout while a script is running +// return; +// } - File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { - if(storage_file_open( - layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint16_t layout[128]; - if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { - memcpy(bad_ble->layout, layout, sizeof(layout)); - } - } - storage_file_close(layout_file); - } else { - bad_ble_script_set_default_keyboard_layout(bad_ble); - } - storage_file_free(layout_file); -} +// File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); +// if(!furi_string_empty(layout_path)) { +// if(storage_file_open( +// layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { +// uint16_t layout[128]; +// if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { +// memcpy(bad_ble->layout, layout, sizeof(layout)); +// } +// } +// storage_file_close(layout_file); +// } else { +// bad_ble_script_set_default_keyboard_layout(bad_ble); +// } +// storage_file_free(layout_file); +// } -void bad_ble_script_toggle(BadBleScript* bad_ble) { - furi_assert(bad_ble); - furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtToggle); -} +// void bad_ble_script_toggle(BadBleScript* bad_ble) { +// furi_assert(bad_ble); +// furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtToggle); +// } -BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) { - furi_assert(bad_ble); - return &(bad_ble->st); -} +// BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) { +// furi_assert(bad_ble); +// return &(bad_ble->st); +// } diff --git a/applications/main/bad_ble/bad_ble_script.h b/applications/main/bad_ble/bad_ble_script.h deleted file mode 100644 index 5445c45ed..000000000 --- a/applications/main/bad_ble/bad_ble_script.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -typedef struct BadBleScript BadBleScript; - -typedef enum { - BadBleStateInit, - BadBleStateNotConnected, - BadBleStateIdle, - BadBleStateWillRun, - BadBleStateRunning, - BadBleStateDelay, - BadBleStateDone, - BadBleStateScriptError, - BadBleStateFileError, -} BadBleWorkerState; - -typedef struct { - BadBleWorkerState state; - uint16_t line_cur; - uint16_t line_nb; - uint32_t delay_remain; - uint16_t error_line; - char error[64]; -} BadBleState; - -BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt); - -void bad_ble_script_close(BadBleScript* bad_ble); - -void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path); - -void bad_ble_script_start(BadBleScript* bad_ble); - -void bad_ble_script_stop(BadBleScript* bad_ble); - -void bad_ble_script_toggle(BadBleScript* bad_ble); - -BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/bad_ble/bad_ble_settings_filename.h b/applications/main/bad_ble/bad_ble_settings_filename.h deleted file mode 100644 index 73245ad5f..000000000 --- a/applications/main/bad_ble/bad_ble_settings_filename.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define BAD_BLE_SETTINGS_FILE_NAME ".BadBle.settings" diff --git a/applications/main/bad_ble/badusb_10px.png b/applications/main/bad_ble/badusb_10px.png deleted file mode 100644 index 037474aa3bc9c2e1aca79a68483e69980432bcf5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 576 zcmV-G0>AxEX>4Tx04R}tkv&MmKpe$i(`rSk4t5Z6$WWau6cusQDionYs1;guFuC*#nlvOW zE{=k0!NHHks)LKOt`4q(Aou~|=;Wm6A|?JWDYS_3;J6>}?mh0_0Yan9G%FATG`(u3 z5^*t;T@{0`5D-8=V(6BcWz0!Z5}xDh9zMR_MR}I@xj#prnzI<-6NzV;VOEJZh^IHJ z2Iqa^Fe}O`@j3ChNf#u3C`-Nm{=@yu+qV-Xlle$#1U1~DPPFA zta9Gstd(o5bx;1nP)=W2<~q$0B(R7jND!f*h7!uCB1)@HiiH&I$36VRj$a~|Laq`R zITlcX2HEk0|H1EWt^DMKn-q!zT`#u%F$x5Cfo9#dzmILZc>?&Kfh)c3uQY&}Ptxmc zEph}5Yy%h9ZB5w&E_Z;TCqp)6NAlAY@_FF>jJ_!g4Bi60Yi@6?eVjf3Y3eF@0~{Oz zV+G1y_jq?tXK(+WY4!I5C=YUpXXIhH00006VoOIv0RI600RN!9r;`8x010qNS#tmY z3ljhU3ljkVnw%H_000McNliru<^lu`HWXkp{t5s906j@WK~xyijZi@f05Awj>HlAL zr$MwDdI>{Qf+U53tOUR#xOeyy)jcQo#JNRv)7r6DVVK|+*(cmT+R+EbO(O#X#REG4 O0000&vafZq~f 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 diff --git a/applications/main/bad_ble/images/Error_18x18.png b/applications/main/bad_ble/images/Error_18x18.png deleted file mode 100644 index 16a5a74d96686c9ff2d9d96984a285f3885cffbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1083 zcmbVLO=#0l91nDeA53H@GKcca5EPcrrb`o6$JVsAu+G{QR&T!My{=(RUY5MA>A)Sl zdD4@f#M8iCym|7VCs7c=gNnj#9z6JU@F>)meoPNz2Ls9f|6cyT-~an|dGX5V(KAOm zjvFl&tO}E3@q0MIzVO5rW@4P?YIKP-Xd4EYn?t0ILD7XPxPl?-ti8fB9GBQ|sx?|G zEtocOMHt(Nk?S)w$IZ+}KD1Xc1$DgQcp3i3(`P(zP=;SlmE@A2#Z9NM8Q`VO#j3rz zY8!~3y$og|lM%R>LJ+wvFEpbJ-{Uoz9$!m5=$X*f4Bro`Rw{!m2{6z_MX+UA2D%|4 zSci7KJ_S@+RU}!H6itw2GijKb1_lq$+y$s%R;>KM89Qb8CZ)b9N$qx9Y$rt$tVoJs z7?P|?swyxGA?$b*MuHbk4jC*Q+JWO!hj<`ngmtn`Gdv5mpM&d{N_)g!IH(k>nG``^ zQbbvD-8iwHbx14tZy5Vpht-acr3wzodSJ7LG$w~&R=k59#fB^z^J?I*uE3T>>~$A= zv}k2`_D4hxGLuL*QZ`HpN(v?gZCb}d+E%e($Qrg470Wh8L!SNcdRo!)nwHX%a#B%p z*|~I9OY7;JrO#Vx(vXMPq8C!=*?8#NVZH}g?Le%V4KSo6s1ni|jzPIeC<&Xy2dXNj zz{L`@9WTDQ6nCkgw1op_1EYLET+l1C>Fg7Np-(rEjMD;|PN}R0nkLjCM1rR3EG3vi zX~a_KYA3hc1AOxR-^6tGo!e{*7ot=XaSLN&)^x7*$R z_;8nL#iBJ=jXt&Rz8&Mh$jFComtIimxkqQi diff --git a/applications/main/bad_ble/images/EviSmile1_18x21.png b/applications/main/bad_ble/images/EviSmile1_18x21.png deleted file mode 100644 index 987af32587ca7fbada8810abd0cabf788c9c04f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3645 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 V0A_Pz|1^geur#waEi!h!{2vr@XiES9 diff --git a/applications/main/bad_ble/images/EviSmile2_18x21.png b/applications/main/bad_ble/images/EviSmile2_18x21.png deleted file mode 100644 index 7e28c9f018a0f2cb572e5f5ce1db2e5c71511dd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3649 zcmaJ@c{r5q+kPw+itIu%M!c0}78H}QFQW$2*hZ4Z7z}1<3}z&ew2&=Z)`Subsgy~! zitI#@P?jtS4GGE8H@&~N_xJtr^*zV&JokNH_j#Vzbzj%@9LIeHV`nWYq96hQfT#`1 z0?QjEd9RF+0Ph;}C*NUXe8#ULo#uHtV0i zpB@kifK}N-&El^4;@1HD1#wA}#^}o;&eAdx*(j%m^SvUdoXcZ*`#3(PF_(|WI-St} zqC8ae=xiu=Zf@=ETJ==+)OshYYiERnq1_+?Ndf*|q9 zw&y-u8UbKlfW-`FlpC+}-J=5h0IgShuVmBc&!{Slx(fhG0!F}+Q``9xu|Tu7W3x2S zybCCIc<3bpqyRtwE6fZGl!yYe-)xMw0R6?uLvlcW{_bKSAdU~n*k`?$-{dK9$|(}7 z$zT5*$YYy;wFT?T_##{%!>#!vYPJBu@wmjDCZ~Xi3^UDk0Hn_knD3G55CEYC@}NC+ zBgG!HXby@GsBcT{NI%-6Bh5*Dr4aIUeq>B#?0LX_GrZh>ac|*qaCUl@suXHU0NuF* z02EfcpKaI@2Vhw7wu}<20TUT!xLGY7;brQC6l@H=Cl*ZN%^I9@D*lLQ^JY0e6Li z0oyjQo?w$KR9aHUB&W~87nIXBgp)%=0ro}vdb`Kl9<>G3hkxPYj}^o91Oq1Fi&|F| zwkHANKDuz$3IHV6ttOag@Btm^g&zT+`qQoxcT(igFNFZWA}{hlx#_kY&!pM)V%g7> zs_W(W@mnoScI>S;6gS&C9C0fjt?%u(@*XE1%ysS(K&kux;8 zt*3V7KHpV+QCQHlSx5@6g19W<8Q%}?6q3t`7X;%`y4NBKLDQF|kAWMT>4p5oW`0TT zDAli8bZLXQ6DB_r2b)3gnDv-yYgkI;gJS}3_=8NI+)-ADd6^g3&CuQH9+8&s->p!w z2O04=zo`4@ryvG!HYT1B(G3&xzWNS-;_4;KQ&(^b>P@nQ37npDf*wH$cPLm!u|5~i z723-m8zD6-bn=4u^MLb-iPktY&iszrtZId1m5_^Y)CJh{zre|N>?_nlC084mo{0O2 zI4idL7nMCKxoRi>5|i>sM(q`Axi)SmqN0`vx7lvvj~Ya26*?3e^@x+Q(dsjaIPL(8;)$RkGdjuG7xDC!NpUwsLxi`B*IcM)q!Rv69o%;)7+K*br<2 zrt6qTL9NHe`5y$)2N$EQ@-CtZ90`>#<>ORjU&4tCII}*wv%rj||8-kWw+E}U=-@4D ziouXGXb1Da5^uJ5l6TJJ=?*@zm-k2J4c=uR=~U?y?L4C;pk=Iezt6AKyEMG?&_L)w z?SSVTeNJ|6W`G++%Q4B(%vnN^5i3E$RR^n%RYg|~26cTldQF&NO$#rzE{RRQ@3vkd ze=As$`^@d*b}Ju(>Ixl9ln;RE6Xx3!37`D0lQ`Y;7e?<$wE0#gHTV{E+Z6o8QU7wu z=c67|&d8fh-R;TN{XiV@H^h6A;Ddz?g^lC2`#VznGrg<2D_%3&+nY6q*!}F5*?5EA zZ2w$*?Yrv1^|@*}vpK8Gy~M&x*`u&TgGESjI1_Et8kKl-hSo zD)k*^91f#1g4%-vXw@@?qq;AO8;V~{yZ9*j+ziZF)RVh?G_g%GJvd#?fm{?*M7a^# zmO7#ErK;!A>!pIMr&&X#@5pc7w<8B-c3xvz?=1f3xt&CG6@R-qi35(yM%_t!>PAd(bMgZ zg)Wa+2VCYTljJkxR?kZBKL9V${(P*$fpMC#qS?nDcU|+TiC;)4zWU_wpxLpU6#4 zcedq*7`p1YCWh%pUzbdOU_228GQ&W2*-sQvY?Y+GUdW2Jx2(;N%RhF%l5@oH+GLJ% z>aza(!)MKZ_+GTP3VNv{Y>(AoCCOiVqPl47Y|;0D-SzJDJ1v8h?3C;RtSBk1LgOv8 za$lvrw}wWt=s0VV+^U#-sdZ&sbv1BtP$nQ6-Cag6M>i8R- zVeie)tE$`2%ZAk?mSZ^O5BoVx*M$*qo#j(m)mR6)5N(({w#ti1n(sN==G*olZ38og z!#aKSV-0+T(?@iXmxb#Y#_RB<70LeYbKE}|R||5KPAXZ~R{jjiouAKDY~ClS_&l{>hpNygN0#F}8NJ3%A}szkM~ftFDYyyh!KX zExw0nQf*SM?qnesZm*Yi4xZ(5xK+bVHOd+L)=f4si`_p6O+~NlSB$2@HrF957Z%qd z4Adlew@P`2C63`h^=5?N=|sTPi|R=P*^u!*L@W{S#X8+WGz0(vb&?~FfwM&;2vo8* z{uf4@Nv84G0AOg$q~QtvLQ0B@n?xg8$Y<@aDhF5HRR(2*V!<{!dUiTMWpYN+*I2 zX~VP#P$(31$Uxf*?};aPdTN5;P&f<%)rG)xwV+UhjsZef7xd2q=DDMLc_XkER{uET zt&m_}27`uxKte)7v_o{XsdOI*%)r0^0)<21a4jA}E09HD;F(&KK-J$07Q{dTokU}h zs1(pIMZ709h=Bz2LjBJf$h3cDDS`jwiI*`56HkM{w4uMw^c!ev`~O49CbG!PuFtq0m? zizkpMzbyOzrr6pdY$$;YJcU5Cu|R@(BHAR97sATS%0LGSgX`*;8o*$d=K4?=%=Dm{ zf&L+jL#Db=z2965Dj|qWq%eMSz5dJ9`6KsNJCJES&lW^FDVXSGMW>QMf1esb`g1JM zKkEI%_4;!xus?DkykH=|YWu%x{oBNApkLE}TbH-^xA}<_UdPjUt$l7jBboPGY{j4* zEqUY57+@fIgLlscFg6yZj?96SL>n;xBqVV7=g*4MrZFc|8Zg#q#Rey{Ywmgxt<5DE zn>{g#)i*sWWaOWVf0#?<-q|qEZthKQh$%lzIx_&3hz5B~zOjwq1H8`xkrFLe+<4l6 NjisGMnc1oH{{vTjXlVcd diff --git a/applications/main/bad_ble/images/EviWaiting1_18x21.png b/applications/main/bad_ble/images/EviWaiting1_18x21.png deleted file mode 100644 index d39d2173329d5317fc2cdfb18738922a5eeec6fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13020 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%0DogpcYr`$ z++qJ}>3@a%wq1X@`yto-TOabSio8kl{&AQ7JzRfg^#9`P_cZuloB>JwpGp2Leg7lZ zf8_eN6!^Em|B0^u$n|e2@Na?t6J7t`SN77L~l0$9an+)c;9a+DZRZZS8{^eP~v1|B4dJsqoRU?V`GD5l`ss3 nQ0V!FVYmG zH|f%R(Y?>!`@FaBx%Z6m?tdpEZO!$~Z>{yMIp<1#;}@m*P?4CBjt~F<5GyOmX=6Sq zFt3|fIGA^LT&`CD0F9Q9t|3a>$`j~@aJ7Xy!GI`lHy9A+1-AtNyk^SM4G?UNPvw8` zMKj5?<8%Z+_Tpdrd@=**N>YLC_z%P6qVG_O*k&ZdCw^SnW&HT8fnZN1Ln;dvL`dd_ zZ_sIcME;mnJP$iKxcXwgJ6%yhPjBHWV!khScJ63gF(_;vFWHjNv3YcLgih|>6#0q< zo=fa~Uqai8XIIF#kSx#n&$R>yA00hCh-Xdv75om~hYTlc}h+plGT1A~ac z<)ekO_kouviGAr!w!u8!)uS`<0EKt`SDS#A>`SV5c1`9dlI>P5HWtiEl3nXZ5Q6V# z4T0J3{f!dQyZnuV_dm|BtR7@*eEa14w0UyQjHrjK{BUNhTYrFG;zM|-q=o85|3$pm z;Hv9aSMM4A4>LBUb7IdeO3-qCpDc@&-hKtg4-g}ACZh+-8$PFm^nErT%My`SlAEV0goU+uTlYjJQu^kaL07k|bC=CZBhl4(f0 ziMVI65t%OH(Oc#=&{WsGT9dkgzRH#hkTv4boweg=}h)H^Ha4yDvOL)4YG7+A_mwe zXQO#@JCjs+3dTlCN||oO6c(mCzN~%oUfIN+q4eAL_e68(1=)D8-b$?Y$&Wq{^u)iV=_h)fLUu{#UGc@!p)sQN$Va{Uyk=#~w=!LPGOOIxm9-!ntZI+; zVde9%ljNHo%98ie9%?^RDR*WhYK^^QprCuf!wRV_X!@%+&4Ux*n(l=GWo=f&sF~grAM9Jb`(wH^Tv}*!)rYUF}%kj z;*L^`TXP?c<_Nhr`Cyc*adS8|2P|x&4Vl`^=v@azVlqF@GD#3rsdQ)NP;l3GVQ@L8 zpr3j_D-E*m6C+SVd;Otma%g^&Lw?+KvvMaMnjH8JI1=HQxiBzq?d06i#P|w6{&tMs zm*s~grTjA#ld~?Bdd$jn#wGY4m%Vu?_c<4T~K1q;FX3Q)HtJQLUyek}Cvk zV!<*weJ~IvtV1N=TwTu%%&*EA*GVl9{Fpt7h)sT)#($z9KW=2=N0)uCtXNm%Lk(K% zR8080zrq{GrSn*CgC7#m-MPm|84cy}V!6TeoRM1)vPuNMRb}kBXXz)Cht#swE?tGd z?M~75m&jG6mXT@~tPoB2stN6*sJ7_CeH|2?@ckl7k%YM6OurmUyLn4SGVkFZjW2z+WiTO3eJ`z(I>8D4Kr zZ*)|CC3mY5fWmMW`^oX*uAfWpw&@$+F+*H&>+A00-yW>ai8n|n_ub0U&tJlGuCpDE zmpZ{wV7~!|2$LgfUgN&JcQi>nRI2-~hDkClOUFf9jtMOYHmcscnCqfWDSa>D(gB*;d+Av zOFJj6k3bL=|BToc!}`OjYUZ*qbIQf|A*TeTrDAnS0;rn(;F%1I9!zukWXn4xi639y z94^HY>#6G7{#{C>E{UAqBnZFwWpLpx1Hgf{CO*BKzMBQPAbmvWP^iPgADL|inNnj? z;*R1dF9or^!ww=&fz$!%Gm$AD!?mz{Y$&g-3f&-*Q*+?!FG$h|c{@u3pQw;xNY&K1 zlqH?lA)t)pU!u1UXzRlX)|7Oj^Esfw8fBOu!g>G?{xFe6Km_eC&GK$_gDxImBh%m9?*UXLS(1i@lcAWGIe+@qLLE>UF|sWyR}y-z?S-u zvT__5gx@+-s&%6=-GrrD?)<=H*VFuY&(qIG)qw)`1p%z&Xj@qO8F2Cj1_83FY3_tG&%{DPbpC*KY4FxgKnn`C^|t)9>w8b7s-#*xcoEEbT)pK~~tCb=msX##j4 zWNo!V{>*WL6`#6_nz;RZQMP7H(t^+Q&_l5|I0;ZC%%+5%&-&=@p-VyZ!gjqj^LQp8 z*BuN2=CE7cQhCsZ#D1YV67nof?ZKvqn!gu>lIjfn^xlgYvr7sdx1w?g?Q039q7u|i$|Y>@5R&`U>A~vIFfw+>Cn&XppwebPL_i*2L`PbL zE)@|jXnjWt+hv)HZtc-h>h`;Ja^@RpvU4Y=v6?K^3JwZ*{);_+jnMS1y0#D?)Eq6tTOvP*R_*rO1O5F2b!jSv-HeF zu%h|Bq5{sFT*A#HJaj9p&Wr>9awgagNFCZjCd(PUXv#cnRj0{<=Q{pw_jEK|x(0~e z=4U1EtTDo~6w?YJkXaAedfFFu=V5$syS2KhW>Tlh3$~)OuI<)ljXO9^5AjEgTD1CV ziOWcOKH8~wWA#i9+h`D#dLM_?8EqqMUwrSDq$dBNfF8PwbMM9}WAW`nnMrvgtud6e z!Aw#+ZLKH}=R%2AUgea({Q7u$NwUZu1$KWTm*aWZuj$E1OB*_Fon0WA5zPGyl1dX55X)vo##ip+!FqXtP0qJZ zTKv8=EeyP8(siL7%_Vvzd;X%uv;oF<8sIwkfDkf7AR7p03CWpAL~?)sz%Ie9y|JVw z5EkF=mDAF7&5OnTkQv{5t5+q-Goe5Dhp`7UDYvw=O9GCd1`DYdo8-%p6-|Iv96_!* z-SKFIZNhW{IeY)7#ew_tkqPNndQJV3XN&i9-o@P|c`}vu!G$t4UZ4PRPRAb1hrdJ1^;33EgepeYBSM%9g}65R5MjSjT$R5{?JfJtHyp-+EY7 zU0cdi_3841gyyk6f#7@*E?>D0Ht68=hQU;exZm`k47s&iA2kCDp4M0GTOdv!Ym)|a zTSHp>=SEW+plW0DwZmZ)k-XQ2b7v|skfbj!q|7w%u&R&|9i*IK7Lao*p|oH^&WDNr zHu^=w?arx9cQ&;%%B%ZxWCk176snJ=Sg*J1M^@ip#ly23(a;d|rmHOVvKk zMI4r0Y0VMv1vTN)Oa+@R+qtV7U!_QUz|`h)m#epEuEvvoV7)Bs?5)&Id3l%Qe>)=x8R4TImi3B zL5!S>sN*a9RWj!D6}i{F`F9LdN}L*uGP1uK(OrLd+{emd0OTkp(=ydDFEnaN&n{39E-IovC}l-NYVDNp ztba^uUOEi*x=YJ+v^(Mn+q6;#E<|wH^(!0K+bw8-s0-N(e894S4xw`7fx~;;P(hOJ zghW9<|8d2#>>U2|1Y#H33;uUk#V{`064( zJr*4_6XKm^sh;p=XxX+;cu&6~G!v9k(%j%N#BK@`wmS@J!vcd+$E2Y!ZAI4)RA(0e zbwZkl_li5zz;0?e1eEB7{+_e{TdeUp%kBRd=(!^>Orb%!| zir&*iI)EX;UIRBhx3f?L2*ku%IRWSZ%)FGvDEZho-zMTthusX@;CJ}MsLe4@>*F)o zihz6vVVOn6!lce!g1ug(hiX{|;N=uG$4kMXo5DP@fauorm>5r@fs-meNMU4pYfhU&-W^ER|FV;tOu2XdR3D{6n z-XSw}seRRvHDN6(s2G^ID)tm?v44A+kv-r;bxw=I^;B&YRZ$tOUmF^fzc$g*XTZ1%>?sM$z%aRLIj^e57nWG z0bW&9B*3p~13k9U8`1cWSG?M_UfhPvJftit_UIkWfG#8fJdIn)k$i(yfcaY z-Y-Y0V7pCPzDti*v>SZ)#J`k%ULRKEB$vP~fB$`sby19j@O8kQR{>A#-0Z`2LEZU6 zf)2TB6r^s?-LRg;w55=0>rax8enQLiP4Ade_2PRen@A@l#U{@|HG&vFWY05{oQ-Oo zk^;tVE`#}6FQQgAJS*)bl)nYnZ*+P^M*>7zxzUl-xDA?jh%E-a?Qxn=jBd{64XHMR z%d;2McZv;?v|24UX@eupEB#1`HY~Ot zkCb~UkjLES!0ejp8Xe2}eJ0>++R6;E#^dp7-z@7chCxxjre%_&-DI@ZmPL7HZm-sJ zoxau9xNo<6Xzz=TvV|$KlWV47jVCIWM(8CHHmedyeR=kGt5Lv{aPTZe9Nwy{r=Lo-&2+a3o#|T9t41bXHmwy;>4|lvpgsxhD0T zdu>{V9I=+}g9F0-P-N40cvc!kS+H}v`^s%-^pjmcPFnSdg4}aCLG&_9E^d@_MfpV9 z4nx9Rj$DxW!xyS)N?ZIAG=U0c7p&LK*TghN>4709Uorm-9Xewj@WsbHb+|s!$rb4iY4u=cTxn(>kuv!q<}63X5os z(qc4rCT_rMN340h;{Q1F1^-mv{`3;kKQ@kQh75g;Qr}hzF|fYYP?zU|xYn>9;eJ^K zKpG^h)f7fely955iRR66a4hF2k0`A|y7o4EwN}jI8!n%|Xj;^L*H$9;%`5$@mDL%| zwGm>Tyv|Md2kr(Ig-jW44xxN}qt*L{EnMSI@IP9xP11I`rhSn@Op=D`ZF<=ycr-(s zYWvfc;ADbKm7Af}{k3p&gO}Zcl|rJ&wxsZm>yv7TLILI%J$XcETzex0u^d^orENpn~f3U%IOJR z3o)4#Bp8cq7+6hi@D8Vyon(GM;b>3ioa+?Z;?k01N7mTDp1&DNga}%ZCc4|D*w(uN zPpat(c!@348UUiFmWd0_V#u`h@==`XIA)*?9kci-aN$sOu|9mU^VmXs{^9~6 zj)#&}a4>Bkx+kGLw*hIk&)XL#(g~vD6dU6;3H5tpEF$!U!2!nv4(1d}EvVw2BMJ!| zeH>&ylwu|Ob{C+Xbh>wwQ|6A60C+x)ukoEBlUa?^}49TQa@FT?7VpRGU zdJHBDu=v_iDD_`4pwg!48C2(AYRuGhRzBZx2(AymXu6S7c(9j`su%Z*>S)Wcta_;gpTm;gkw(XWWvRKC@wI(d!lH0=UjYl&t}VFrAQ9Pm+qVg2eW~ z>0-M9w&Ra$Y}Xm}wj%AHPdj_MOvsj~&DkrB41HdYoy&M{b3o>PXcxxgk0kR8nOKo& z*u>R+MK>VY3CbskJ{Cm0&~`KQdN2n|dT?xHu-4g4z;ZhyQGWHjihlpTA{x2h9#X`Rr`A^!65=OM8Q;MH!!E`L6A@ zuR+GUH%IFmwLkEL3l-;DgMl?=_TaAq*ir#DXL)f*972^^O^@3J*tqt&?e%E!+c%@Q zC-rIP#rP7)%_P<7cE=?lLiVw9q;Ixg$2YQq-GGKqjQaFq<{kz5)raRnn@ht{_E5?3 z*?Ri9l`faeLiE(RFSDx`$JWGFJ#@K&HgdJ2UuwwcS*86QHOjc6s{5m7V`hj!@<+}wMnUM_H-xu-zUugd0tz;7A)CZgH=^OU9d&0y5| z@Q@y@+02Tj1?v?qv1IG(;5_jhrjYFS1rZz%-&!~+B_=BcT0xFX;W_9j~~n4xB2)Y8>a5r5aV_3p{L*1G4g}dohfLb8==Gq01)!QWo0#$Wo7?y zaUF9>J@fffPhFj9>WIN+;bzSwvgjv6j`rHvO2o}di9GlC2*^}g69BA2a=LDET8V|% z!5oW0d9sO>tkI9Kv?@2R(aMdvyHU|URkkUkU1;*&?ax?XJ_&HlZXTSo+x4Gl03=+5 z;&6}c#|g0LR$VPaeX4++hV3^M>y$=$O4Hmx0{nh{ zZ&Wh24e%rTNxcy(A3!rTc6I-B+LV^2M?~Bplf6!(C-seh`T$5}fYm4jte=eB=Q8m- zPJ@w(9)QEdXnT2J94qg~hc{|fqu|Aj*5fuSBb33*PM<|M!*2r2HnB7tFC+Rbc>&Q?Eoc&dccunEbM4MATz2I3!dz&NO)9QZAT*1%T! zg)z1O5Fjswvmr&lhC|tU?M*BBTL9@I4z@H$lT*xQIN6j2!YJ21HKXt{{`> zb(?^kr8I#e9~&+;lfabETpz8JSoFb!3Qu01PgCs)(MRT+kG{Nknya5HlvkpKulc-K zVoYz0!-4RKTwTHOMgQ=?8UV!wp?x01qhOGeF~qo5y+(d0a&{9hG#47n7k4XAjp5GC zYV2y-YWmSQ(MPoJW337+4PQYf7&fDcyAZFfo%r4m`jYul_~Jf@^ABbFf^vH!Vqq%w zF!Fw<-lDPFT{iq?Yu+!~D(A#iBWidH14F>iWb}c2_+d`bdw^^K7w(mY?onvhVCif`V zkRrs4)aGSe?qstp+f40FBY+?Bq) zq%iMMiYn{WGA_`~L+P%EkJNzmrWHhWMPo zXux#p$IxR68%7%<#IpQ_xI^0Tf*M!*&>GI0(b3OUYE-gR9)EPd$;p^YKa8`seh5CZ zIzyj5(BszA&}-SaGcGiKzocS(rP#ap+qlZu%(%kX{7e2Z|FO}rx|g0W7e=Mvsqiqz zWrwX{k%_0X6{x`<+pSq0EVqiT%-!BPAn{yd&L=^6lK` zCC#$uVaNF-dfn=ZIn}bO`2)~!!j9y`fnu9OzCx5B`V!6FaMTdm!0+?kN6Y7t&$iFW zA;B5feQ>dD*8q=HS{r=$efNu41{YE{)bknTBnV@}u3PlJt}zC#gT zD8K4#BNq$?g{SMrJwFc4yFOar46F%E_#yOz9?KYOHfSa2rBruoO0d6leh#kmxHM@< ze~5gWIE$N-<%i>h#slJ2qE*jFAwxk!+qoz0u^mqz`7_56kP=l81m3cu)FK;x7t~SeN9zV|chG3^Q9!Pbs?CGuag>hNJkRxTAJOb2IS<4o89tYE_Hg zI_>c6P-fw~;=3m8e&k)%xLtoI<*vAjhx&Y$SlX8??ZkcER%%_MtfI4`iGr4gMCzZi zk2%HfG>hXrOH6gwRU$7WI0x7AAAlpO`>La>^LsO&ZJP?Q6H*AmWT#`Q8oLCh2gias z<*LlV=}S*_k`L|(Lr#>k5LqBs%lhek_1?St{s-^OgN@tFhD~xzUca$K6|8tyA%* z$Qh0)k+<-j!V-06RWEiL)iTxlDhsYFE-j}i{RSx({xeB6u-ARLPJ{Y=`kyB@Kh4%L zav1Oo@-ly7s%l2!Llt}Co;`TB)ud@#f2zOeIg^lr@Nr=OSwp>Y-piWx zj)2br>%oyY{{3myCSdQB6w zN4=oTez=mIDUpKSSYKZrg*L1D>}E!ZBg=T3T=$%YrVm#A2A{8=Y)N|0A6--u4ba4y z+n8-NEW5o#wg%)Z;h!@@@EGwPL@-1&@IElxq0JKZ`x1SgGHTFVk=01;wmtIUBJuO4 z)}-sa(p#41p2qKM`e$XUbx4=NStBHkf8BD{NOSag`U!o2z-6V`22fmLLmaYa*%tZ$ zI$L>H687UTplOZeyH!`%fZPGwC&lhN{&#sL%}>29lqZtYin=84|1dr8Tb}lO=XC)F z-FzT;WqNoqT9IK<{BG*U$PR25HFbDtKwDvJ{;WwW5V^>`>HMQH)svFHez$V>zGP~^ z+V@+B-*$>(HL?S6PQnh(MRPvY0{~>?aLk<BIXk%^AztFFzjz^-??27FtiWFol%qJSp}Hne))fH*3h)TL3nvRJw16m`FUIscD#I|qN2PYFfSO)jX`iDyc15GaSy?gtz~9L+y-;~CjPdsYezt$nBT+WI z%9w@#W<3}HFBk+8;Rb=Z!J@o>^~dz8tN-Ecg8W@YjGnw+R&Km}JRn|Y=YR4*qU1gP z9Pe*Ekh+)~WO%hGCXdvL2e6Vu*yP1G&p&%$9KZqL)wHD$Q011G(twlim+`?dMetteE zCWiQIeo_4t6C$mtEY1q%0sU#wbh1L(x+0v#S=Hb!XsZU;NEE_b`M+7z|AiC#Efke7 zzOD%Gzl^T~bN{XNTlsN<|B?y_{FU+$E9g%Z#aWS79~&wngW>}P#YSBLyn zfZsKdgCXFU)hQrcoqw(USEYj3{MSh<^e>YZBM+u@LH6Z_Ntd$Rm@?tg{-<}K^$=8Y*D_9zWcmw!3@-vEDOP=iBZ zE=bpZHTAzje%r3UoPL-&|7OFSs+dEX_m5Nh_i+80(f^B&-_ziKaRdzYe+K!t^!<-q z|B>t8QsCbL|0lZsBiFyBz`q6lPjvl%lZ)`r|M@T%%nwjc%)j`s<=w}aD*$+!YP#~6 z&mYl%ndHkQ%r`7+RYf^KBrc;8ra|DQWP}6&h$w%)umI_qw=hP0l(M=!{#PQ}8xTsb zL|1)`38E|~t$TeED)M6$&~Xt+a%2C;ty^ARw-$4P+pyxKrQ{-mrKJEN-%mBi83670 kvG}(nK+Kk>9f1Hq`XyQ5V~1&c3=2S6{-IpCjAh9G0TnlF!TKKE6}V>ZHq@`3;W2-_kpQJiyI z7UAdNyp=;k(>b~Y-onDs*1`frp^|;@1RMY``m&vZ(Mc=P#)B(3QCIWcxyNaQ3jkP! z#os9Ao+ow#z+4g&H?5C~7Ic-A+MR`Tsk_)Mh|G>2$=zS>Yj!RAM2yQRgYQZMh0j7m z*t4_iAJ;1jQ+@EgXq zC+;(gsEir z9B?+nG`+YsKXUUdh7?qSikocVHHVQ_CRMf?qQW=h4s?#_K0c)(b!{GfwcHVG-@tr< zO0-mNN*NKK3fG=jO5GGl3Kj-fmmO-4J>U7Pg-{#zor^n01l<(`n1(HCUYFmM2#ERW zYBN^=AgN|G-c+3nXiF;?1%S%0vZwAP%hLqPsb+G{weVivXODQ~b9ZM9i zODszeO>nre-t=ayOcGunJz^)2w+~g2EPg}}XEM%v=a#m4dhY$>ZACdB`0q!5b0NkJ<|9zBfg0l3maTmk*woN^BNSfv32p zgr!X0cXpwgG|_zA<6rDk99FE~7t&YV=QT^6 zm7bmMV+T^QO|vz!m*7PBN}xc+p^D=bQ5F4x9p8-KW2c!@0!z(HB2%9`ZI*~X@|R@3 zv3uB^O8QV;{zD0J1egJKt~CMsHQ*+`ymRi-@V(tFeieyze)*lY`G1wGeZS&6s>hHq z_F<9tc(HW9;06MWFkX1={(`!K`myQ*ZOH){J{6s%@9C|rjT!^2URKjgoMCrXKn|w* zQ)T}~Y}6gAtokD{hhiF1`%=%h@TJw+?ggOVeX@dvsNEIE<-U^5#tf-@O2J7x`+G0UpU#N`gf);;2iJ%{Os z&r|7vtw97z<#fTY?wg{zqBm1+z6!p#Y1xZ-Jii3QZnw5Nxv_b1UePv#q1qg_(t~7ZWBSr-M$r zpI)!p>+m#7EvE-cZ0xT)IaJkh?hM=*fm>^z;xT?}%zZ25)|KkJ)!;!ywMtk|SbEsb zjhywvYb~4cKWm1W3)1t)_|9;>;u`0wx*&hyDNg{85^p;1ot=7ov3xIgK!l8?%!LGm z-Ib_G$xYKH;B3^p7z9KJQ8H?2$6LdFC^J3N-SK;jB>E5&Wp`E{boP^`VRBY-Rz6A?pIWN^5n8st%)v)p zJxO?sIYDezNV?LzGN!UhTkMx2MA>!bm!e7>MWpQ@_tY6DWlu87kyXeOA9MzNw!W>Z z?7Ap@zAE97WBH?}z2yw|`5|zP)k|rW7$=!vEtm(Y!kdAg>k4i0Yk88o={SN$xh(iI z2NvQ`kzIH0Sg2og(@iN#ZfYTw{5bIfD}_n%c47Ocb)R$%KPZ#p$)d3CmHd2UT|P15 zcFeQ;{1TJc*Z1W}S@Y~Pa=Kue#9DE$d3dDC->m!HIr!Y7NTj0F;VdBglQ3vl+q9C?^hzyfzzu_1ECt^XEn#zusq ziMfc|RqU_V^RpFCE80|-{R(5lMzMhndhu>Kx!L;>^Q&VQlST`@3v;rMnIJx-4=`8k zHqrw;j(b%3T6E-?$k4yrm3gi-EqBF7u_u*^)wcWIjKR;@D++tR--_9tMk<>o#DOasn`Wdu5D-$gE-EP1m zB%bkbqq=YN+s-x7Z{ej|k8!ocp`T}J&vd+T7iEhGT*=u={`LOKaF>lR}A(<}lY^%xT(#$-&K$^`jl=jo!Ikt1%rlCCs8lD*HjvLwJKq zgHmNX6ES~xqqx04lwHoQ7;LCgO5eX>y5+doxrS{heCM!YJb8X=1F~{yaXrFNbvNf$ zDMzK8~BE}bo!eP9bXPabH;`QU=6*& zCdMEm2Ao4c#L+Pz29ZFb!5Ikf-@I^+ylsYnL4Ui@0}$ZX;eAU{)3eP#DbYkh!6OzNNmI zE>!O~){=}3B;rW)-&pVeV)g%s-ChSGg%jBlN5u!>ysfEZBIxg`;rKu2qWed_f3V(v z&PDH!SO_N>$o6di*Q|e=I198b{=2#y<=^GUkvJPq<*as+(AH7TZ?VJC&e@7{{<;SA zC2ex<*?_*SrI|CMj}U7n2!Mp%-1rrKqIp1a{+*U&-|gJ9U_si^SW0@*ROzlyv?|%L hsb8nFi0Yo)LV!dcVD^DN6V9OkY^{!2mYAQp_&*x=T!;Vw diff --git a/applications/main/bad_ble/images/SDQuestion_35x43.png b/applications/main/bad_ble/images/SDQuestion_35x43.png deleted file mode 100644 index 9b9c9a58e3257f926677533f8cc99ffb19dd74f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1950 zcmcIlTWs7!6g5TAQV0(rqPBcsIS921UeDNG@7i=zCA)d7sMC-vG>8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H diff --git a/applications/main/bad_ble/images/Smile_18x18.png b/applications/main/bad_ble/images/Smile_18x18.png deleted file mode 100644 index d2aae0dc37f4fd3453e3254e4a9cd33e753019b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1080 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 O0000gN=z( mnGc5;aHhO3XW%q4U|?wcz_Hx?k)a$=ErX}4pUXO@geCyYPARJZ diff --git a/applications/main/bad_ble/scenes/bad_ble_scene.c b/applications/main/bad_ble/scenes/bad_ble_scene.c deleted file mode 100644 index 351bb1e79..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "bad_ble_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const bad_ble_scene_on_enter_handlers[])(void*) = { -#include "bad_ble_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 bad_ble_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "bad_ble_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 bad_ble_scene_on_exit_handlers[])(void* context) = { -#include "bad_ble_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers bad_ble_scene_handlers = { - .on_enter_handlers = bad_ble_scene_on_enter_handlers, - .on_event_handlers = bad_ble_scene_on_event_handlers, - .on_exit_handlers = bad_ble_scene_on_exit_handlers, - .scene_num = BadBleSceneNum, -}; diff --git a/applications/main/bad_ble/scenes/bad_ble_scene.h b/applications/main/bad_ble/scenes/bad_ble_scene.h deleted file mode 100644 index 25b19fc4b..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -// Generate scene id and total number -#define ADD_SCENE(prefix, name, id) BadBleScene##id, -typedef enum { -#include "bad_ble_scene_config.h" - BadBleSceneNum, -} BadBleScene; -#undef ADD_SCENE - -extern const SceneManagerHandlers bad_ble_scene_handlers; - -// Generate scene on_enter handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "bad_ble_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_event handlers declaration -#define ADD_SCENE(prefix, name, id) \ - bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "bad_ble_scene_config.h" -#undef ADD_SCENE - -// Generate scene on_exit handlers declaration -#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "bad_ble_scene_config.h" -#undef ADD_SCENE diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config.c b/applications/main/bad_ble/scenes/bad_ble_scene_config.c deleted file mode 100644 index 0987b153b..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../bad_ble_app_i.h" -#include "furi_hal_power.h" - -enum SubmenuIndex { - SubmenuIndexKeyboardLayout, - SubmenuIndexAdvertisementName, - SubmenuIndexMacAddress, -}; - -void bad_ble_scene_config_submenu_callback(void* context, uint32_t index) { - BadBleApp* bad_ble = context; - view_dispatcher_send_custom_event(bad_ble->view_dispatcher, index); -} - -void bad_ble_scene_config_on_enter(void* context) { - BadBleApp* bad_ble = context; - Submenu* submenu = bad_ble->submenu; - - submenu_add_item( - submenu, - "Keyboard layout", - SubmenuIndexKeyboardLayout, - bad_ble_scene_config_submenu_callback, - bad_ble); - - submenu_add_item( - submenu, - "Change adv name", - SubmenuIndexAdvertisementName, - bad_ble_scene_config_submenu_callback, - bad_ble); - - submenu_add_item( - submenu, - "Change MAC address", - SubmenuIndexMacAddress, - bad_ble_scene_config_submenu_callback, - bad_ble); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(bad_ble->scene_manager, BadBleSceneConfig)); - - view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfig); -} - -bool bad_ble_scene_config_on_event(void* context, SceneManagerEvent event) { - BadBleApp* bad_ble = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_ble->scene_manager, BadBleSceneConfig, event.event); - consumed = true; - if(event.event == SubmenuIndexKeyboardLayout) { - scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigLayout); - } else if(event.event == SubmenuIndexAdvertisementName) { - scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigName); - } else if(event.event == SubmenuIndexMacAddress) { - scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneConfigMac); - } else { - furi_crash("Unknown key type"); - } - } - - return consumed; -} - -void bad_ble_scene_config_on_exit(void* context) { - BadBleApp* bad_ble = context; - Submenu* submenu = bad_ble->submenu; - - submenu_reset(submenu); -} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config.h b/applications/main/bad_ble/scenes/bad_ble_scene_config.h deleted file mode 100644 index 73742d9f4..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config.h +++ /dev/null @@ -1,7 +0,0 @@ -ADD_SCENE(bad_ble, file_select, FileSelect) -ADD_SCENE(bad_ble, work, Work) -ADD_SCENE(bad_ble, error, Error) -ADD_SCENE(bad_ble, config, Config) -ADD_SCENE(bad_ble, config_layout, ConfigLayout) -ADD_SCENE(bad_ble, config_name, ConfigName) -ADD_SCENE(bad_ble, config_mac, ConfigMac) diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c deleted file mode 100644 index aabb1b900..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config_layout.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bad_ble_app_i.h" -#include "furi_hal_power.h" -#include - -static bool bad_ble_layout_select(BadBleApp* bad_ble) { - furi_assert(bad_ble); - - FuriString* predefined_path; - predefined_path = furi_string_alloc(); - if(!furi_string_empty(bad_ble->keyboard_layout)) { - furi_string_set(predefined_path, bad_ble->keyboard_layout); - } else { - furi_string_set(predefined_path, BAD_BLE_APP_PATH_LAYOUT_FOLDER); - } - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BLE_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_ble->dialogs, bad_ble->keyboard_layout, predefined_path, &browser_options); - - furi_string_free(predefined_path); - return res; -} - -void bad_ble_scene_config_layout_on_enter(void* context) { - BadBleApp* bad_ble = context; - - if(bad_ble_layout_select(bad_ble)) { - bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout); - } - scene_manager_previous_scene(bad_ble->scene_manager); -} - -bool bad_ble_scene_config_layout_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadBleApp* bad_ble = context; - return false; -} - -void bad_ble_scene_config_layout_on_exit(void* context) { - UNUSED(context); - // BadBleApp* bad_ble = context; -} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c deleted file mode 100644 index a9ee7e34c..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config_mac.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bad_ble_app_i.h" - -#define TAG "BadBleConfigMac" - -static uint8_t* reverse_mac_addr(uint8_t* mac) { - uint8_t tmp; - for(int i = 0; i < 3; i++) { - tmp = mac[i]; - mac[i] = mac[5 - i]; - mac[5 - i] = tmp; - } - return mac; -} - -void bad_ble_scene_config_mac_byte_input_callback(void* context) { - BadBleApp* bad_ble = context; - - view_dispatcher_send_custom_event(bad_ble->view_dispatcher, BadBleAppCustomEventByteInputDone); -} - -void bad_ble_scene_config_mac_on_enter(void* context) { - BadBleApp* bad_ble = context; - - // Setup view - ByteInput* byte_input = bad_ble->byte_input; - byte_input_set_header_text(byte_input, "Enter new MAC address"); - byte_input_set_result_callback( - byte_input, - bad_ble_scene_config_mac_byte_input_callback, - NULL, - bad_ble, - reverse_mac_addr(bad_ble->mac), - GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfigMac); -} - -bool bad_ble_scene_config_mac_on_event(void* context, SceneManagerEvent event) { - BadBleApp* bad_ble = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBleAppCustomEventByteInputDone) { - bt_set_profile_mac_address(bad_ble->bt, reverse_mac_addr(bad_ble->mac)); - scene_manager_previous_scene(bad_ble->scene_manager); - consumed = true; - } - } - return consumed; -} - -void bad_ble_scene_config_mac_on_exit(void* context) { - BadBleApp* bad_ble = context; - - // Clear view - byte_input_set_result_callback(bad_ble->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_ble->byte_input, ""); -} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c b/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c deleted file mode 100644 index ae9d16ba4..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_config_name.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../bad_ble_app_i.h" - -static void bad_ble_scene_config_name_text_input_callback(void* context) { - BadBleApp* bad_ble = context; - - view_dispatcher_send_custom_event( - bad_ble->view_dispatcher, BadBleAppCustomEventTextEditResult); -} - -void bad_ble_scene_config_name_on_enter(void* context) { - BadBleApp* bad_ble = context; - TextInput* text_input = bad_ble->text_input; - - text_input_set_header_text(text_input, "Set BLE adv name"); - - text_input_set_result_callback( - text_input, - bad_ble_scene_config_name_text_input_callback, - bad_ble, - bad_ble->name, - BAD_BLE_ADV_NAME_MAX_LEN, - true); - - view_dispatcher_switch_to_view(bad_ble->view_dispatcher, BadBleAppViewConfigName); -} - -bool bad_ble_scene_config_name_on_event(void* context, SceneManagerEvent event) { - BadBleApp* bad_ble = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadBleAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_ble->bt, bad_ble->name); - } - scene_manager_previous_scene(bad_ble->scene_manager); - } - return consumed; -} - -void bad_ble_scene_config_name_on_exit(void* context) { - BadBleApp* bad_ble = context; - TextInput* text_input = bad_ble->text_input; - - text_input_reset(text_input); -} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_error.c b/applications/main/bad_ble/scenes/bad_ble_scene_error.c deleted file mode 100644 index a212f2087..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_error.c +++ /dev/null @@ -1,65 +0,0 @@ -#include "../bad_ble_app_i.h" -#include "../../../settings/desktop_settings/desktop_settings_app.h" - -static void - bad_ble_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { - furi_assert(context); - BadBleApp* app = context; - - if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, BadBleCustomEventErrorBack); - } -} - -void bad_ble_scene_error_on_enter(void* context) { - BadBleApp* app = context; - DesktopSettings* settings = malloc(sizeof(DesktopSettings)); - DESKTOP_SETTINGS_LOAD(settings); - - if(app->error == BadBleAppErrorNoFiles) { - widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); - widget_add_string_multiline_element( - app->widget, - 81, - 4, - AlignCenter, - AlignTop, - FontSecondary, - "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); - widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_ble_scene_error_event_callback, app); - } else if(app->error == BadBleAppErrorCloseRpc) { - widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - widget_add_string_multiline_element( - app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "Connection\nis active!"); - widget_add_string_multiline_element( - app->widget, - 3, - 30, - AlignLeft, - AlignTop, - FontSecondary, - "Disconnect from\nPC or phone to\nuse this function."); - } - - view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewError); - free(settings); -} - -bool bad_ble_scene_error_on_event(void* context, SceneManagerEvent event) { - BadBleApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadBleCustomEventErrorBack) { - view_dispatcher_stop(app->view_dispatcher); - consumed = true; - } - } - return consumed; -} - -void bad_ble_scene_error_on_exit(void* context) { - BadBleApp* app = context; - widget_reset(app->widget); -} diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c b/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c deleted file mode 100644 index d60f6a246..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_file_select.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "../bad_ble_app_i.h" -#include "furi_hal_power.h" -#include - -static bool bad_ble_file_select(BadBleApp* bad_ble) { - furi_assert(bad_ble); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_BLE_APP_SCRIPT_EXTENSION, &I_badusb_10px); - browser_options.base_path = BAD_BLE_APP_BASE_FOLDER; - browser_options.skip_assets = true; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_ble->dialogs, bad_ble->file_path, bad_ble->file_path, &browser_options); - - return res; -} - -void bad_ble_scene_file_select_on_enter(void* context) { - BadBleApp* bad_ble = context; - - // furi_hal_ble_disable(); - if(bad_ble->bad_ble_script) { - bad_ble_script_close(bad_ble->bad_ble_script); - bad_ble->bad_ble_script = NULL; - } - - if(bad_ble_file_select(bad_ble)) { - bad_ble->bad_ble_script = bad_ble_script_open(bad_ble->file_path, bad_ble->bt); - bad_ble_script_set_keyboard_layout(bad_ble->bad_ble_script, bad_ble->keyboard_layout); - - scene_manager_next_scene(bad_ble->scene_manager, BadBleSceneWork); - } else { - // furi_hal_ble_enable(); - view_dispatcher_stop(bad_ble->view_dispatcher); - } -} - -bool bad_ble_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadBleApp* bad_ble = context; - return false; -} - -void bad_ble_scene_file_select_on_exit(void* context) { - UNUSED(context); - // BadBleApp* bad_ble = context; -} \ No newline at end of file diff --git a/applications/main/bad_ble/scenes/bad_ble_scene_work.c b/applications/main/bad_ble/scenes/bad_ble_scene_work.c deleted file mode 100644 index a2c009fc3..000000000 --- a/applications/main/bad_ble/scenes/bad_ble_scene_work.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "../bad_ble_script.h" -#include "../bad_ble_app_i.h" -#include "../views/bad_ble_view.h" -#include "furi_hal.h" -#include "toolbox/path.h" - -void bad_ble_scene_work_button_callback(InputKey key, void* context) { - furi_assert(context); - BadBleApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, key); -} - -bool bad_ble_scene_work_on_event(void* context, SceneManagerEvent event) { - BadBleApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InputKeyLeft) { - scene_manager_next_scene(app->scene_manager, BadBleSceneConfig); - consumed = true; - } else if(event.event == InputKeyOk) { - bad_ble_script_toggle(app->bad_ble_script); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script)); - } - return consumed; -} - -void bad_ble_scene_work_on_enter(void* context) { - BadBleApp* app = context; - - FuriString* file_name; - file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - bad_ble_set_file_name(app->bad_ble_view, furi_string_get_cstr(file_name)); - furi_string_free(file_name); - - FuriString* layout; - layout = furi_string_alloc(); - path_extract_filename(app->keyboard_layout, layout, true); - bad_ble_set_layout(app->bad_ble_view, furi_string_get_cstr(layout)); - furi_string_free(layout); - - bad_ble_set_state(app->bad_ble_view, bad_ble_script_get_state(app->bad_ble_script)); - - bad_ble_set_button_callback(app->bad_ble_view, bad_ble_scene_work_button_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadBleAppViewWork); -} - -void bad_ble_scene_work_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/bad_ble/switch_ble.py b/applications/main/bad_ble/switch_ble.py deleted file mode 100644 index 32ed65492..000000000 --- a/applications/main/bad_ble/switch_ble.py +++ /dev/null @@ -1,24 +0,0 @@ -import os -import re - -def analyze_and_replace(directory): - # Recursively search for .c and .h files in the given directory - for root, dirs, files in os.walk(directory): - for file in files: - if file.endswith(".c") or file.endswith(".h"): - # Read the contents of the file - with open(os.path.join(root, file), "r") as f: - contents = f.read() - - # Replace all occurrences of "BadUsb" and "bad_usb" with "BadBle" and "bad_ble" - contents = contents.replace("usb", "ble") - contents = contents.replace("USB", "BLE") - contents = contents.replace("Usb", "Ble") - - - # Write the modified contents back to the file - with open(os.path.join(root, file), "w") as f: - f.write(contents) - -# Test the function with a sample directory -analyze_and_replace(".") diff --git a/applications/main/bad_ble/views/bad_ble_view.c b/applications/main/bad_ble/views/bad_ble_view.c deleted file mode 100644 index 2690b6702..000000000 --- a/applications/main/bad_ble/views/bad_ble_view.c +++ /dev/null @@ -1,221 +0,0 @@ -#include "bad_ble_view.h" -#include "../bad_ble_script.h" -#include -#include -#include -#include "../../../settings/desktop_settings/desktop_settings_app.h" - -#define MAX_NAME_LEN 64 - -struct BadBle { - View* view; - BadBleButtonCallback callback; - void* context; -}; - -typedef struct { - char file_name[MAX_NAME_LEN]; - char layout[MAX_NAME_LEN]; - BadBleState state; - uint8_t anim_frame; -} BadBleModel; - -static void bad_ble_draw_callback(Canvas* canvas, void* _model) { - BadBleModel* model = _model; - - FuriString* disp_str; - disp_str = furi_string_alloc_set(model->file_name); - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - DesktopSettings* settings = malloc(sizeof(DesktopSettings)); - DESKTOP_SETTINGS_LOAD(settings); - - if(strlen(model->layout) == 0) { - furi_string_set(disp_str, "(default)"); - } else { - furi_string_reset(disp_str); - furi_string_push_back(disp_str, '('); - for(size_t i = 0; i < strlen(model->layout); i++) - furi_string_push_back(disp_str, model->layout[i]); - furi_string_push_back(disp_str, ')'); - } - elements_string_fit_width(canvas, disp_str, 128 - 2); - canvas_draw_str( - canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); - - furi_string_reset(disp_str); - - canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - - if((model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone) || - (model->state.state == BadBleStateNotConnected)) { - elements_button_center(canvas, "Start"); - } else if((model->state.state == BadBleStateRunning) || (model->state.state == BadBleStateDelay)) { - elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadBleStateWillRun) { - elements_button_center(canvas, "Cancel"); - } - - if((model->state.state == BadBleStateNotConnected) || - (model->state.state == BadBleStateIdle) || (model->state.state == BadBleStateDone)) { - elements_button_left(canvas, "Config"); - } - - if(model->state.state == BadBleStateNotConnected) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect me"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to a computer"); - } else if(model->state.state == BadBleStateWillRun) { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(model->state.state == BadBleStateFileError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadBleStateScriptError) { - canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "line %u", model->state.error_line); - canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); - } else if(model->state.state == BadBleStateIdle) { - canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBleStateRunning) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBleStateDone) { - canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); - canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadBleStateDelay) { - if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); - } else { - canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); - } - canvas_set_font(canvas, FontBigNumbers); - furi_string_printf( - disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); - canvas_draw_str_aligned( - canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - canvas_set_font(canvas, FontSecondary); - furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); - canvas_draw_str_aligned( - canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); - furi_string_reset(disp_str); - } else { - canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); - } - - furi_string_free(disp_str); - free(settings); -} - -static bool bad_ble_input_callback(InputEvent* event, void* context) { - furi_assert(context); - BadBle* bad_ble = context; - bool consumed = false; - - if(event->type == InputTypeShort) { - if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { - consumed = true; - furi_assert(bad_ble->callback); - bad_ble->callback(event->key, bad_ble->context); - } - } - - return consumed; -} - -BadBle* bad_ble_alloc() { - BadBle* bad_ble = malloc(sizeof(BadBle)); - - bad_ble->view = view_alloc(); - view_allocate_model(bad_ble->view, ViewModelTypeLocking, sizeof(BadBleModel)); - view_set_context(bad_ble->view, bad_ble); - view_set_draw_callback(bad_ble->view, bad_ble_draw_callback); - view_set_input_callback(bad_ble->view, bad_ble_input_callback); - - return bad_ble; -} - -void bad_ble_free(BadBle* bad_ble) { - furi_assert(bad_ble); - view_free(bad_ble->view); - free(bad_ble); -} - -View* bad_ble_get_view(BadBle* bad_ble) { - furi_assert(bad_ble); - return bad_ble->view; -} - -void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context) { - furi_assert(bad_ble); - furi_assert(callback); - with_view_model( - bad_ble->view, - BadBleModel * model, - { - UNUSED(model); - bad_ble->callback = callback; - bad_ble->context = context; - }, - true); -} - -void bad_ble_set_file_name(BadBle* bad_ble, const char* name) { - furi_assert(name); - with_view_model( - bad_ble->view, - BadBleModel * model, - { strlcpy(model->file_name, name, MAX_NAME_LEN); }, - true); -} - -void bad_ble_set_layout(BadBle* bad_ble, const char* layout) { - furi_assert(layout); - with_view_model( - bad_ble->view, - BadBleModel * model, - { strlcpy(model->layout, layout, MAX_NAME_LEN); }, - true); -} - -void bad_ble_set_state(BadBle* bad_ble, BadBleState* st) { - furi_assert(st); - with_view_model( - bad_ble->view, - BadBleModel * model, - { - memcpy(&(model->state), st, sizeof(BadBleState)); - model->anim_frame ^= 1; - }, - true); -} diff --git a/applications/main/bad_ble/views/bad_ble_view.h b/applications/main/bad_ble/views/bad_ble_view.h deleted file mode 100644 index ccbd1d97b..000000000 --- a/applications/main/bad_ble/views/bad_ble_view.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include "../bad_ble_script.h" - -typedef struct BadBle BadBle; -typedef void (*BadBleButtonCallback)(InputKey key, void* context); - -BadBle* bad_ble_alloc(); - -void bad_ble_free(BadBle* bad_ble); - -View* bad_ble_get_view(BadBle* bad_ble); - -void bad_ble_set_button_callback(BadBle* bad_ble, BadBleButtonCallback callback, void* context); - -void bad_ble_set_file_name(BadBle* bad_ble, const char* name); - -void bad_ble_set_layout(BadBle* bad_ble, const char* layout); - -void bad_ble_set_state(BadBle* bad_ble, BadBleState* st); diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index bb29d3be5..94717cd37 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -5,6 +5,9 @@ #include #include +#include +#include + #define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { @@ -51,6 +54,17 @@ static void bad_usb_save_settings(BadUsbApp* app) { storage_file_free(settings_file); } +void bad_usb_set_name(BadUsbApp* app, const char* fmt, ...) { + furi_assert(app); + + va_list args; + va_start(args, fmt); + + vsnprintf(app->name, BAD_USB_ADV_NAME_MAX_LEN, fmt, args); + + va_end(args); +} + BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); @@ -81,19 +95,38 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_set_navigation_event_callback( app->view_dispatcher, bad_usb_app_back_event_callback); + Bt* bt = furi_record_open(RECORD_BT); + app->bt = bt; + const char* adv_name = bt_get_profile_adv_name(bt); + memcpy(app->name, adv_name, BAD_USB_ADV_NAME_MAX_LEN); + + const uint8_t* mac_addr = bt_get_profile_mac_address(bt); + memcpy(app->mac, mac_addr, BAD_USB_MAC_ADDRESS_LEN); + // Custom Widget app->widget = widget_alloc(); view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); - app->submenu = submenu_alloc(); + app->var_item_list_bt = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + app->view_dispatcher, BadUsbAppViewConfigBt, variable_item_list_get_view(app->var_item_list_bt)); + app->var_item_list_usb = variable_item_list_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfigUsb, variable_item_list_get_view(app->var_item_list_usb)); app->bad_usb_view = bad_usb_alloc(); view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); + app->text_input = text_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfigName, text_input_get_view(app->text_input)); + + app->byte_input = byte_input_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfigMac, byte_input_get_view(app->byte_input)); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); if(furi_hal_usb_is_locked()) { @@ -101,7 +134,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { if(!furi_string_empty(app->file_path)) { - app->bad_usb_script = bad_usb_script_open(app->file_path); + app->bad_usb_script = bad_usb_script_open(app->file_path, bt); bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); } else { @@ -129,9 +162,19 @@ void bad_usb_app_free(BadUsbApp* app) { view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); widget_free(app->widget); - // Submenu - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); - submenu_free(app->submenu); + // Variable item list + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigBt); + variable_item_list_free(app->var_item_list_bt); + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigUsb); + variable_item_list_free(app->var_item_list_usb); + + // Text Input + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigName); + text_input_free(app->text_input); + + // Byte Input + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigMac); + byte_input_free(app->byte_input); // View dispatcher view_dispatcher_free(app->view_dispatcher); @@ -141,6 +184,7 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_BT); bad_usb_save_settings(app); diff --git a/applications/main/bad_usb/bad_usb_app.h b/applications/main/bad_usb/bad_usb_app.h index afadd87e9..4192a59c9 100644 --- a/applications/main/bad_usb/bad_usb_app.h +++ b/applications/main/bad_usb/bad_usb_app.h @@ -6,6 +6,8 @@ extern "C" { typedef struct BadUsbApp BadUsbApp; +void bad_usb_set_name(BadUsbApp* app, const char* fmt, ...); + #ifdef __cplusplus } #endif diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index b3fbb1679..93b5cf08d 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -8,11 +8,12 @@ #include #include #include -#include #include #include #include #include +#include +#include #include "views/bad_usb_view.h" #define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") @@ -20,11 +21,20 @@ #define BAD_USB_APP_SCRIPT_EXTENSION ".txt" #define BAD_USB_APP_LAYOUT_EXTENSION ".kl" +#define BAD_USB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro +#define BAD_USB_ADV_NAME_MAX_LEN 18 + typedef enum { BadUsbAppErrorNoFiles, BadUsbAppErrorCloseRpc, } BadUsbAppError; +typedef enum BadUsbCustomEvent { + BadUsbAppCustomEventTextEditResult, + BadUsbAppCustomEventByteInputDone, + BadUsbCustomEventErrorBack +} BadUsbCustomEvent; + struct BadUsbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -32,17 +42,29 @@ struct BadUsbApp { NotificationApp* notifications; DialogsApp* dialogs; Widget* widget; - Submenu* submenu; + VariableItemList* var_item_list_bt; + VariableItemList* var_item_list_usb; + + Bt* bt; + TextInput* text_input; + ByteInput* byte_input; + uint8_t mac[BAD_USB_MAC_ADDRESS_LEN]; + char name[BAD_USB_ADV_NAME_MAX_LEN + 1]; BadUsbAppError error; FuriString* file_path; FuriString* keyboard_layout; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; + + bool is_bluetooth; }; typedef enum { BadUsbAppViewError, BadUsbAppViewWork, - BadUsbAppViewConfig, -} BadUsbAppView; \ No newline at end of file + BadUsbAppViewConfigBt, + BadUsbAppViewConfigUsb, + BadUsbAppViewConfigMac, + BadUsbAppViewConfigName +} BadUsbAppView; diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 1a73150a5..9c24ff777 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -3,11 +3,16 @@ #include #include #include +#include #include #include #include "bad_usb_script.h" #include +#include + +#define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") + #define TAG "BadUSB" #define WORKER_TAG TAG "Worker" #define FILE_BUFFER_LEN 16 @@ -26,6 +31,27 @@ typedef enum { WorkerEvtDisconnect = (1 << 3), } WorkerEvtFlags; +typedef enum { + LevelRssi122_100, + LevelRssi99_80, + LevelRssi79_60, + LevelRssi59_40, + LevelRssi39_0, + LevelRssiNum, + LevelRssiError = 0xFF, +} LevelRssiRange; + +/** + * Delays for waiting between HID key press and key release +*/ +const uint8_t bt_hid_delays[LevelRssiNum] = { + 30, // LevelRssi122_100 + 25, // LevelRssi99_80 + 20, // LevelRssi79_60 + 17, // LevelRssi59_40 + 14, // LevelRssi39_0 +}; + struct BadUsbScript { FuriHalUsbHidConfig hid_cfg; BadUsbState st; @@ -41,6 +67,8 @@ struct BadUsbScript { FuriString* line_prev; uint32_t repeat_cnt; + + Bt* bt; }; typedef struct { @@ -75,8 +103,8 @@ static const DuckyKey ducky_keys[] = { {"BREAK", HID_KEYBOARD_PAUSE}, {"PAUSE", HID_KEYBOARD_PAUSE}, {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, - {"DELETE", HID_KEYBOARD_DELETE_FORWARD}, - {"BACKSPACE", HID_KEYBOARD_DELETE}, + {"DELETE", HID_KEYBOARD_DELETE}, + {"BACKSPACE", HID_KEYPAD_BACKSPACE}, {"END", HID_KEYBOARD_END}, {"ESC", HID_KEYBOARD_ESCAPE}, {"ESCAPE", HID_KEYBOARD_ESCAPE}, @@ -134,6 +162,51 @@ static const uint8_t numpad_keys[10] = { HID_KEYPAD_9, }; +uint8_t bt_timeout = 0; + +static LevelRssiRange bt_remote_rssi_range(Bt* bt) { + BtRssi rssi_data = {0}; + + if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; + + if(rssi_data.rssi <= 39) + return LevelRssi39_0; + else if(rssi_data.rssi <= 59) + return LevelRssi59_40; + else if(rssi_data.rssi <= 79) + return LevelRssi79_60; + else if(rssi_data.rssi <= 99) + return LevelRssi99_80; + else if(rssi_data.rssi <= 122) + return LevelRssi122_100; + + return LevelRssiError; +} + +static inline void update_bt_timeout(Bt* bt) { + LevelRssiRange r = bt_remote_rssi_range(bt); + if(r < LevelRssiNum) { + bt_timeout = bt_hid_delays[r]; + } +} + +/** + * @brief Wait until there are enough free slots in the keyboard buffer + * + * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) +*/ +// static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { +// uint32_t start = furi_get_tick(); +// uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; +// while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { +// furi_delay_ms(100); + +// if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { +// break; +// } +// } +// } + static bool ducky_get_number(const char* param, uint32_t* val) { uint32_t value = 0; if(sscanf(param, "%lu", &value) == 1) { @@ -664,7 +737,7 @@ static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); } -BadUsbScript* bad_usb_script_open(FuriString* file_path) { +BadUsbScript* bad_usb_script_open(FuriString* file_path, Bt* bt) { furi_assert(file_path); BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); @@ -675,6 +748,8 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) { bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; + bad_usb->bt = bt; + bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); furi_thread_start(bad_usb->thread); return bad_usb; @@ -682,6 +757,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path) { void bad_usb_script_close(BadUsbScript* bad_usb) { furi_assert(bad_usb); + furi_record_close(RECORD_STORAGE); furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); furi_thread_join(bad_usb->thread); furi_thread_free(bad_usb->thread); diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index 1e4d98fe7..5c37bcec2 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -5,6 +5,7 @@ extern "C" { #endif #include +#include typedef struct BadUsbScript BadUsbScript; @@ -29,7 +30,7 @@ typedef struct { char error[64]; } BadUsbState; -BadUsbScript* bad_usb_script_open(FuriString* file_path); +BadUsbScript* bad_usb_script_open(FuriString* file_path, Bt* bt); void bad_usb_script_close(BadUsbScript* bad_usb); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c deleted file mode 100644 index 2a9f2f76c..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" - -enum SubmenuIndex { - SubmenuIndexKeyboardLayout, -}; - -void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { - BadUsbApp* bad_usb = context; - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); -} - -void bad_usb_scene_config_on_enter(void* context) { - BadUsbApp* bad_usb = context; - Submenu* submenu = bad_usb->submenu; - - submenu_add_item( - submenu, - "Keyboard layout", - SubmenuIndexKeyboardLayout, - bad_usb_scene_config_submenu_callback, - bad_usb); - - submenu_set_selected_item( - submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); - - view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); -} - -bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* bad_usb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); - consumed = true; - if(event.event == SubmenuIndexKeyboardLayout) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); - } else { - furi_crash("Unknown key type"); - } - } - - return consumed; -} - -void bad_usb_scene_config_on_exit(void* context) { - BadUsbApp* bad_usb = context; - Submenu* submenu = bad_usb->submenu; - - submenu_reset(submenu); -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 423aedc51..2524464f1 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -1,5 +1,8 @@ ADD_SCENE(bad_usb, file_select, FileSelect) ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) -ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_bt, ConfigBt) +ADD_SCENE(bad_usb, config_usb, ConfigUsb) ADD_SCENE(bad_usb, config_layout, ConfigLayout) +ADD_SCENE(bad_usb, config_name, ConfigName) +ADD_SCENE(bad_usb, config_mac, ConfigMac) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c new file mode 100644 index 000000000..19576ddd0 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c @@ -0,0 +1,81 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum VarItemListIndex { + VarItemListIndexConnection, + VarItemListIndexKeyboardLayout, + VarItemListIndexAdvertisementName, + VarItemListIndexMacAddress, +}; + +void bad_usb_scene_config_bt_connection_callback(VariableItem* item) { + BadUsbApp* bad_usb = variable_item_get_context(item); + bad_usb->is_bluetooth = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); +} + +void bad_usb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_bt_on_enter(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list_bt; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_usb_scene_config_bt_connection_callback, bad_usb); + variable_item_set_current_value_index(item, bad_usb->is_bluetooth); + variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + + item = variable_item_list_add( + var_item_list, "Keyboard layout", 0, NULL, bad_usb); + + item = variable_item_list_add( + var_item_list, "Change adv name", 0, NULL, bad_usb); + + item = variable_item_list_add( + var_item_list, "Change MAC address", 0, NULL, bad_usb); + + variable_item_list_set_enter_callback(var_item_list, bad_usb_scene_config_bt_var_item_list_callback, bad_usb); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigBt); +} + +bool bad_usb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfigBt, event.event); + consumed = true; + if(event.event == VarItemListIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else if(event.event == VarItemListIndexConnection) { + scene_manager_previous_scene(bad_usb->scene_manager); + if (bad_usb->is_bluetooth) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); + } else { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); + } + } else if(event.event == VarItemListIndexAdvertisementName) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigName); + } else if(event.event == VarItemListIndexMacAddress) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigMac); + // } else { + // furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_bt_on_exit(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list_bt; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c new file mode 100644 index 000000000..597cbcbd7 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c @@ -0,0 +1,57 @@ +#include "../bad_usb_app_i.h" + +#define TAG "BadUsbConfigMac" + +static uint8_t* reverse_mac_addr(uint8_t* mac) { + uint8_t tmp; + for(int i = 0; i < 3; i++) { + tmp = mac[i]; + mac[i] = mac[5 - i]; + mac[5 - i] = tmp; + } + return mac; +} + +void bad_usb_scene_config_mac_byte_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, BadUsbAppCustomEventByteInputDone); +} + +void bad_usb_scene_config_mac_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + // Setup view + ByteInput* byte_input = bad_usb->byte_input; + byte_input_set_header_text(byte_input, "Enter new MAC address"); + byte_input_set_result_callback( + byte_input, + bad_usb_scene_config_mac_byte_input_callback, + NULL, + bad_usb, + reverse_mac_addr(bad_usb->mac), + GAP_MAC_ADDR_SIZE); + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigMac); +} + +bool bad_usb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadUsbAppCustomEventByteInputDone) { + bt_set_profile_mac_address(bad_usb->bt, reverse_mac_addr(bad_usb->mac)); + scene_manager_previous_scene(bad_usb->scene_manager); + consumed = true; + } + } + return consumed; +} + +void bad_usb_scene_config_mac_on_exit(void* context) { + BadUsbApp* bad_usb = context; + + // Clear view + byte_input_set_result_callback(bad_usb->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(bad_usb->byte_input, ""); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c new file mode 100644 index 000000000..c22558d58 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c @@ -0,0 +1,46 @@ +#include "../bad_usb_app_i.h" + +static void bad_usb_scene_config_name_text_input_callback(void* context) { + BadUsbApp* bad_usb = context; + + view_dispatcher_send_custom_event( + bad_usb->view_dispatcher, BadUsbAppCustomEventTextEditResult); +} + +void bad_usb_scene_config_name_on_enter(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + text_input_set_header_text(text_input, "Set BLE adv name"); + + text_input_set_result_callback( + text_input, + bad_usb_scene_config_name_text_input_callback, + bad_usb, + bad_usb->name, + BAD_USB_ADV_NAME_MAX_LEN, + true); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigName); +} + +bool bad_usb_scene_config_name_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadUsbAppCustomEventTextEditResult) { + bt_set_profile_adv_name(bad_usb->bt, bad_usb->name); + } + scene_manager_previous_scene(bad_usb->scene_manager); + } + return consumed; +} + +void bad_usb_scene_config_name_on_exit(void* context) { + BadUsbApp* bad_usb = context; + TextInput* text_input = bad_usb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c new file mode 100644 index 000000000..77a08611c --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c @@ -0,0 +1,69 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum VarItemListIndex { + VarItemListIndexConnection, + VarItemListIndexKeyboardLayout, +}; + +void bad_usb_scene_config_usb_connection_callback(VariableItem* item) { + BadUsbApp* bad_usb = variable_item_get_context(item); + bad_usb->is_bluetooth = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); +} + +void bad_usb_scene_config_usb_var_item_list_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_usb_on_enter(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list_usb; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_usb_scene_config_usb_connection_callback, bad_usb); + variable_item_set_current_value_index(item, bad_usb->is_bluetooth); + variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + + item = variable_item_list_add( + var_item_list, "Keyboard layout", 0, NULL, bad_usb); + + variable_item_list_set_enter_callback(var_item_list, bad_usb_scene_config_usb_var_item_list_callback, bad_usb); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigUsb); +} + +bool bad_usb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsb, event.event); + consumed = true; + if(event.event == VarItemListIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else if(event.event == VarItemListIndexConnection) { + scene_manager_previous_scene(bad_usb->scene_manager); + if (bad_usb->is_bluetooth) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); + } else { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); + } + // } else { + // furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_usb_on_exit(void* context) { + BadUsbApp* bad_usb = context; + VariableItemList* var_item_list = bad_usb->var_item_list_usb; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 2c707bbf1..d1290e0e0 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,10 +1,6 @@ #include "../bad_usb_app_i.h" #include "../../../settings/xtreme_settings/xtreme_settings.h" -typedef enum { - BadUsbCustomEventErrorBack, -} BadUsbCustomEvent; - static void bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index 21a2ce024..5176e3b28 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -29,7 +29,7 @@ void bad_usb_scene_file_select_on_enter(void* context) { } if(bad_usb_file_select(bad_usb)) { - bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path); + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->bt); bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 2971c01e9..1f6af5545 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -16,7 +16,11 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InputKeyLeft) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + if (app->is_bluetooth) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigBt); + } else { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigUsb); + } consumed = true; } else if(event.event == InputKeyOk) { bad_usb_script_toggle(app->bad_usb_script); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index e4ed2e142..aabe45714 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.4,, +Version,+,13.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1038,7 +1038,7 @@ Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" -Function,+furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" +Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" Function,+,furi_hal_bt_start_advertising,void, Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" @@ -4422,6 +4422,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* From f2ce7dad14832376540ff95cadb2956c898307ea Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 28 Jan 2023 16:44:17 +0000 Subject: [PATCH 039/231] Fix RFID success icon --- ...id_success.png => RFIDDolphinSuccess_108x57.png} | Bin ...id_success.bmx => RFIDDolphinSuccess_108x57.bmx} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename assets/dolphin/custom/WatchDogs/Icons/RFID/{rfid_success.png => RFIDDolphinSuccess_108x57.png} (100%) rename assets/resources/dolphin_custom/WatchDogs/Icons/RFID/{rfid_success.bmx => RFIDDolphinSuccess_108x57.bmx} (100%) diff --git a/assets/dolphin/custom/WatchDogs/Icons/RFID/rfid_success.png b/assets/dolphin/custom/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.png similarity index 100% rename from assets/dolphin/custom/WatchDogs/Icons/RFID/rfid_success.png rename to assets/dolphin/custom/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.png diff --git a/assets/resources/dolphin_custom/WatchDogs/Icons/RFID/rfid_success.bmx b/assets/resources/dolphin_custom/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.bmx similarity index 100% rename from assets/resources/dolphin_custom/WatchDogs/Icons/RFID/rfid_success.bmx rename to assets/resources/dolphin_custom/WatchDogs/Icons/RFID/RFIDDolphinSuccess_108x57.bmx From 2b89b1c42a163074082160193aa1d31cf73484c0 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 28 Jan 2023 16:48:32 +0000 Subject: [PATCH 040/231] Fix spelling --- assets/dolphin/custom/ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/dolphin/custom/ReadMe.md b/assets/dolphin/custom/ReadMe.md index 359cad154..7dd7f80fb 100644 --- a/assets/dolphin/custom/ReadMe.md +++ b/assets/dolphin/custom/ReadMe.md @@ -1,6 +1,6 @@ # Pre-included Asset Packs Includes a WatchDogs asset pack by default. Credits: -- [WrenchAtHome](https://github.com/wrenchathome) for some [pdesktop anims](https://github.com/wrenchathome/flip0anims) +- [WrenchAtHome](https://github.com/wrenchathome) for some [desktop anims](https://github.com/wrenchathome/flip0anims) - [u/Cheroon](https://www.reddit.com/user/Cheroon/) for the [passport portait](https://www.reddit.com/r/watch_dogs/comments/50n046/pixel_art_wrench_mask_gif/) - [WillyJL](https://github.com/Willy-JL) for other assets, icons and animations From 4ec539ff996b139e74f14a5173eac40b8beab9fb Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 29 Jan 2023 03:24:42 +0000 Subject: [PATCH 041/231] Move all Bad BLE functionality to Bad USB --- applications/main/bad_ble/bad_ble_script.c | 819 ------------------ applications/main/bad_usb/bad_usb_app.c | 2 +- applications/main/bad_usb/bad_usb_app_i.h | 2 +- applications/main/bad_usb/bad_usb_script.c | 240 +++-- .../bad_usb/scenes/bad_usb_scene_config_bt.c | 12 +- .../bad_usb/scenes/bad_usb_scene_config_usb.c | 12 +- .../scenes/bad_usb_scene_file_select.c | 2 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 2 +- 8 files changed, 202 insertions(+), 889 deletions(-) delete mode 100644 applications/main/bad_ble/bad_ble_script.c diff --git a/applications/main/bad_ble/bad_ble_script.c b/applications/main/bad_ble/bad_ble_script.c deleted file mode 100644 index a969df7dd..000000000 --- a/applications/main/bad_ble/bad_ble_script.c +++ /dev/null @@ -1,819 +0,0 @@ -// #include -// #include -// #include -// #include -// #include -// #include - -// #include -// #include "bad_ble_script.h" -// #include - -// #include - -// #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") - -// #define TAG "BadBle" -// #define WORKER_TAG TAG "Worker" -// #define FILE_BUFFER_LEN 16 - -// #define SCRIPT_STATE_ERROR (-1) -// #define SCRIPT_STATE_END (-2) -// #define SCRIPT_STATE_NEXT_LINE (-3) - -// #define BADBLE_ASCII_TO_KEY(script, x) \ -// (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) - -// typedef enum { -// WorkerEvtToggle = (1 << 0), -// WorkerEvtEnd = (1 << 1), -// WorkerEvtConnect = (1 << 2), -// WorkerEvtDisconnect = (1 << 3), -// } WorkerEvtFlags; - -// typedef enum { -// LevelRssi122_100, -// LevelRssi99_80, -// LevelRssi79_60, -// LevelRssi59_40, -// LevelRssi39_0, -// LevelRssiNum, -// LevelRssiError = 0xFF, -// } LevelRssiRange; - -// /** -// * Delays for waiting between HID key press and key release -// */ -// const uint8_t bt_hid_delays[LevelRssiNum] = { -// 30, // LevelRssi122_100 -// 25, // LevelRssi99_80 -// 20, // LevelRssi79_60 -// 17, // LevelRssi59_40 -// 14, // LevelRssi39_0 -// }; - -// struct BadBleScript { - -// BadBleState st; -// FuriString* file_path; -// uint32_t defdelay; -// uint16_t layout[128]; -// FuriThread* thread; -// uint8_t file_buf[FILE_BUFFER_LEN + 1]; -// uint8_t buf_start; -// uint8_t buf_len; -// bool file_end; -// FuriString* line; - -// FuriString* line_prev; -// uint32_t repeat_cnt; - -// Bt* bt; -// }; - -// typedef struct { -// char* name; -// uint16_t keycode; -// } DuckyKey; - -// static const DuckyKey ducky_keys[] = { -// {"CTRL-ALT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_ALT}, -// {"CTRL-SHIFT", KEY_MOD_LEFT_CTRL | KEY_MOD_LEFT_SHIFT}, -// {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, -// {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, -// {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, - -// {"CTRL", KEY_MOD_LEFT_CTRL}, -// {"CONTROL", KEY_MOD_LEFT_CTRL}, -// {"SHIFT", KEY_MOD_LEFT_SHIFT}, -// {"ALT", KEY_MOD_LEFT_ALT}, -// {"GUI", KEY_MOD_LEFT_GUI}, -// {"WINDOWS", KEY_MOD_LEFT_GUI}, - -// {"DOWNARROW", HID_KEYBOARD_DOWN_ARROW}, -// {"DOWN", HID_KEYBOARD_DOWN_ARROW}, -// {"LEFTARROW", HID_KEYBOARD_LEFT_ARROW}, -// {"LEFT", HID_KEYBOARD_LEFT_ARROW}, -// {"RIGHTARROW", HID_KEYBOARD_RIGHT_ARROW}, -// {"RIGHT", HID_KEYBOARD_RIGHT_ARROW}, -// {"UPARROW", HID_KEYBOARD_UP_ARROW}, -// {"UP", HID_KEYBOARD_UP_ARROW}, - -// {"ENTER", HID_KEYBOARD_RETURN}, -// {"BREAK", HID_KEYBOARD_PAUSE}, -// {"PAUSE", HID_KEYBOARD_PAUSE}, -// {"CAPSLOCK", HID_KEYBOARD_CAPS_LOCK}, -// {"DELETE", HID_KEYBOARD_DELETE}, -// {"BACKSPACE", HID_KEYPAD_BACKSPACE}, -// {"END", HID_KEYBOARD_END}, -// {"ESC", HID_KEYBOARD_ESCAPE}, -// {"ESCAPE", HID_KEYBOARD_ESCAPE}, -// {"HOME", HID_KEYBOARD_HOME}, -// {"INSERT", HID_KEYBOARD_INSERT}, -// {"NUMLOCK", HID_KEYPAD_NUMLOCK}, -// {"PAGEUP", HID_KEYBOARD_PAGE_UP}, -// {"PAGEDOWN", HID_KEYBOARD_PAGE_DOWN}, -// {"PRINTSCREEN", HID_KEYBOARD_PRINT_SCREEN}, -// {"SCROLLLOCK", HID_KEYBOARD_SCROLL_LOCK}, -// {"SPACE", HID_KEYBOARD_SPACEBAR}, -// {"TAB", HID_KEYBOARD_TAB}, -// {"MENU", HID_KEYBOARD_APPLICATION}, -// {"APP", HID_KEYBOARD_APPLICATION}, - -// {"F1", HID_KEYBOARD_F1}, -// {"F2", HID_KEYBOARD_F2}, -// {"F3", HID_KEYBOARD_F3}, -// {"F4", HID_KEYBOARD_F4}, -// {"F5", HID_KEYBOARD_F5}, -// {"F6", HID_KEYBOARD_F6}, -// {"F7", HID_KEYBOARD_F7}, -// {"F8", HID_KEYBOARD_F8}, -// {"F9", HID_KEYBOARD_F9}, -// {"F10", HID_KEYBOARD_F10}, -// {"F11", HID_KEYBOARD_F11}, -// {"F12", HID_KEYBOARD_F12}, -// }; - -// static const char ducky_cmd_comment[] = {"REM"}; -// static const char ducky_cmd_id[] = {"ID"}; -// static const char ducky_cmd_delay[] = {"DELAY "}; -// static const char ducky_cmd_string[] = {"STRING "}; -// static const char ducky_cmd_defdelay_1[] = {"DEFAULT_DELAY "}; -// static const char ducky_cmd_defdelay_2[] = {"DEFAULTDELAY "}; -// static const char ducky_cmd_repeat[] = {"REPEAT "}; -// static const char ducky_cmd_sysrq[] = {"SYSRQ "}; - -// static const char ducky_cmd_altchar[] = {"ALTCHAR "}; -// static const char ducky_cmd_altstr_1[] = {"ALTSTRING "}; -// static const char ducky_cmd_altstr_2[] = {"ALTCODE "}; - -// static const char ducky_cmd_lang[] = {"DUCKY_LANG"}; - -// static const uint8_t numpad_keys[10] = { -// HID_KEYPAD_0, -// HID_KEYPAD_1, -// HID_KEYPAD_2, -// HID_KEYPAD_3, -// HID_KEYPAD_4, -// HID_KEYPAD_5, -// HID_KEYPAD_6, -// HID_KEYPAD_7, -// HID_KEYPAD_8, -// HID_KEYPAD_9, -// }; - -// uint8_t bt_timeout = 0; - -// static LevelRssiRange bt_remote_rssi_range(Bt* bt) { -// BtRssi rssi_data = {0}; - -// if(!bt_remote_rssi(bt, &rssi_data)) return LevelRssiError; - -// if(rssi_data.rssi <= 39) -// return LevelRssi39_0; -// else if(rssi_data.rssi <= 59) -// return LevelRssi59_40; -// else if(rssi_data.rssi <= 79) -// return LevelRssi79_60; -// else if(rssi_data.rssi <= 99) -// return LevelRssi99_80; -// else if(rssi_data.rssi <= 122) -// return LevelRssi122_100; - -// return LevelRssiError; -// } - -// static inline void update_bt_timeout(Bt* bt) { -// LevelRssiRange r = bt_remote_rssi_range(bt); -// if(r < LevelRssiNum) { -// bt_timeout = bt_hid_delays[r]; -// } -// } - -/** - * @brief Wait until there are enough free slots in the keyboard buffer - * - * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) -*/ -static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { - uint32_t start = furi_get_tick(); - uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; - while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { - furi_delay_ms(100); - - if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { - break; - } - } -} - -// static bool ducky_get_number(const char* param, uint32_t* val) { -// uint32_t value = 0; -// if(sscanf(param, "%lu", &value) == 1) { -// *val = value; -// return true; -// } -// return false; -// } - -// static uint32_t ducky_get_command_len(const char* line) { -// uint32_t len = strlen(line); -// for(uint32_t i = 0; i < len; i++) { -// if(line[i] == ' ') return i; -// } -// return 0; -// } - -// static bool ducky_is_line_end(const char chr) { -// return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); -// } - -static void ducky_numlock_on() { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); - furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); - } -} - -static bool ducky_numpad_press(const char num) { - if((num < '0') || (num > '9')) return false; - - uint16_t key = numpad_keys[num - '0']; - bt_hid_hold_while_keyboard_buffer_full(1, -1); - FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); - furi_hal_bt_hid_kb_press(key); - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - - return true; -} - -static bool ducky_altchar(const char* charcode) { - uint8_t i = 0; - bool state = false; - - FURI_LOG_I(WORKER_TAG, "char %s", charcode); - - bt_hid_hold_while_keyboard_buffer_full(1, -1); - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); - - while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(charcode[i]); - if(state == false) break; - i++; - } - - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); - return state; -} - -// static bool ducky_altstring(const char* param) { -// uint32_t i = 0; -// bool state = false; - -// while(param[i] != '\0') { -// if((param[i] < ' ') || (param[i] > '~')) { -// i++; -// continue; // Skip non-printable chars -// } - -// char temp_str[4]; -// snprintf(temp_str, 4, "%u", param[i]); - -// state = ducky_altchar(temp_str); -// if(state == false) break; -// i++; -// } -// return state; -// } - -static bool ducky_string(BadBleScript* bad_ble, const char* param) { - uint32_t i = 0; - while(param[i] != '\0') { - uint16_t keycode = BADBLE_ASCII_TO_KEY(bad_ble, param[i]); - if(keycode != HID_KEYBOARD_NONE) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); - furi_hal_bt_hid_kb_press(keycode); - - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(keycode); - } - i++; - } - return true; -} - -// static uint16_t ducky_get_keycode(BadBleScript* bad_ble, const char* param, bool accept_chars) { -// for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { -// size_t key_cmd_len = strlen(ducky_keys[i].name); -// if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && -// (ducky_is_line_end(param[key_cmd_len]))) { -// return ducky_keys[i].keycode; -// } -// } -// if((accept_chars) && (strlen(param) > 0)) { -// return (BADBLE_ASCII_TO_KEY(bad_ble, param[0]) & 0xFF); -// } -// return 0; -// } - -static int32_t - ducky_parse_line(BadBleScript* bad_ble, FuriString* line, char* error, size_t error_len) { - uint32_t line_len = furi_string_size(line); - const char* line_tmp = furi_string_get_cstr(line); - bool state = false; - - if(line_len == 0) { - return SCRIPT_STATE_NEXT_LINE; // Skip empty lines - } - - // General commands - if(strncmp(line_tmp, ducky_cmd_comment, strlen(ducky_cmd_comment)) == 0) { - // REM - comment line - return (0); - } else if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - // ID - executed in ducky_script_preload - return (0); - } else if(strncmp(line_tmp, ducky_cmd_lang, strlen(ducky_cmd_lang)) == 0) { - // DUCKY_LANG - ignore command to retain compatibility with existing scripts - return (0); - } else if(strncmp(line_tmp, ducky_cmd_delay, strlen(ducky_cmd_delay)) == 0) { - // DELAY - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint32_t delay_val = 0; - state = ducky_get_number(line_tmp, &delay_val); - if((state) && (delay_val > 0)) { - return (int32_t)delay_val; - } - if(error != NULL) { - snprintf(error, error_len, "Invalid number %s", line_tmp); - } - return SCRIPT_STATE_ERROR; - } else if( - (strncmp(line_tmp, ducky_cmd_defdelay_1, strlen(ducky_cmd_defdelay_1)) == 0) || - (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { - // DEFAULT_DELAY - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_ble->defdelay); - if(!state && error != NULL) { - snprintf(error, error_len, "Invalid number %s", line_tmp); - } - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { - // STRING - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_string(bad_ble, line_tmp); - if(!state && error != NULL) { - snprintf(error, error_len, "Invalid string %s", line_tmp); - } - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { - // ALTCHAR - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altchar(line_tmp); - if(!state && error != NULL) { - snprintf(error, error_len, "Invalid altchar %s", line_tmp); - } - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if( - (strncmp(line_tmp, ducky_cmd_altstr_1, strlen(ducky_cmd_altstr_1)) == 0) || - (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { - // ALTSTRING - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altstring(line_tmp); - if(!state && error != NULL) { - snprintf(error, error_len, "Invalid altstring %s", line_tmp); - } - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { - // REPEAT - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_ble->repeat_cnt); - if(!state && error != NULL) { - snprintf(error, error_len, "Invalid number %s", line_tmp); - } - return (state) ? (0) : SCRIPT_STATE_ERROR; - } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { - // SYSRQ - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint16_t key = ducky_get_keycode(bad_ble, line_tmp, true); - bt_hid_hold_while_keyboard_buffer_full(1, -1); - furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - furi_hal_bt_hid_kb_press(key); - - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - return (0); - } else { - // Special keys + modifiers - uint16_t key = ducky_get_keycode(bad_ble, line_tmp, false); - if(key == HID_KEYBOARD_NONE) { - if(error != NULL) { - snprintf(error, error_len, "No keycode defined for %s", line_tmp); - } - return SCRIPT_STATE_ERROR; - } - if((key & 0xFF00) != 0) { - // It's a modifier key - line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(bad_ble, line_tmp, true); - } - furi_hal_bt_hid_kb_press(key); - - furi_delay_ms(bt_timeout); - furi_hal_bt_hid_kb_release(key); - return (0); - } -} - -static bool ducky_script_preload(BadBleScript* bad_ble, File* script_file) { - uint8_t ret = 0; - uint32_t line_len = 0; - - furi_string_reset(bad_ble->line); - - do { - ret = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN); - for(uint16_t i = 0; i < ret; i++) { - if(bad_ble->file_buf[i] == '\n' && line_len > 0) { - bad_ble->st.line_nb++; - line_len = 0; - } else { - if(bad_ble->st.line_nb == 0) { // Save first line - furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]); - } - line_len++; - } - } - if(storage_file_eof(script_file)) { - if(line_len > 0) { - bad_ble->st.line_nb++; - break; - } - } - } while(ret > 0); - - storage_file_seek(script_file, 0, true); - furi_string_reset(bad_ble->line); - - return true; -} - -static int32_t ducky_script_execute_next(BadBleScript* bad_ble, File* script_file) { - int32_t delay_val = 0; - - if(bad_ble->repeat_cnt > 0) { - bad_ble->repeat_cnt--; - delay_val = ducky_parse_line( - bad_ble, bad_ble->line_prev, bad_ble->st.error, sizeof(bad_ble->st.error)); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val < 0) { // Script error - bad_ble->st.error_line = bad_ble->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur - 1U); - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_ble->defdelay); - } - } - - furi_string_set(bad_ble->line_prev, bad_ble->line); - furi_string_reset(bad_ble->line); - - while(1) { - if(bad_ble->buf_len == 0) { - bad_ble->buf_len = storage_file_read(script_file, bad_ble->file_buf, FILE_BUFFER_LEN); - if(storage_file_eof(script_file)) { - if((bad_ble->buf_len < FILE_BUFFER_LEN) && (bad_ble->file_end == false)) { - bad_ble->file_buf[bad_ble->buf_len] = '\n'; - bad_ble->buf_len++; - bad_ble->file_end = true; - } - } - - bad_ble->buf_start = 0; - if(bad_ble->buf_len == 0) return SCRIPT_STATE_END; - } - for(uint8_t i = bad_ble->buf_start; i < (bad_ble->buf_start + bad_ble->buf_len); i++) { - if(bad_ble->file_buf[i] == '\n' && furi_string_size(bad_ble->line) > 0) { - bad_ble->st.line_cur++; - bad_ble->buf_len = bad_ble->buf_len + bad_ble->buf_start - (i + 1); - bad_ble->buf_start = i + 1; - furi_string_trim(bad_ble->line); - delay_val = ducky_parse_line( - bad_ble, bad_ble->line, bad_ble->st.error, sizeof(bad_ble->st.error)); - if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line - return 0; - } else if(delay_val < 0) { - bad_ble->st.error_line = bad_ble->st.line_cur; - if(delay_val == SCRIPT_STATE_NEXT_LINE) { - snprintf( - bad_ble->st.error, sizeof(bad_ble->st.error), "Forbidden empty line"); - FURI_LOG_E( - WORKER_TAG, "Forbidden empty line at line %u", bad_ble->st.line_cur); - } else { - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_ble->st.line_cur); - } - return SCRIPT_STATE_ERROR; - } else { - return (delay_val + bad_ble->defdelay); - } - } else { - furi_string_push_back(bad_ble->line, bad_ble->file_buf[i]); - } - } - bad_ble->buf_len = 0; - if(bad_ble->file_end) return SCRIPT_STATE_END; - } - - return 0; -} - -static void bad_ble_hid_state_callback(BtStatus status, void* context) { - furi_assert(context); - BadBleScript* bad_ble = (BadBleScript*)context; - bool state = (status == BtStatusConnected); - - if(state == true) { - LevelRssiRange r = bt_remote_rssi_range(bad_ble->bt); - if(r != LevelRssiError) { - bt_timeout = bt_hid_delays[r]; - } - furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtConnect); - } else - furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtDisconnect); -} - -static int32_t bad_ble_worker(void* context) { - BadBleScript* bad_ble = context; - - BadBleWorkerState worker_state = BadBleStateInit; - int32_t delay_val = 0; - - // BLE HID init - bt_timeout = bt_hid_delays[LevelRssi39_0]; - - bt_disconnect(bad_ble->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - bt_keys_storage_set_storage_path(bad_ble->bt, HID_BT_KEYS_STORAGE_PATH); - - if(!bt_set_profile(bad_ble->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - return -1; - } - - furi_hal_bt_start_advertising(); - - FURI_LOG_I(WORKER_TAG, "Init"); - File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - bad_ble->line = furi_string_alloc(); - bad_ble->line_prev = furi_string_alloc(); - - bt_set_status_changed_callback(bad_ble->bt, bad_ble_hid_state_callback, bad_ble); - while(1) { - if(worker_state == BadBleStateInit) { // State: initialization - if(storage_file_open( - script_file, - furi_string_get_cstr(bad_ble->file_path), - FSAM_READ, - FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_ble, script_file)) && (bad_ble->st.line_nb > 0)) { - worker_state = BadBleStateNotConnected; // Ready to run - } else { - worker_state = BadBleStateScriptError; // Script preload error - } - } else { - FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadBleStateFileError; // File open error - } - bad_ble->st.state = worker_state; - - } else if(worker_state == BadBleStateNotConnected) { // State: ble not connected - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { - worker_state = BadBleStateIdle; // Ready to run - } else if(flags & WorkerEvtToggle) { - worker_state = BadBleStateWillRun; // Will run when ble is connected - } - bad_ble->st.state = worker_state; - - } else if(worker_state == BadBleStateIdle) { // State: ready to start - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtToggle) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); - delay_val = 0; - bad_ble->buf_len = 0; - bad_ble->st.line_cur = 0; - bad_ble->defdelay = 0; - bad_ble->repeat_cnt = 0; - bad_ble->file_end = false; - storage_file_seek(script_file, 0, true); - worker_state = BadBleStateRunning; - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBleStateNotConnected; // ble disconnected - } - bad_ble->st.state = worker_state; - - } else if(worker_state == BadBleStateWillRun) { // State: start on connection - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); - delay_val = 0; - bad_ble->buf_len = 0; - bad_ble->st.line_cur = 0; - bad_ble->defdelay = 0; - bad_ble->repeat_cnt = 0; - bad_ble->file_end = false; - storage_file_seek(script_file, 0, true); - // extra time for PC to recognize Flipper as keyboard - furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - - update_bt_timeout(bad_ble->bt); - FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); - - worker_state = BadBleStateRunning; - } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution - worker_state = BadBleStateNotConnected; - } - bad_ble->st.state = worker_state; - - } else if(worker_state == BadBleStateRunning) { // State: running - uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); - delay_val -= delay_cur; - if(!(flags & FuriFlagError)) { - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtToggle) { - worker_state = BadBleStateIdle; // Stop executing script - furi_hal_bt_hid_kb_release_all(); - } else if(flags & WorkerEvtDisconnect) { - worker_state = BadBleStateNotConnected; // ble disconnected - furi_hal_bt_hid_kb_release_all(); - } - bad_ble->st.state = worker_state; - continue; - } else if( - (flags == (unsigned)FuriFlagErrorTimeout) || - (flags == (unsigned)FuriFlagErrorResource)) { - if(delay_val > 0) { - bad_ble->st.delay_remain--; - continue; - } - bad_ble->st.state = BadBleStateRunning; - delay_val = ducky_script_execute_next(bad_ble, script_file); - if(delay_val == SCRIPT_STATE_ERROR) { // Script error - delay_val = 0; - worker_state = BadBleStateScriptError; - bad_ble->st.state = worker_state; - } else if(delay_val == SCRIPT_STATE_END) { // End of script - delay_val = 0; - worker_state = BadBleStateIdle; - bad_ble->st.state = BadBleStateDone; - furi_hal_bt_hid_kb_release_all(); - continue; - } else if(delay_val > 1000) { - bad_ble->st.state = BadBleStateDelay; // Show long delays - bad_ble->st.delay_remain = delay_val / 1000; - } - } else { - furi_check((flags & FuriFlagError) == 0); - } - - } else if( - (worker_state == BadBleStateFileError) || - (worker_state == BadBleStateScriptError)) { // State: error - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } - } - - update_bt_timeout(bad_ble->bt); - FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); - } - - // release all keys - bt_hid_hold_while_keyboard_buffer_full(6, 3000); - - // stop ble - bt_set_status_changed_callback(bad_ble->bt, NULL, NULL); - - bt_disconnect(bad_ble->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - bt_keys_storage_set_default_path(bad_ble->bt); - - if(!bt_set_profile(bad_ble->bt, BtProfileSerial)) { - FURI_LOG_E(TAG, "Failed to switch to Serial profile"); - } - - storage_file_close(script_file); - storage_file_free(script_file); - furi_string_free(bad_ble->line); - furi_string_free(bad_ble->line_prev); - - FURI_LOG_I(WORKER_TAG, "End"); - - return 0; -} - -// static void bad_ble_script_set_default_keyboard_layout(BadBleScript* bad_ble) { -// furi_assert(bad_ble); -// memset(bad_ble->layout, HID_KEYBOARD_NONE, sizeof(bad_ble->layout)); -// memcpy(bad_ble->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_ble->layout))); -// } - -// BadBleScript* bad_ble_script_open(FuriString* file_path, Bt* bt) { -// furi_assert(file_path); - -// BadBleScript* bad_ble = malloc(sizeof(BadBleScript)); -// bad_ble->file_path = furi_string_alloc(); -// furi_string_set(bad_ble->file_path, file_path); -// bad_ble_script_set_default_keyboard_layout(bad_ble); - -// bad_ble->st.state = BadBleStateInit; -// bad_ble->st.error[0] = '\0'; - -// bad_ble->bt = bt; - -// bad_ble->thread = furi_thread_alloc_ex("BadBleWorker", 2048, bad_ble_worker, bad_ble); -// furi_thread_start(bad_ble->thread); -// return bad_ble; -// } //-V773 - -// void bad_ble_script_close(BadBleScript* bad_ble) { -// furi_assert(bad_ble); -// furi_record_close(RECORD_STORAGE); -// furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtEnd); -// furi_thread_join(bad_ble->thread); -// furi_thread_free(bad_ble->thread); -// furi_string_free(bad_ble->file_path); -// free(bad_ble); -// } - -// void bad_ble_script_set_keyboard_layout(BadBleScript* bad_ble, FuriString* layout_path) { -// furi_assert(bad_ble); - -// if((bad_ble->st.state == BadBleStateRunning) || (bad_ble->st.state == BadBleStateDelay)) { -// // do not update keyboard layout while a script is running -// return; -// } - -// File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); -// if(!furi_string_empty(layout_path)) { -// if(storage_file_open( -// layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { -// uint16_t layout[128]; -// if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { -// memcpy(bad_ble->layout, layout, sizeof(layout)); -// } -// } -// storage_file_close(layout_file); -// } else { -// bad_ble_script_set_default_keyboard_layout(bad_ble); -// } -// storage_file_free(layout_file); -// } - -// void bad_ble_script_toggle(BadBleScript* bad_ble) { -// furi_assert(bad_ble); -// furi_thread_flags_set(furi_thread_get_id(bad_ble->thread), WorkerEvtToggle); -// } - -// BadBleState* bad_ble_script_get_state(BadBleScript* bad_ble) { -// furi_assert(bad_ble); -// return &(bad_ble->st); -// } diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 94717cd37..63f95a860 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -134,7 +134,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { if(!furi_string_empty(app->file_path)) { - app->bad_usb_script = bad_usb_script_open(app->file_path, bt); + app->bad_usb_script = bad_usb_script_open(app->file_path, app->is_bt ? app->bt : NULL); bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); } else { diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index 93b5cf08d..ba0dee88b 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -57,7 +57,7 @@ struct BadUsbApp { BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; - bool is_bluetooth; + bool is_bt; }; typedef enum { diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 9c24ff777..a61bc1e06 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -195,17 +195,17 @@ static inline void update_bt_timeout(Bt* bt) { * * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) */ -// static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { -// uint32_t start = furi_get_tick(); -// uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; -// while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { -// furi_delay_ms(100); +static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { + uint32_t start = furi_get_tick(); + uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; + while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { + furi_delay_ms(100); -// if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { -// break; -// } -// } -// } + if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { + break; + } + } +} static bool ducky_get_number(const char* param, uint32_t* val) { uint32_t value = 0; @@ -228,42 +228,68 @@ static bool ducky_is_line_end(const char chr) { return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); } -static void ducky_numlock_on() { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { - furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); - furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); +static void ducky_numlock_on(BadUsbScript* bad_usb) { + if (bad_usb->bt) { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } + } else { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { + furi_hal_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); + furi_hal_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); + } } } -static bool ducky_numpad_press(const char num) { +static bool ducky_numpad_press(BadUsbScript* bad_usb, const char num) { if((num < '0') || (num > '9')) return false; uint16_t key = numpad_keys[num - '0']; - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); + FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); + if (bad_usb->bt) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + } else { + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + } return true; } -static bool ducky_altchar(const char* charcode) { +static bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) { uint8_t i = 0; bool state = false; FURI_LOG_I(WORKER_TAG, "char %s", charcode); - furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); + if (bad_usb->bt) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); + } else { + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); + } while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(charcode[i]); + state = ducky_numpad_press(bad_usb, charcode[i]); if(state == false) break; i++; } - furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); + if (bad_usb->bt) { + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); + } else { + furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); + } return state; } -static bool ducky_altstring(const char* param) { +static bool ducky_altstring(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; bool state = false; @@ -276,7 +302,7 @@ static bool ducky_altstring(const char* param) { char temp_str[4]; snprintf(temp_str, 4, "%u", param[i]); - state = ducky_altchar(temp_str); + state = ducky_altchar(bad_usb, temp_str); if(state == false) break; i++; } @@ -288,8 +314,15 @@ static bool ducky_string(BadUsbScript* bad_usb, const char* param) { while(param[i] != '\0') { uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { - furi_hal_hid_kb_press(keycode); - furi_hal_hid_kb_release(keycode); + if (bad_usb->bt) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(keycode); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(keycode); + } else { + furi_hal_hid_kb_press(keycode); + furi_hal_hid_kb_release(keycode); + } } i++; } @@ -365,8 +398,8 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { // ALTCHAR line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altchar(line_tmp); + ducky_numlock_on(bad_usb); + state = ducky_altchar(bad_usb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid altchar %s", line_tmp); } @@ -376,8 +409,8 @@ static int32_t (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { // ALTSTRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(); - state = ducky_altstring(line_tmp); + ducky_numlock_on(bad_usb); + state = ducky_altstring(bad_usb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid altstring %s", line_tmp); } @@ -394,9 +427,18 @@ static int32_t // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true); - furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release_all(); + if (bad_usb->bt) { + bt_hid_hold_while_keyboard_buffer_full(1, -1); + furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + } else { + furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release_all(); + } return (0); } else { // Special keys + modifiers @@ -412,8 +454,14 @@ static int32_t line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; key |= ducky_get_keycode(bad_usb, line_tmp, true); } - furi_hal_hid_kb_press(key); - furi_hal_hid_kb_release(key); + if (bad_usb->bt) { + furi_hal_bt_hid_kb_press(key); + furi_delay_ms(bt_timeout); + furi_hal_bt_hid_kb_release(key); + } else { + furi_hal_hid_kb_press(key); + furi_hal_hid_kb_release(key); + } return (0); } } @@ -470,16 +518,18 @@ static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { } } while(ret > 0); - const char* line_tmp = furi_string_get_cstr(bad_usb->line); - bool id_set = false; // Looking for ID command at first line - if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); - } + if (!bad_usb->bt) { + const char* line_tmp = furi_string_get_cstr(bad_usb->line); + bool id_set = false; // Looking for ID command at first line + if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { + id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); + } - if(id_set) { - furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); - } else { - furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); + if(id_set) { + furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); + } else { + furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); + } } storage_file_seek(script_file, 0, true); @@ -535,7 +585,14 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil return 0; } else if(delay_val < 0) { bad_usb->st.error_line = bad_usb->st.line_cur; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); + if(delay_val == SCRIPT_STATE_NEXT_LINE) { + snprintf( + bad_usb->st.error, sizeof(bad_usb->st.error), "Forbidden empty line"); + FURI_LOG_E( + WORKER_TAG, "Forbidden empty line at line %u", bad_usb->st.line_cur); + } else { + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); + } return SCRIPT_STATE_ERROR; } else { return (delay_val + bad_usb->defdelay); @@ -551,7 +608,22 @@ static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_fil return 0; } -static void bad_usb_hid_state_callback(bool state, void* context) { +static void bad_usb_bt_hid_state_callback(BtStatus status, void* context) { + furi_assert(context); + BadUsbScript* bad_usb = (BadUsbScript*)context; + bool state = (status == BtStatusConnected); + + if(state == true) { + LevelRssiRange r = bt_remote_rssi_range(bad_usb->bt); + if(r != LevelRssiError) { + bt_timeout = bt_hid_delays[r]; + } + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); + } else + furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); +} + +static void bad_usb_usb_hid_state_callback(bool state, void* context) { furi_assert(context); BadUsbScript* bad_usb = context; @@ -567,15 +639,28 @@ static int32_t bad_usb_worker(void* context) { BadUsbWorkerState worker_state = BadUsbStateInit; int32_t delay_val = 0; - FuriHalUsbInterface* usb_mode_prev = furi_hal_usb_get_config(); + FuriHalUsbInterface* usb_mode_prev = NULL; + if (bad_usb->bt) { + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(bad_usb->bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(bad_usb->bt, HID_BT_KEYS_STORAGE_PATH); + if(!bt_set_profile(bad_usb->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(TAG, "Failed to switch to HID profile"); + return -1; + } + furi_hal_bt_start_advertising(); + bt_set_status_changed_callback(bad_usb->bt, bad_usb_bt_hid_state_callback, bad_usb); + } else { + usb_mode_prev = furi_hal_usb_get_config(); + furi_hal_hid_set_state_callback(bad_usb_usb_hid_state_callback, bad_usb); + } FURI_LOG_I(WORKER_TAG, "Init"); File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); bad_usb->line = furi_string_alloc(); bad_usb->line_prev = furi_string_alloc(); - furi_hal_hid_set_state_callback(bad_usb_hid_state_callback, bad_usb); - while(1) { if(worker_state == BadUsbStateInit) { // State: initialization if(storage_file_open( @@ -584,10 +669,14 @@ static int32_t bad_usb_worker(void* context) { FSAM_READ, FSOM_OPEN_EXISTING)) { if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) { - if(furi_hal_hid_is_connected()) { - worker_state = BadUsbStateIdle; // Ready to run + if (bad_usb->bt) { + worker_state = BadUsbStateNotConnected; // Ready to run } else { - worker_state = BadUsbStateNotConnected; // USB not connected + if(furi_hal_hid_is_connected()) { + worker_state = BadUsbStateIdle; // Ready to run + } else { + worker_state = BadUsbStateNotConnected; // USB not connected + } } } else { worker_state = BadUsbStateScriptError; // Script preload error @@ -655,6 +744,10 @@ static int32_t bad_usb_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); + if (bad_usb->bt) { + update_bt_timeout(bad_usb->bt); + FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + } worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; @@ -671,10 +764,18 @@ static int32_t bad_usb_worker(void* context) { break; } else if(flags & WorkerEvtToggle) { worker_state = BadUsbStateIdle; // Stop executing script - furi_hal_hid_kb_release_all(); + if (bad_usb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected - furi_hal_hid_kb_release_all(); + if (bad_usb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } } bad_usb->st.state = worker_state; continue; @@ -695,7 +796,11 @@ static int32_t bad_usb_worker(void* context) { delay_val = 0; worker_state = BadUsbStateIdle; bad_usb->st.state = BadUsbStateDone; - furi_hal_hid_kb_release_all(); + if (bad_usb->bt) { + furi_hal_bt_hid_kb_release_all(); + } else { + furi_hal_hid_kb_release_all(); + } continue; } else if(delay_val > 1000) { bad_usb->st.state = BadUsbStateDelay; // Show long delays @@ -715,11 +820,34 @@ static int32_t bad_usb_worker(void* context) { break; } } + if (bad_usb->bt) { + update_bt_timeout(bad_usb->bt); + FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); + } } - furi_hal_hid_set_state_callback(NULL, NULL); + if (bad_usb->bt) { + // release all keys + bt_hid_hold_while_keyboard_buffer_full(6, 3000); - furi_hal_usb_set_config(usb_mode_prev, NULL); + // stop ble + bt_set_status_changed_callback(bad_usb->bt, NULL, NULL); + + bt_disconnect(bad_usb->bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(bad_usb->bt); + + if(!bt_set_profile(bad_usb->bt, BtProfileSerial)) { + FURI_LOG_E(TAG, "Failed to switch to Serial profile"); + } + } else { + furi_hal_hid_set_state_callback(NULL, NULL); + + furi_hal_usb_set_config(usb_mode_prev, NULL); + } storage_file_close(script_file); storage_file_free(script_file); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c index 19576ddd0..494e56b1f 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c @@ -11,8 +11,8 @@ enum VarItemListIndex { void bad_usb_scene_config_bt_connection_callback(VariableItem* item) { BadUsbApp* bad_usb = variable_item_get_context(item); - bad_usb->is_bluetooth = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + bad_usb->is_bt = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); } @@ -28,8 +28,8 @@ void bad_usb_scene_config_bt_on_enter(void* context) { item = variable_item_list_add( var_item_list, "Connection", 2, bad_usb_scene_config_bt_connection_callback, bad_usb); - variable_item_set_current_value_index(item, bad_usb->is_bluetooth); - variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + variable_item_set_current_value_index(item, bad_usb->is_bt); + variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); item = variable_item_list_add( var_item_list, "Keyboard layout", 0, NULL, bad_usb); @@ -55,8 +55,10 @@ bool bad_usb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { if(event.event == VarItemListIndexKeyboardLayout) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); scene_manager_previous_scene(bad_usb->scene_manager); - if (bad_usb->is_bluetooth) { + if (bad_usb->is_bt) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); } else { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c index 77a08611c..7cc81de11 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c @@ -9,8 +9,8 @@ enum VarItemListIndex { void bad_usb_scene_config_usb_connection_callback(VariableItem* item) { BadUsbApp* bad_usb = variable_item_get_context(item); - bad_usb->is_bluetooth = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + bad_usb->is_bt = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); } @@ -26,8 +26,8 @@ void bad_usb_scene_config_usb_on_enter(void* context) { item = variable_item_list_add( var_item_list, "Connection", 2, bad_usb_scene_config_usb_connection_callback, bad_usb); - variable_item_set_current_value_index(item, bad_usb->is_bluetooth); - variable_item_set_current_value_text(item, bad_usb->is_bluetooth ? "BT" : "USB"); + variable_item_set_current_value_index(item, bad_usb->is_bt); + variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); item = variable_item_list_add( var_item_list, "Keyboard layout", 0, NULL, bad_usb); @@ -47,8 +47,10 @@ bool bad_usb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { if(event.event == VarItemListIndexKeyboardLayout) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); scene_manager_previous_scene(bad_usb->scene_manager); - if (bad_usb->is_bluetooth) { + if (bad_usb->is_bt) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); } else { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index 5176e3b28..eb72f1765 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -29,7 +29,7 @@ void bad_usb_scene_file_select_on_enter(void* context) { } if(bad_usb_file_select(bad_usb)) { - bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->bt); + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 1f6af5545..03a8114e0 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -16,7 +16,7 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InputKeyLeft) { - if (app->is_bluetooth) { + if (app->is_bt) { scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigBt); } else { scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigUsb); From d93ed003fec62ec8b9dc8a6b5ffbe93a25a0ef40 Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Sun, 29 Jan 2023 06:53:35 +0100 Subject: [PATCH 042/231] Change camelCase to PascalCase in code style (#2329) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- CODING_STYLE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODING_STYLE.md b/CODING_STYLE.md index 6c7d6d792..c62009eff 100644 --- a/CODING_STYLE.md +++ b/CODING_STYLE.md @@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept. ## Naming -### Type names are CamelCase +### Type names are PascalCase Examples: From f5fe0ff694fd5c51472d3430565a156a66a05c15 Mon Sep 17 00:00:00 2001 From: Petr Portnov | PROgrm_JARvis Date: Sun, 29 Jan 2023 13:12:24 +0300 Subject: [PATCH 043/231] Furi: make `furi_is_irq_context` public (#2276) * Furi: make `furi_is_irq_context` public * Furi: proper name and documentation for furi_kernel_is_irq_or_masked. * Target: bump symbol table version * Furi: proper doxygen context for warnings Co-authored-by: Aleksandr Kutuzov --- firmware/targets/f7/api_symbols.csv | 1 + furi/core/common_defines.h | 24 ------------------- furi/core/kernel.c | 36 ++++++++++++++++++++++++----- furi/core/kernel.h | 29 ++++++++++++++++++++++- furi/core/message_queue.c | 16 ++++++------- furi/core/timer.c | 11 ++++----- furi/furi.c | 4 ++-- 7 files changed, 74 insertions(+), 47 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 0e027a6a2..a75e88bad 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1389,6 +1389,7 @@ Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool Function,-,furi_init,void, +Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_get_tick_frequency,uint32_t, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t diff --git a/furi/core/common_defines.h b/furi/core/common_defines.h index c7acf95b4..1ec847d45 100644 --- a/furi/core/common_defines.h +++ b/furi/core/common_defines.h @@ -52,30 +52,6 @@ extern "C" { } #endif -static inline bool furi_is_irq_context() { - bool irq = false; - BaseType_t state; - - if(FURI_IS_IRQ_MODE()) { - /* Called from interrupt context */ - irq = true; - } else { - /* Get FreeRTOS scheduler state */ - state = xTaskGetSchedulerState(); - - if(state != taskSCHEDULER_NOT_STARTED) { - /* Scheduler was started */ - if(FURI_IS_IRQ_MASKED()) { - /* Interrupts are masked */ - irq = true; - } - } - } - - /* Return context, 0: thread context, 1: IRQ context */ - return (irq); -} - #ifdef __cplusplus } #endif diff --git a/furi/core/kernel.c b/furi/core/kernel.c index 73d2012b4..7928ad11c 100644 --- a/furi/core/kernel.c +++ b/furi/core/kernel.c @@ -7,8 +7,32 @@ #include CMSIS_device_header +bool furi_kernel_is_irq_or_masked() { + bool irq = false; + BaseType_t state; + + if(FURI_IS_IRQ_MODE()) { + /* Called from interrupt context */ + irq = true; + } else { + /* Get FreeRTOS scheduler state */ + state = xTaskGetSchedulerState(); + + if(state != taskSCHEDULER_NOT_STARTED) { + /* Scheduler was started */ + if(FURI_IS_IRQ_MASKED()) { + /* Interrupts are masked */ + irq = true; + } + } + } + + /* Return context, 0: thread context, 1: IRQ context */ + return (irq); +} + int32_t furi_kernel_lock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -33,7 +57,7 @@ int32_t furi_kernel_lock() { } int32_t furi_kernel_unlock() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); int32_t lock; @@ -63,7 +87,7 @@ int32_t furi_kernel_unlock() { } int32_t furi_kernel_restore_lock(int32_t lock) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); switch(xTaskGetSchedulerState()) { case taskSCHEDULER_SUSPENDED: @@ -99,7 +123,7 @@ uint32_t furi_kernel_get_tick_frequency() { } void furi_delay_tick(uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); if(ticks == 0U) { taskYIELD(); } else { @@ -108,7 +132,7 @@ void furi_delay_tick(uint32_t ticks) { } FuriStatus furi_delay_until_tick(uint32_t tick) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); TickType_t tcnt, delay; FuriStatus stat; @@ -137,7 +161,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) { uint32_t furi_get_tick() { TickType_t ticks; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { ticks = xTaskGetTickCountFromISR(); } else { ticks = xTaskGetTickCount(); diff --git a/furi/core/kernel.h b/furi/core/kernel.h index f30f109bb..371f76c1f 100644 --- a/furi/core/kernel.h +++ b/furi/core/kernel.h @@ -10,19 +10,42 @@ extern "C" { #endif +/** Check if CPU is in IRQ or kernel running and IRQ is masked + * + * Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK. + * + * Meaningful use cases are: + * + * - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section) + * - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK. + * + * As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior. + * Most likely it will happen after kernel primitives being used, but control not yet passed to kernel. + * It's up to you to figure out if it is safe for your code or not. + * + * @return true if CPU is in IRQ or kernel running and IRQ is masked + */ +bool furi_kernel_is_irq_or_masked(); + /** Lock kernel, pause process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_lock(); /** Unlock kernel, resume process scheduling + * + * @warning This should never be called in interrupt request context. * * @return previous lock state(0 - unlocked, 1 - locked) */ int32_t furi_kernel_unlock(); /** Restore kernel lock state + * + * @warning This should never be called in interrupt request context. * * @param[in] lock The lock state * @@ -37,7 +60,9 @@ int32_t furi_kernel_restore_lock(int32_t lock); uint32_t furi_kernel_get_tick_frequency(); /** Delay execution - * + * + * @warning This should never be called in interrupt request context. + * * Also keep in mind delay is aliased to scheduler timer intervals. * * @param[in] ticks The ticks count to pause @@ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency(); void furi_delay_tick(uint32_t ticks); /** Delay until tick + * + * @warning This should never be called in interrupt request context. * * @param[in] ticks The tick until which kerel should delay task execution * diff --git a/furi/core/message_queue.c b/furi/core/message_queue.c index 9a41f8775..ddf56f006 100644 --- a/furi/core/message_queue.c +++ b/furi/core/message_queue.c @@ -1,11 +1,11 @@ +#include "kernel.h" #include "message_queue.h" -#include "core/common_defines.h" #include #include #include "check.h" FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) { - furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U)); QueueHandle_t handle = xQueueCreate(msg_count, msg_size); furi_check(handle); @@ -14,7 +14,7 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size } void furi_message_queue_free(FuriMessageQueue* instance) { - furi_assert(furi_is_irq_context() == 0U); + furi_assert(furi_kernel_is_irq_or_masked() == 0U); furi_assert(instance); vQueueDelete((QueueHandle_t)instance); @@ -28,7 +28,7 @@ FuriStatus stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -65,7 +65,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin stat = FuriStatusOk; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) { stat = FuriStatusErrorParameter; } else { @@ -131,7 +131,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) { if(hQueue == NULL) { count = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { count = uxQueueMessagesWaitingFromISR(hQueue); } else { count = uxQueueMessagesWaiting(hQueue); @@ -148,7 +148,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) { if(mq == NULL) { space = 0U; - } else if(furi_is_irq_context() != 0U) { + } else if(furi_kernel_is_irq_or_masked() != 0U) { isrm = taskENTER_CRITICAL_FROM_ISR(); /* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */ @@ -167,7 +167,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) { QueueHandle_t hQueue = (QueueHandle_t)instance; FuriStatus stat; - if(furi_is_irq_context() != 0U) { + if(furi_kernel_is_irq_or_masked() != 0U) { stat = FuriStatusErrorISR; } else if(hQueue == NULL) { stat = FuriStatusErrorParameter; diff --git a/furi/core/timer.c b/furi/core/timer.c index be7efebe2..4b6ccecba 100644 --- a/furi/core/timer.c +++ b/furi/core/timer.c @@ -3,7 +3,6 @@ #include "memmgr.h" #include "kernel.h" -#include "core/common_defines.h" #include #include @@ -27,7 +26,7 @@ static void TimerCallback(TimerHandle_t hTimer) { } FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) { - furi_assert((furi_is_irq_context() == 0U) && (func != NULL)); + furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL)); TimerHandle_t hTimer; TimerCallback_t* callb; @@ -60,7 +59,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co } void furi_timer_free(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -82,7 +81,7 @@ void furi_timer_free(FuriTimer* instance) { } FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -99,7 +98,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) { } FuriStatus furi_timer_stop(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; @@ -117,7 +116,7 @@ FuriStatus furi_timer_stop(FuriTimer* instance) { } uint32_t furi_timer_is_running(FuriTimer* instance) { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(instance); TimerHandle_t hTimer = (TimerHandle_t)instance; diff --git a/furi/furi.c b/furi/furi.c index a616bce63..cc0e3f4f1 100644 --- a/furi/furi.c +++ b/furi/furi.c @@ -3,7 +3,7 @@ #include "queue.h" void furi_init() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); furi_log_init(); @@ -11,7 +11,7 @@ void furi_init() { } void furi_run() { - furi_assert(!furi_is_irq_context()); + furi_assert(!furi_kernel_is_irq_or_masked()); furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED); #if(__ARM_ARCH_7A__ == 0U) From 55054fc1a7467aff87f7250178ecd8efd9e7a738 Mon Sep 17 00:00:00 2001 From: DEXV <89728480+DXVVAY@users.noreply.github.com> Date: Sun, 29 Jan 2023 11:55:04 +0100 Subject: [PATCH 044/231] Assets: correct MicroSD card pinout in service animations (#2323) * New Micro Sd-Card pinout: fixing the Micro Sd-Card pinout for the blocking an internal sd card animations * Updating the sd card pinout * Updating magnifying glass --- .../blocking/L0_NoDb_128x51/frame_0.png | Bin 1398 -> 1424 bytes .../blocking/L0_NoDb_128x51/frame_1.png | Bin 1403 -> 1425 bytes .../blocking/L0_NoDb_128x51/frame_2.png | Bin 1403 -> 1423 bytes .../blocking/L0_NoDb_128x51/frame_3.png | Bin 1401 -> 1420 bytes .../blocking/L0_SdBad_128x51/frame_0.png | Bin 1552 -> 1370 bytes .../blocking/L0_SdBad_128x51/frame_1.png | Bin 1586 -> 1387 bytes .../blocking/L0_SdOk_128x51/frame_0.png | Bin 1549 -> 1387 bytes .../blocking/L0_SdOk_128x51/frame_1.png | Bin 1559 -> 1395 bytes .../blocking/L0_SdOk_128x51/frame_2.png | Bin 1578 -> 1413 bytes .../blocking/L0_SdOk_128x51/frame_3.png | Bin 1567 -> 1403 bytes .../blocking/L0_Url_128x51/frame_0.png | Bin 1358 -> 1380 bytes .../blocking/L0_Url_128x51/frame_1.png | Bin 1964 -> 2046 bytes .../blocking/L0_Url_128x51/frame_2.png | Bin 1956 -> 2044 bytes .../blocking/L0_Url_128x51/frame_3.png | Bin 1955 -> 2045 bytes .../internal/L1_NoSd_128x49/frame_0.png | Bin 1382 -> 1411 bytes .../internal/L1_NoSd_128x49/frame_1.png | Bin 1376 -> 1410 bytes .../internal/L1_NoSd_128x49/frame_2.png | Bin 1381 -> 1416 bytes .../internal/L1_NoSd_128x49/frame_3.png | Bin 1378 -> 1415 bytes .../internal/L1_NoSd_128x49/frame_4.png | Bin 1372 -> 1401 bytes .../internal/L1_NoSd_128x49/frame_5.png | Bin 1377 -> 1412 bytes 20 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_0.png index e82c6f2e9c6a2880b0b983e1a80497daac351704..759007623af4e16c1bbdd167750e2ee47152ff88 100644 GIT binary patch delta 602 zcmeyyHGzAAL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s00=W?UF+mzU|^m((aWESmuupzJZ4@lg~=R@POLy$ zL1D8O;{rx@FjGNc@++o=^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( zD9qCSiJ- zyW#1BPdSXM*p9d|D~2C5xiCr9gyl|;y#gb9`nMI)?=+sNMr(WO`!U+tTj@JUKXd43 z{Gi!Y|7GU6jt?Hb4EH3G9nM+%8b4F2)?(by@44lvs=}@Yjs1)%+zm__#v8t`=4hJ9 zc9Zq>j%)|lW|jln)juy-^<5+&DE+5E13$y-z#EJSoEb6Uu?I`}&M{_8KU?Bjaj>zN z_m_3M_?=#n*I#lSJ}P{gZNT_o%Z7!FOEkC{b@}3-$_-7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM;Hv@wJ z000J1OjJbx00960|E1ZV-2eaqd`Uz>RCwC#n28R9APk28zW+;aS)`X$px}`Ow|GRb z-+KKr(0{`l7Qkx&aJpR&06avX0Dc$o_ay-EIfH{^TdLtk0MHzOa;2wyclM-QZUa%U z*?=0=nS|IrRQv%)YIbt+Grk{?0pKUq05-pwu~PtAJs>|`oG}Yvp9554D$(WetC@Vy zl=hn%(E?Zi*4PYyQYWBlD+Vc3`Y-|@?Vzd!+JC+-=6(R|N}K^;+JzZp?UQEd1aMuB zyA)9*6ao<1x2G8Z69Ut}&j2tD03M+30TOeLpeqM#2&Aq=415ka08T-=sSkuczY~BF zJj!zbgl7PTt6&-cjRQ2wo7V!{{qg|y8K7e=P_KbWfR+id7AQ5evAGr?h>m+N$N=DA zS#E|JK=fvk0zmAGomvy>1UV0sr5x0X(X4fA078%qK`4Pm?pgpa2tXFVER5R#FcqLX z2QbtJ*n+H*(N_h70O>yP9{^oiFdu-LpNO{Mm#Ojkvw8v0M}PqUpUe`n6H&y000000 LNkvXXu0mjfS)JjK diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_1.png index 58dab74d4a618d0ccc4674be0754b2f11a28e111..c9810b61e34bfc61f4b62a26225afaab6a05490d 100644 GIT binary patch delta 603 zcmey(HIaLQL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s00=W?UF+mzU|^m((aWESmuupzJZ4@lg~=R@POLy$ zL1D8O;{rx@FjGNc@++o=^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( zw#+_v4VT99ldTO;^Yd!VcHOU+&u6IPQh4!Rae=@sf5r)id;HRkUwNA| zYDhC^zKm_2#HKbe>{OA5@tck2OBoCgo|as@q*SYF^1Har;g*yxYf5 zObhI0*hDuhb6mKJQ9RhubiypFDNK2{dZ`~99 z7&mBk)t@k)DL7BLo%usHS4X9K+^-mw;-ie)W>v&TG1{gf=iXSmvmTNHLopF6}g0)F({~@jmx34pL>g6#1O7XGdSBqbDyZ&o% b*eUK(MH8nPTONl3(*uL2tDnm{r-UW|yvo^{ delta 578 zcmV-I0=@l_3;PO?7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oOC5CQ4{ z000J1OjJbx00960|E1ZV-2eaqfk{L`RCwC#m9uK7aBSSEh13Cm;RV)TJ2MmBnK-$s=sy^Qd zAP}f^+ze0@;x>TEDp&?U^8ikHE484a3ITxr4A7kxh!?>SfM-HW3xpa9S3V3-jgNXS zh;sm-u`EL$pnNk40if=SLahmXf|v)=QVwFpsMflD0JuY14M9}`lic|LFa&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R0E8K{u61%UFfh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD7gEebrY#pM&; z)Nh_Vy+fg+g4D?$Q&(m^P|J;PVZPhH(v9(An@T*T|+p7|IW>`T;td(YD#u8JhAX$_#oi=Y^Iyb z6A#q}m3n*j?+rcHZ-qCW&{Jiw(@K8JDG=;%MW5kEf(=6fgB7ce*nP#sC(U!3eDBmc zwDU75JQKUu)x1cyaJej#)6sN>8m*Qgr1$WJiwROa&G5npZC0?L% zgzZ6%x$rZF5NYXk4CjxnIKxnL%Ao%W!+*IQi_;lye>xJe-Sd4dgTe~DWM4fw#Vk% delta 578 zcmV-I0=@l@3;PO?7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oMwV421M z000J1OjJbx00960|E1ZV-2eaqfk{L`RCwC#n2QdBAPhxs|Np1A7@#kdg2-ILw)lu> z4{hnS1AqPagAMQ+09<~a2LOIVAOMb&`tKzGusMU1eJs_m5CAj>AfL%wz8igzPt!nT z>|#KS>P$kUFJ*tgkg5$I9LD{C5rFcf7C`cw8ao9b>H(fSJ7X4r&jHFY<>;31i#y9T zwY1+|YxxTRy0lh_w0q7&Z07-ZfvS0hb Qf&c&j07*qoM6N<$f^|vo?*IS* diff --git a/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png b/assets/dolphin/blocking/L0_NoDb_128x51/frame_3.png index 789dc8822e8442ddd49ea503b9ac8d109475e1ef..b48aef97839e4766b73c9630ae25c4fc2abb3d3a 100644 GIT binary patch delta 598 zcmey#)x$kOB9Midfq`MIpR6*FVk{1FcVbv~PUa<$!;&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R0E8K{u61%UFfh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD4`zo``_31b32qV@>rWQ9?@9NxS(A+JlrtPTc2?O zYs0iRdCiyD5=+-k`+6bm$K#AwOjktKhi}_-b>^F8cNaffU(&9?&yYJcV=v>a%#f-4 z43{6gi)0d!7m&9TDAJ$7za_0{SHtvg%zKzBPR+fl^U3JI|6qrS-W1%uJMFH1XI%QP7`_(^63JMb_Du4nvkZU$okLlmoyh3c1@3C8n+Z@cV{t0*s(kaZm5ZOoNS`1{exrSNu33E&5X58#IG@YS5MbV;d#e+ zYWqwB#s#--6fjnprBwozw^c4-`cWlNC&!p`TKK-&-_Wm86D}J`JKa<{xoH(^ON(Ly zlftJJ3My+De!OdTK2#%C)BdbXtgGd$#g0`!q!vu(KHXp@X}zDl+5YNN|G(PGznGtL WnK&{0-W3K+3JjjEelF{r5}E+=mf!XO delta 576 zcmV-G0>Ax?3;7C=7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN5K?$w^ z000J1OjJbx00960|E1ZV-2eaqe@R3^RCwC#n2QdBAPhxs|Np1AEI=O=t@6+%xXnoh z_E1W%9e?QI0~_Eq060A60{{;Z2!LP1f4>9(HfGTHj)fW)0)WN<P3?TVU^&J8b^#D(voiPfajsePR%Bx$JUyLlj zsjdB{M6>}mz<7YXPe7-s7&HNZ)4{10X#1W^;(wF2Tf6{3r3=+b*-y3&LVVIP%atre zNJ}!Ebpog*Q2DzJK*BI}0>AJ3020R{j{A`-F)HA1og O0000U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iODOmSOyv4Q%M`>aOn5*dHIgX+W?QkAwwy`O3H@L=4^)oo@i zzKS~6l>_CIVl`i!?wmJiOC^P?u>qNI^UZB z_TvrHHgG$v%Vlz4ePO4tWOtCCvx9krSD@q};RpLxJ`q-W(!~5ma>3c%THX2ywIc{w;!)foz+D=s%LwzPi+6`4nyAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oMEGI*l^ z00JmUL_t(|+U#2Gj>I4cJR9Htm3y1poM}J-KWNnsKQ`Uex^+fiU=8Pa{=lBI0}y}! z5CD75Vt?sI2*K$mk#%$r@I&8w^4&tv`DX(!U<>2`?jmhYlb))5Jp3E^+V8u3hB)WG z_`GQHnEqu~IUo7y|>!DM4zFZj0QpJW%S`&z|Xm@w-`6)LFY#hn+m z91!%sUrXEYdY3;=23%*$a<39V-iJ@f{brdE^na{itqnLm?}E<;bG1f-Ask9JZNT@Wp1^M2ZR$(1-DI zOn>O`sBI$217y*rYG>oHr9+V2IYuU7l4s<8S^&>QfFSb+{#`Xy+2txdVy(au1ipM^VL9lB zuOf>~MK7qi*aP~)wmxdnm(ToM&m(=2zagi3NQd~FXriq;F=f!015yA LNkvXXu0mjfqA^8s diff --git a/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png b/assets/dolphin/blocking/L0_SdBad_128x51/frame_1.png index bd0f2b9338264ef39ffcf378f9e6017edc05286e..147561f0a40ab1c8ac7134844843d659783c2109 100644 GIT binary patch delta 565 zcmdnQ^O|ddL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD7K5f@kuUvl$d`dceBj?BVltoMHa~b5`YnL%L=JGReh8H(%VBLSrcGDk* zDh8IljMrOF)S5HQkYrL4lStTm?Zq_NE6?Wsy({qME`!`B%ibRQd*1h(UmW3g;5s-j zwBeYM8RI0z8O%!}WF4;8hz8XgvKKr3id86X=sUJw5tmEpqsDVK~`H#}*0;5XZpp|#)pPTYlQP6`fNS=RV0Viu5-JENbR%=aaL z{dmKy4crf&G9D1!z{{X*(B_oIb~;9&;^;f3d9JGO1rk`6@$@hR+3qf43~ZEQkJuQ) ze#5<1_E8QGV`Z@>qt+~Ap~V6_%oR3HTRMl)aN!pghG{0P=C&5In!jnSVc@D}yl|#< pYS$IHNJgvo^ZzgZ+bnj7-E@s%@+~b+J77#Rc)I$ztaD0e0sw!z%fSEu delta 763 zcmVAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oO0n2LV@ z00K%$L_t(|+U#1{j>8}fED`_z%h{^cqR2M33AhPl{IE(D!ZCx-b=vp+1-VW(KmYOA6dF+Fn_yp6!bpi7k6@Caxsq&{Mz!rl7BzSD_O;&bERA51j7THWi=qk+FW}Z9! zP3kc8qR=tx_6Mw+Gb$kQR&6Ecuz7t(kx?FH6VW%pRbX`vTTCtZVZ}0$5d|seLwGu- zRDXEPGLfbMdeCOZ&dy;|n;^e(jC8`p&*=5YRXG^WR&LckT~VCFmVwwT{+MMVT5x|( z3W`Jw!U#a`!tdo|G0yy79gvM8KxtMeuO56Bl`m^5&hSp zeSxJOMQCdMCEXA9jO)^RdCfIJB9Midfq`MIpR6*FVk{1FcVbv~PUa<$!;&U>cv7h@-A}f&37T^=& z`v3obAT#vPO>_%)r2R2!t6$HM|-a7?@{H^zvuo<(fDvkC~TCVKN7!6DyEb zP}uCnxPXxz%v4aA{EBH|y@I|SglnT;l#*uUl3JWxlvz-cnV$z1hlmEbxB+=K`W03c zxdpkYC5a%OeMLcHa&~HoLTX-$tx}Paz1{yC;&wpydwRM!hIkx*I<0%%VFix1pZ6K= z{ZBo9G%EM%#xl>%#UA3~UGq=4de^%stc|H>DEZvFp6SH=YzDTg&u=quY|L`_$ekEk zD0CpNmf^y2hOf>MXH8twQnj{D=5c7wI-OV@%eZcp+h>zD_T+{;tR{K;m@9Y^m>XOD z41f5k{h*gK?#Oig delta 726 zcmV;{0xA9L3XKeq7%~U~0002c0+&Ys000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNTQ!`Ki z00JdRL_t(|+U#1}vV$NDEDrzw%bYXq)JcKF5Qr#lo={sZ*<4pcO6dieCmU1%0001F zo<#0J2!Fw8No1X45Af5z_vEXEp!Al31IU6oAbf>-Sk=!s=QBGcU5ielpD-?<{oS|d z_#Ek9?kQCeb&@}6t6LKi9u`&&?{P`p4J?11SSB_E36|c}Slt&w zC+%}O*_z^vzBNaNFeYK;M@}3JpGT`|zkm7>syud~A_OO0*zkpqP|Lue57)70LkJ*( zu9XU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iODB1mW<1j6)WVvJ9oPPxyQ!48jGOlskvre!-&%m?l{BlNtq^k{E}{ z?x>7senJa+~jX3G0qkQoWZYdcR2>Cs}-xt=ljs?3_^!8hfzIzx&1YFU5FJ^T-N zN?o)M)q5-R__NnM>H7YwdzSvx_g7t9tG^uLXoygq#8Akh|3d75X}c!dM$5~b2N)lS zHdL?&^g3ELovr54IKJTZ!A%FQUo>Nw&0u%#lZb*egUi2**Brxl)t>(A^lDvxxt;8b zuTJN$pN`RbT4}zHVcB;_W(%oTOcA>#9(d~aAi=aDI!E1x;YV~eb3*gY+Vjn8C3rS( y;PLpx>abm0Z~d{C(_eiGOBN5?{>$F!3-e7Dv!zz0+0TJ7&fw|l=d#Wzp$P!L_tz)@ delta 736 zcmV<60w4YJ3YQF!7%~U~0002c0+&Ys000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aik!eo>Ad$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNGb`Hz{ z00J*bL_t(|+U#1}vV$NDEDrzw%bYXq)JcKF5Qr#lo={sZ*<4pcO6dieCmU1%0001F zo<#0J2!Fw8No1X45Af5z_vEXEp!Al31IU6oAbf>-Sk=!s=QBGcU5ielpD-?<{oS|d z_#Ek9?kQCeb&cR(W=_7euOHIU8o4bNf$PJ;Um;C zFzCZ|?AZ_kh@fjF26!p&qJ6!M0)X*_ja>KxfRiwA&jC+Xbf(<$&7HoD004F_fzx&U>cv7h@-A}f&36W|l# z`v3obAoK6vzyJRI`{g134#?pw@Q5sCVBk9h!i=ICUJVQk%rhr?`7`lyO`Mg-%*&-P znS;@Z6-X;6Z1!SYz{n0}Dkw~T#k8Y^rpl>0b=AFvA)PXxA4=E<|EgDRR_$kXFpuBBrLg}RLqgW| z-HbXcTMdrtvt4QNWVln!zF<0|RJ+67lI}C7eO{>>Gx)C3J^rf z4qTtP9#|&q7Ic20$^L%jk@xHh_Iu23{9=sgyL!XtS)00bPLzqL9s3KnBK1xYhoApg z5@OG09p64Dmi+Y5p9;U(`@Eru??RgX)yDJP%WBvYHr``Yu+?Eava}&o zw;+%?o}q1*tJV^3hx$*S#143HFODdGAEY^dbs<~9Eq*Hp(JOog9#(x{Wi9gq*LHum0&Vv8-3GiKkC_`5ij}!PerbR8<iZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM%Oi~C71l@rZ^CN`rWH(PVL%q>bkUycgPaP-qu>$(mxMhY~CNP0@yp6uo9o!5R!k$u$X4#%CD7S(J% z`hR87A$*qJ`BIl#-kgE;&9&sq$)Y z|G5W~8GX5!ph=A-a`6;g(D+Wcbar4mKmpbmnqCI_M|(h4$$^JBS^*{8K;O3ts`hB| z5%#SK2@jJ}HDXrmrZjxzp<@rr#D*Y&jejulQ1>-|C-gZ?3|x?@HAhNlOu~8wO6U(} zMZfwdRC(+|MF>thvEd7ULM;P>K3vD14IzLCSSxjaLwOhU^)?Cxj4y2D!XFAaNC)mY z;K@LjOmG&9^lbzb(9R`rh?|-?grJKxVi3O~yIL|K1hKLH7&dfiNZcO#xN@v_og_2h lG!Z!f3V=WXkCi_G1^|H}F1Di~quBre002ovPDHLkV1k$oN;v=k diff --git a/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png b/assets/dolphin/blocking/L0_SdOk_128x51/frame_3.png index 614ef9c5aeea94fc93a47ccf41b0ba8b4c8ee7ee..1e52f1513742e3b985d00385f5e153ccc3937edf 100644 GIT binary patch delta 581 zcmbQw^P6jeL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-Y=;hDE%QbOU9y2eO!ekCcCsrV> zps?ACaRDPcn5m#J`4!W`dIfzu2-ilxC?(CxCAB!YD6^m>Gd~Y34iOD9odm%?2E0G_Ydu}l%9{f|7{Pwvlh}5fR*!A_)YKG3&QVkQz_DVB|&SDFybIs7w zW0JVW-mr&fgT<7zH1{WA>x`^p86$%3%5J;PE3hif#QvDjL5T(BjGAZC8%{9(QCo1z zneSe3=YRPFe?H7Q@SJhs=e!&(^T*Be_O0O&ox}V_;o?>ot_APx858d9DSLE6u(lx~ zLLp2ruwLqD0l&h#oF6Op2Yuf9Y4NNjS=-}gFb0IJRCBoLBvQhkV10N>^P@SNm^Uz5 zFnX|Mlr(ISxuW~y_ld?QG8@7i#1C##V6dA~$o*jBJw^lD7^aLZOzXcfWtl(VOt`UK zM*LDi?fXjpge%;OBlzD3Y0fWy%=Mr|uB2h@LcWHjoh;W}W*#{7ib31BkXd2hEpvvz zl0PXd+lvfrTT>k-@iVmTE%;$F?_Y3inJItb)vND2PROSwnAsjIw2T7AJcFmJpUXO@ GgeCxpA=_&J delta 744 zcmVAd$LmlPdv2vvL8T0h1X6p?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN1o5w={ z00K8jL_t(|+U#1{vV$NDEe`+x%icTflu3cautX3}p0HY$ll`bW&+`QtCkG&a0-ylM zIPu&K=YO2hlE^yO9$<&Q_2jFCpz|#q2apAGfcpydu&keP&R6Y}bS*lGenP(h`oXv8 z_#SCrZkH;Ex=7?Sk^M&g6j`*!&x93=J%C%ddN3rb2hRwo(aZla{e|9YcaG15>HO=2 zAQ}f|uCKVg`4?npw(y%Ao*H0uDLk3}P6ry34}VoS_pf}}VC~E33AU?(pa2LIFs0Nd z;UUQb3ixR;if#=jDk^IB^f-;>gQ^h=&0A5S7S&$Qb%iXsWJfpoj_4~VsW8UK5(V-= z?uxnDs>@|=QF?qiD){c`cho`F7&&A)?uC0MqHD6plofEfsAlti<}6B2RZX_E)ECOt zTYrMmQG~?}39x$I^DA9~p7md>xS+mOP?kT>+&rG&~IS@Ad#1WPttv1*C8TecLK1+gQoX(W40o3zN!*ci&ij zFKPJ7myRusJsW}q3Zba6dan5!q0eD*6n_yo`qmsNp)v_8KXT&K@VT`r`q__A<*^AB zAvo#6hA(`Cng#}axb`g@LI4r4R_XvRU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O+BKL|5gum-CzFfg}F^zvuo<(fDvpP83SVKOJ96DyEb zP}uCvxPXxz%v4aA{FCzM>#8IXksPAvG_>R;ftI-tPJ7X{JE;hkLp>hIkx*JB>T9#em1f*nGy$ z|Eby5iuPu^8MQ*QUb|*Ap6Pz}&n8*Ht_WcUcAM?y#sVlDj4O{@87Xw70?z zOtDLMGhA>x_Tskp494r1en=$vMe+FDP5PkQ`o)_u;=vE53l?{UU9V1RXHZ@cx65m( zcr}B9!b0DUzz!>)Ejy10AK1XSV3E_}Ka%ASjxKn{;M60TAZ~VNS^|^4gZ<|J)5Tx$ zwM=`iTL0vyl!JCd7(;Kv6+hbp&(pQCUu=G`J)1|uu;I1NE?qX(h9A1G8caV0J6Scv zF|69lDzh$l*G_G5ML7ns$Q>L9|T#nOn<}vA5HgA*J2Fp};AT{lWY2k31Lj5}rDK`NEeV z%KS1?%#87D9b3V>)@+8J$ocs%KTm)4Eu{Wv&@ZO<4Ti?5S8OVQ@y_7s>gTe~DWM4f DD0JIS delta 554 zcmV+_0@eNG3eF0U7%~V00001JzVIRd000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000ANk!eo>Ad$LnlPm#3vvUEU0h1a7i+?XZAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oOw-4GW5 z000J1OjJbx00960|E1ZV-2eaqX-PyuRCwC#*nzHtAPhj!+yDRRB@Ss}1wldH+cZnm zMClQs#eX4-Hy*&B0Z`rM17MNB0JanV_XU8Y3GN4wgk1#KqG&8`9fa?Qgn04EZUxA1 z1Hv#ugWfy_{zoQ!0NsF28O;DsR36x&89oEhavFCgl--p9+btn3?(3n>E2o`FcbVTpnNNCEilzEeK&If&fCe>1s4IfYUG_? zSz8Vn!8JhkSwJg*Gb^teAnDu|fC`2H%$#opFcfg22V!MjGzJ)z2Tsm{ae%%&@NIyG sS}+qJ2QAX{WjL=IKYvy~0C@>806$X`4`zs|dH?_b07*qoM6N<$f+3vSUH||9 diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_1.png b/assets/dolphin/blocking/L0_Url_128x51/frame_1.png index 69f1fb365988f248aa238fa6a2d672fa7d8bcee1..9975ca3f042623b1cefdd5eb913a804dd185d734 100644 GIT binary patch delta 682 zcmZ3(|BruyL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPAbocRnwN7ht`GX(lx-}lyu z|AzPDq#B&{BP!Dh5S#|Hi>aZs!4%{dD^NuZ+)Mju{Sg6x+O`tx~ zx3|>Li$7o$b4%R9xPOm~HzXx&Wju0&@r&cttE{FQn6?DIihsBwpP}$|Y{yrw24@C# zhHVWYZEOtIKK$~pbZ(UNHCFH>lu6wcYG!VbSohkY=*jMu5QaF0O*JfYqQ(0d=RY+# zB7C4!%#1ODIb?15v>z|`m>=P3SS5Tru47J)KjYc#lhPe|KJU(%Da=>CSWxU>!clcV zaH^$4?pnh~DeoE1%)iIUuq=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YckBCC zYX%0!bWaz@kch)?XWZV@tiamQiU zxS;ARdqX=1%Yie0YZx6;ntn5O+_7UxNb8<^RD8SI!$%XZ-nZKv&G=?J<24f{i@lcY z|J>DU7VDGYvW8teSGB_FhEI#%ayX={<85eeeZw?iRzCX!r-op`3(8l8Ef1L1J}425VZCsheING% sNA5JH8;!2B{MtTW`SEx2FV+qGb}NPXFA6B10HzKGPgg&ebxsLQ04xUSEdT%j diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_2.png b/assets/dolphin/blocking/L0_Url_128x51/frame_2.png index 855e7450e53b99c522d473e9e913271be4b36536..84241c3f16ab65f696964e17ab96c1deb8a7dc6c 100644 GIT binary patch delta 680 zcmZ3&|A&8qL?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPARUho>;uzv_{Oz>tqQeS2EnCea?*892QNUkV$miC- zjMX12at_sT*cLpwe`t&Oz3a&gFEkrIycSS!*;|snPr9Fx=dC&eW9-u93>VUt-8>$> zgX!JUkI4;|nT7_X-3OL8TN_JU(V znKXk*Q~sk_toF&-JTsCQu1A(c^Dr&2I~FRt!R_Cq2}}SSw$=v*})r_JJ=3#c-)oHf6uV&>#>f> zdl?tqo!)+fwPA)WLs8r}hJUr8=NQ(?|99q}#BJS>Zm6L*CvD-bf(Z6z;rrY(svpWR nB%fIC{OjNRSKor_kGjaE>KGbtX4qr|Ok50}u6{1-oD!Mq=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YcjC&P zrwj~?@t!V@ArXh)&bZxoSb@jo{Gb2+-xXQTm~LRbRG%vx>ZP@HUbA5`$F84`7=K7G z%(`C1u%q(;gU8kL;tVc|H~1Y?s)ZYtD4$z+^qP{5NMTi7N;Kn_+YIY^I2O&$eo$}m zklBF8z~fjBL--9no}CP~@qcqA7!I!VF03&T7GPZRt1?LIT^Hj6lS&awJEI8|?i(1E zBz+5GRXEe=&02r#-J#+`3H47Hll~s4uw*TKBExWuF{P!H*Fo6f=yJ7&bD6!IS*r!4 zEw3>J{5+S!)e!l4FT<1e-+LuLu*}X@P`$B-tEg$Oox%5%FN_SbdlOU}^pq}@ j8bn|3oj?65e?#1h55oNRTYdxrQw4*ktDnm{r-UW|Ys2O5 diff --git a/assets/dolphin/blocking/L0_Url_128x51/frame_3.png b/assets/dolphin/blocking/L0_Url_128x51/frame_3.png index 9e9a79405817d4f1f3ec242de249beedac773c57..c44b171bfaae20b74657855c263a48cb0ca3ae85 100644 GIT binary patch delta 681 zcmZ3?|CfJ)L?8<@0|UcaKUrlU#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O*?7=#%aX3dcRs#2KflA_ zpXF>ll4hVPeRO3u`q)f_sB=jzPA_)(Vst@sWaSZV|{&re+(P0IimYdRR-qqiH)yR5d*})}m z`?vL|CtKh0ck=K$^}kE?@1tT{wuU!M9J|dJTTgh0y~!<>&a&}@i^PKDw=QKk_mO148k(ujE_RS<(PUJYUVI9Fmlau?r~Z&^K=30ixu^S z3`e!EoSy%WEsW<9Yrqn}X2u2lS?i($8bue>Zrb17`zlC$*TdPmE&JIfa5Cr}<8^Rz z$dtEjSanw5SMUbO*_I2QGc3FOW}A$~fjiBYtrF7yl_-b`Y;<1c{59BNB|}$^-&3t4 zyBU5imdKI;Vst04103%>V!Z delta 569 zcmey%znFi5gfs^;0|P_Gz7I-3iY>q=#1%;Y|NnpK&G~mH28c7U-kg}}&!{kQSN>!j zMyJiij9-~1=d&!7(YI5usmLwx^|kWIEH23}ss!?jQqrt~Qw!}Td$69Z2Px2pi2#}C ziotR&sl~}fnFS@8`FSwqK`w3}W`z|{LvCtGB9LQWQIMFNom!%hnwMg$RHS5YXE%4t z0R{%fI8PVHkch)?XLR!&Qs8m*|NnovubR_ouBJ`r>W_<^-FD=w+&eFBC3X2226<+O z?-p_leo6rhw|oEpWMmL-s$l4O!Okg=ld>|y*8Ty7=GqAOmP#r_(0}IpH~H= z53|ptjZzA#Wn72j8YbSZnZd6Md9nT+5Rr(SumCB;Q{Rp6TdSUa4UF4xG}t7>ZrcTD3IHzmT}gp z+Pcd zYZ-)h@jh7VGmT+|r9ImL?dU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODy-7)iZ-yW^cKASs1s%!TYpTt@Q=^YUv z71E3s9z|MqZhqO+z!AgXaF0!9k{Cmc{DWD~b_;&8dr+eQS#MaGCx|y}NpR<3 zP(OIptDbd(nU`bt>72mw1I;@H7~7rAS!;IkH!NV>`*+nn;fhk{fOoRD7(tYR5DGNX3G}xo6A4`&apd}80Y#kUU=8jr}l|)!F2rx-0IKMfN{^@>FVdQ I&MBb@00G9?xc~qF delta 551 zcmV+?0@(e73+4)t7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oL}eh47| z000J1OjJex|Nj6009C@%egFUgW=TXrRCwCtn2ip@AP9u--v6bSt<$Lkg{pKvW?7nQ zxvwaQ1%F~1i-z#lh(Yq%H(_gqvV83gECM*!&j&z5tqP0KouFdS##onB48q zUkUJZKc#Xg?L7HPf}Quk!5{z>)07x9@WLp_=6?kM;`x_H%>huPI_z};n3l!d6m$~- zbbw3tf*RmuU}6gB>aPF_FH}TJ&jZxYB~56a190kt3c=ZHlymlHutNY;m9%BapX#+?Pm2=7!0A+&c)!6Oo z=~z#K4?zC9GRm{d0Elv@P~)?H_fhPw02o6CH~_$P pEZ_$H6MOBI0I)7?qq&;^0|4Z21;)FNg7E+V002ovPDHLkV1nMUU}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iOD+9 zf0HHJC8H14qjK5W)do3KJdj%!Q&RggW877sQGTozB4}j`kuj#_1p&zg}wr&gcZ{N zI0cwff@PQ{`26%}R1Mt4B*yqF^cl}(uSNN9`1h=F*dNUx)?qZ$bluU&0EPgD1}o_# zrX3v1>ZeIH*liYA@@m(jyaxSmPE0J#i5!0n`WjX=x<3!CR$JiX9Fiw&#bCm;hM|f< zV_n`&u>#$rKR9oS34CYL5I@1n7}MEU;Sjs>0_&B3D*m%iZfauEe7e|D){Q~qd7olu5+Z>X@>H4PuH#$#hsOG3V$Z7UH8dXogv*L zy5ZEmdd}Gldour*{FAycM_YoSO#emA?ef_SheRB{FcyUBXO=|lJ_n3?22WQ%mvv4F FO#pDe(sBR* delta 545 zcmV++0^a?C3*ZWn7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oM(r97wr z000J1OjJex|Nj6009C@%egFUgU`a$lRCwCtn2QdBAPhx&|No~KryyWKN+0e)mcl00jdy=@o$z5*z`P!XBl2hhKlG@-c$Ak-&x2v)DMQvFLVtUGpu>T~_Fjeg%w z_5ZnUwbwz(JAsvkkPX|Ww%h>Rd2tyaJ>Vw9buIA(z~x9~)pAc^2tdF9fKvV_)osr} zl~Sn~0SuO!0r0PRF8-dWXe$7qvScywDJiIy0K7%>D40r=^X-HVz~FZLH~=jN!MMUW jzXCRNfP%mP;3dESp_c`?`EG1V00000NkvXXu0mjfVa42_ diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_2.png index 12dc13430908844d89e0c7a002030cdc4999124b..5b474fff262306ecccbdc2a0ace273bd26e55219 100644 GIT binary patch delta 588 zcmaFL)xkYMB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODv^(Izmxfoo%Z3S(t+2?qoBLQV~aLI#7j;5jElsuK4x{$WbkrF~__S?SyQ z4Xv$nD`T=F?l_n+eD*4}o3*)*;cTVZ&nds`8h%zWE%E-i*H6c~{A-oBtp0-CE~X9k zT!Gfn`<94*+8YqlaIVk2q0mO>u*F<$8OCPK^Y318J6LGRIdI6=gzKB#_`MI5Y#2OU L{an^LB{Ts5>L%C3 delta 550 zcmV+>0@?kD3*`!s7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oML+;Z^% z000J1OjJex|Nj6009C@%egFUgWl2OqRCwC#n2Qd>AP7ad|Nqm=Qf+;Jq5|2LnPus! z<(>+HlYc0tp=cD|8qsJ5PA(8Zl4|Z_(9ZOAU4Rn-l$~!8@dGFQvfBv&>H$6gppOT* zhqwTMRsbAU0Tu>%0D!&%fPNQX?WsRN9Dral3g9x<+XRIHWDG3m+W}n0I-@gvOMt8n zaB+b&BwcZ^=QDub1(nK$_a1;7*fa>!TM`2RN`GHB#2Ekv>zzgb$YF8j0xV{s3=okC z(RSAWkAVXh%=IAw6thqf+317QXa2fAKehl=`VdM%wPg1IR`t7FSnt?j>3=o~N_}p> zY*mP*g;2dew_CNVgFCws1Av*0Wq=75xB_GefV#G_5teT2W^fZAUqCMVN(UYS5TvqO zI#(aIF`xlxHW_5P3Of53#NpHrI{^GK7k~U@z6y#o$sV@#2M-hlAlM^6fMGt;=h4j& om?U^|JQ>#kcpg9-0=xtm0DO@JvS@mv#Q*>R07*qoM6N<$g7Hz*umAu6 diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_3.png index f17f8713d34b7702ad58def6cf332b1770412808..952f968fb673a5de24fefb834e6c8655cc29a6d4 100644 GIT binary patch delta 587 zcmaFF)y_RZB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iOD!g+4&{$9uXshIkymJ1uh3VFezSm(p9_ z)!&?WBpwptZfZ+(^0SUjtyk(cTKl47|Yk0XXAgSzR zb~yt#x3x!1_DR=AtTVz_eJ*`=GxU&o!?Qix-qkRC{>tFsCH3#o^V=Fv&fFDAmR(-$ zP~Q29^B{M^pDTYiU3-xXHjCsf*;S(Qsp`MB3A-}~ky{o^?wipBB OpTX1B&t;ucLK6VtnBzwP delta 547 zcmV+;0^I$F3*rip7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oN4z+l$^ z000J1OjJex|Nj6009C@%egFUgVo5|nRCwC#m;nxhAPhzO?tkfJ5fr2dg+jJv#26>6 z|6xn1iht~BBu%!ro9J!^O>QGXP3m@^hc>6**9D6R=&0s#360Qp@2+rRz*`v58?cL5y6N}I?wfQW%X-w)t0Rv2yRdjdpt z(BgtHBwT5*^G|@%1)0i$_a49r>>7k&mZAV4;eT{O%mMJQ-sK5^7#4Fbz?g+1z>ZAF zy*mfE3{1G7txpXgc?ubkk3O2d^6Gm17y{_hr?M4fOLh#vs$b;7ykf`G|7sMZ`dYp0 zwGr1ALN7P^ lpO$=HP6xC7PXN3G7yx#e1*^`5DHH$z002ovPDHLkV1mgA;jjPz diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_4.png index 7a6992a2b04cdaf35abb8c23f269293e143a4ea0..2bb43b306f3bf9025ba68190fb4903cc5a132133 100644 GIT binary patch delta 573 zcmcb^^^;M1%fy~fDm+OEOXMsm#F#`kNArNL1)$nRyU|^m<(aWESmuupz0%l$=g~?ouPOLy$ zL1D8G;}S-8FjGNc@&~4c^$PlS5U!1WQA(PXOKNd)QD#9&W_})693mRz;s)f|=vP=( z3GX z?|;K45}z%!CFpnd%qu-752SNv`LMGECR^2S72>k&jo)(aQFL@wwWL&=wfvcc^9*_g zsw*$N=4}vt@1-87e^Vf!L7HL7dPcQO<^%5;S82w%EqTxQ?ZjdJTC;trFl$Ns3O1XL@Pk`6ubuwKq0WDbz=F?@ x#S-4|pEmM)FWT2|aB6k>7gmMbNR|ng_&I;+l{b0*l?BE)gQu&X%Q~loCIB#J)NKF& delta 541 zcmV+&0^Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oL=qvEvy z000J1OjJex|Nj6009C@%egFUgTuDShRCwC#n2Qd>AP7ad|Nqm=nrTr{QGx8`mZqyM z_e>C}M1L_2tWkKYM57rbxj=-TRCOJLcBZfE0-Ok-?0kxJzi`s$-A({d-{1oPdV7F; ziwgi~1;AkzVBsbY0MJJO(DwqYJ@p6pc>qu}KvkoKk3ea3rf&xTmiO8KNGnf1L+UaF z5dTzh1s4M7`n<-*&IR(I-UIMDWQa3>!5GjG0Dmbg&Rl@SB#;0iW&!H%65uv);DWh6 zdH_6Bu=^s1sW`^3uJ?~E0F^#EO98vI`zpJt-{iuwVu$TAAVdMzm+EC3{d)b8^&wTO zHaT#&7%%{s*;odcUs0ZWtOW?Q@QKv4i4d*lZ&%t!h>x)}nK1W%4T<2nG( f4QNAvhX4Zr1eOJu%LfM}00000NkvXXu0mjf9sAk! diff --git a/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png b/assets/dolphin/internal/L1_NoSd_128x49/frame_5.png index 00d9843007e85a247de0c23257c310512b5f71f5..d7f8c6402a27c5a2eb565d12b595a3a438ed94ee 100644 GIT binary patch delta 584 zcmaFJ)xteNB9Midfq~&c+a3)d#aJBV?!>U}oXkrghb7(7*O7r?V?XzwL{=c5Ex;$l z_5c6>KxXKn%XL7Cv%n*=n1O-s5C}7hYIrp;Ffh-b=;hDE%QbOU0W&X`!elN+CsrV> zps?A8aS0Gd~Y34iODz$gZO)!`%nnn~{g8^b441&v9d6lX$2%~n|wpQUNDi>ERVTD;TVg~x&%=k2Q!AubA1(z zAx*2N)^jGDkLIu{+3kCmaeoB2xPqijTf^-4JSU_+l*FA)XNs2GRHXlcIYH=wbOQf^ zCpXvLW_lHJ#`(pzhCN~$sm(kb2Nc?FF`F<*{B>>aUYRR@`DFq7fs*hQn)CgqTQ^K^ zom)98JK~Om9K*b+vud`aS|uyuqh zX6oPe`*r@3B|}Ns9j2bgiIK-%#ysSZ@Yz}J9?y`n+Lb}cz9mQhq<7|ZMPTGJc)I$z JtaD0e0syY@+hza& delta 546 zcmV+-0^R+D3*ico7%~V00000bc2Ow+000?uMObuGZ)S9NVRB^vL1b@YWgtmyVP|Dh zWnpA_ami&o000Aok!eo>Ad$LolPv*4vvdKZ0h1pCp?@zvAUrQ}WM(=fFD zZ*D#yJTG!&W;#+tMm``sFL*k5ZE$U6bYVUqJU@7FVPk7$bRcDJWIZBsB0oNIez$f2 z000J1OjJex|Nj6009C@%egFUgVM#AP7ad|Nqm=)@jv(q5|E+G)*%N z_e=#5qJNl%qEUEjM57rvxj+O-s`(wAwx_S_0-Ok-?0kxd51jPNZYKb!2lxPhJ{}+s zaRC6$062I7CI)!`fW88Neir~8sT%=a?#7sm0yu0jHva_xCqR=8P%uCfUJ*D0NYg!` zZwc_8esJkfI(hOJ2fIH52Ll68EK_34z=mFs&3`ihp!X|}+5-Sfb=d0yAZ?4iCFlkM zk^ycx3u=JVz{V0_s=op#W}zZ7y$?XXmo%=q1~92l5C~SUva|Y^SXgK52-WBEWgC6j zPWAt}Y}GCkSV;&C07f>Z0R~u1nA=H-ne?eSTg8B{4dsgf=>y`iuU)`R0D>6s%VXYl z4^wCWnk0ivz2IUWoqaf5hZO+6%*B_V%)OuplWei=T6mx+0Kp#l0d(V$K8`MKfkA*L k$I7@4z_S2t2=EYK0A@u6zLP8OIRF3v07*qoM6N<$g6II$yZ`_I From e12958d408dfe0e1571b60451bf73b1d41914981 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Sun, 29 Jan 2023 15:08:26 +0400 Subject: [PATCH 045/231] [FL-3082] WS: add protocol LaCrosse-TX (TFA Dostmann) (#2292) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WS: add protocol LaCrosse-TX (TFA Dostmann) * WS: fix syntax * WS: fix MSG_TYPE * WS: fix PVS Co-authored-by: あく --- .../helpers/weather_station_types.h | 2 +- .../weather_station/protocols/lacrosse_tx.c | 329 ++++++++++++++++++ .../weather_station/protocols/lacrosse_tx.h | 79 +++++ .../protocols/protocol_items.c | 1 + .../protocols/protocol_items.h | 1 + 5 files changed, 411 insertions(+), 1 deletion(-) create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx.c create mode 100644 applications/plugins/weather_station/protocols/lacrosse_tx.h diff --git a/applications/plugins/weather_station/helpers/weather_station_types.h b/applications/plugins/weather_station/helpers/weather_station_types.h index d512251f1..1f5612e2e 100644 --- a/applications/plugins/weather_station/helpers/weather_station_types.h +++ b/applications/plugins/weather_station/helpers/weather_station_types.h @@ -3,7 +3,7 @@ #include #include -#define WS_VERSION_APP "0.6.1" +#define WS_VERSION_APP "0.7" #define WS_DEVELOPED "SkorP" #define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.c b/applications/plugins/weather_station/protocols/lacrosse_tx.c new file mode 100644 index 000000000..8d8a24e24 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.c @@ -0,0 +1,329 @@ +#include "lacrosse_tx.h" + +#define TAG "WSProtocolLaCrosse_TX" + +/* + * Help + * https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse.c + * + * + * LaCrosse TX 433 Mhz Temperature and Humidity Sensors. + * - Tested: TX-7U and TX-6U (Temperature only) + * - Not Tested but should work: TX-3, TX-4 + * - also TFA Dostmann 30.3120.90 sensor (for e.g. 35.1018.06 (WS-9015) station) + * - also TFA Dostmann 30.3121 sensor + * Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php + * Message is 44 bits, 11 x 4 bit nybbles: + * [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check] + * Notes: + * - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS + * - One Pulses are shorter ( 550 uS High, 1,000 uS Low) = 1,600 uS + * - Sensor id changes when the battery is changed + * - Primary Value are BCD with one decimal place: vvv = 12.3 + * - Secondary value is integer only intval = 12, seems to be a repeat of primary + * This may actually be an additional data check because the 4 bit checksum + * and parity bit is pretty week at detecting errors. + * - Temperature is in Celsius with 50.0 added (to handle negative values) + * - Humidity values appear to be integer precision, decimal always 0. + * - There is a 4 bit checksum and a parity bit covering the three digit value + * - Parity check for TX-3 and TX-4 might be different. + * - Msg sent with one repeat after 30 mS + * - Temperature and humidity are sent as separate messages + * - Frequency for each sensor may be could be off by as much as 50-75 khz + * - LaCrosse Sensors in other frequency ranges (915 Mhz) use FSK not OOK + * so they can't be decoded by rtl_433 currently. + * - Temperature and Humidity are sent in different messages bursts. +*/ + +#define LACROSSE_TX_GAP 1000 +#define LACROSSE_TX_BIT_SIZE 44 +#define LACROSSE_TX_SUNC_PATTERN 0x0A000000000 +#define LACROSSE_TX_SUNC_MASK 0x0F000000000 +#define LACROSSE_TX_MSG_TYPE_TEMP 0x00 +#define LACROSSE_TX_MSG_TYPE_HUM 0x0E + +static const SubGhzBlockConst ws_protocol_lacrosse_tx_const = { + .te_short = 550, + .te_long = 1300, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct WSProtocolDecoderLaCrosse_TX { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + WSBlockGeneric generic; + + uint16_t header_count; +}; + +struct WSProtocolEncoderLaCrosse_TX { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + WSBlockGeneric generic; +}; + +typedef enum { + LaCrosse_TXDecoderStepReset = 0, + LaCrosse_TXDecoderStepCheckPreambule, + LaCrosse_TXDecoderStepSaveDuration, + LaCrosse_TXDecoderStepCheckDuration, +} LaCrosse_TXDecoderStep; + +const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder = { + .alloc = ws_protocol_decoder_lacrosse_tx_alloc, + .free = ws_protocol_decoder_lacrosse_tx_free, + + .feed = ws_protocol_decoder_lacrosse_tx_feed, + .reset = ws_protocol_decoder_lacrosse_tx_reset, + + .get_hash_data = ws_protocol_decoder_lacrosse_tx_get_hash_data, + .serialize = ws_protocol_decoder_lacrosse_tx_serialize, + .deserialize = ws_protocol_decoder_lacrosse_tx_deserialize, + .get_string = ws_protocol_decoder_lacrosse_tx_get_string, +}; + +const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol ws_protocol_lacrosse_tx = { + .name = WS_PROTOCOL_LACROSSE_TX_NAME, + .type = SubGhzProtocolWeatherStation, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &ws_protocol_lacrosse_tx_decoder, + .encoder = &ws_protocol_lacrosse_tx_encoder, +}; + +void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + WSProtocolDecoderLaCrosse_TX* instance = malloc(sizeof(WSProtocolDecoderLaCrosse_TX)); + instance->base.protocol = &ws_protocol_lacrosse_tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void ws_protocol_decoder_lacrosse_tx_free(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + free(instance); +} + +void ws_protocol_decoder_lacrosse_tx_reset(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + instance->header_count = 0; + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; +} + +static bool ws_protocol_lacrosse_tx_check_crc(WSProtocolDecoderLaCrosse_TX* instance) { + if(!instance->decoder.decode_data) return false; + uint8_t msg[] = { + (instance->decoder.decode_data >> 36) & 0x0F, + (instance->decoder.decode_data >> 32) & 0x0F, + (instance->decoder.decode_data >> 28) & 0x0F, + (instance->decoder.decode_data >> 24) & 0x0F, + (instance->decoder.decode_data >> 20) & 0x0F, + (instance->decoder.decode_data >> 16) & 0x0F, + (instance->decoder.decode_data >> 12) & 0x0F, + (instance->decoder.decode_data >> 8) & 0x0F, + (instance->decoder.decode_data >> 4) & 0x0F}; + + uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9); + return ((crc & 0x0F) == ((instance->decoder.decode_data) & 0x0F)); +} + +/** + * Analysis of received data + * @param instance Pointer to a WSBlockGeneric* instance + */ +static void ws_protocol_lacrosse_tx_remote_controller(WSBlockGeneric* instance) { + uint8_t msg_type = (instance->data >> 32) & 0x0F; + instance->id = (((instance->data >> 28) & 0x0F) << 3) | (((instance->data >> 24) & 0x0F) >> 1); + + float msg_value = (float)((instance->data >> 20) & 0x0F) * 10.0f + + (float)((instance->data >> 16) & 0x0F) + + (float)((instance->data >> 12) & 0x0F) * 0.1f; + + if(msg_type == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051 + instance->temp = msg_value - 50.0f; + instance->humidity = WS_NO_HUMIDITY; + } else if(msg_type == LACROSSE_TX_MSG_TYPE_HUM) { + //ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard + instance->humidity = (uint8_t)msg_value; + } else { + furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type."); + } + + instance->btn = WS_NO_BTN; + instance->battery_low = WS_NO_BATT; + instance->channel = WS_NO_CHANNEL; +} + +void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + + switch(instance->decoder.parser_step) { + case LaCrosse_TXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckPreambule; + instance->header_count = 0; + } + break; + + case LaCrosse_TXDecoderStepCheckPreambule: + + if(level) { + if((DURATION_DIFF(duration, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) && + (instance->header_count > 1)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.te_last = duration; + } else if(duration > (ws_protocol_lacrosse_tx_const.te_long * 2)) { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2) { + instance->decoder.te_last = duration; + instance->header_count++; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + } + + break; + + case LaCrosse_TXDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + break; + + case LaCrosse_TXDecoderStepCheckDuration: + + if(!level) { + if(duration > LACROSSE_TX_GAP * 3) { + if(DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else if( + DURATION_DIFF( + instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < + ws_protocol_lacrosse_tx_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } + if((instance->decoder.decode_data & LACROSSE_TX_SUNC_MASK) == + LACROSSE_TX_SUNC_PATTERN) { + if(ws_protocol_lacrosse_tx_check_crc(instance)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = LACROSSE_TX_BIT_SIZE; + ws_protocol_lacrosse_tx_remote_controller(&instance->generic); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) < + ws_protocol_lacrosse_tx_const.te_delta) && + (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) < + ws_protocol_lacrosse_tx_const.te_delta) && + (DURATION_DIFF(duration, LACROSSE_TX_GAP) < + ws_protocol_lacrosse_tx_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LaCrosse_TXDecoderStepReset; + } + + break; + } +} + +uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool ws_protocol_decoder_lacrosse_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + return ws_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + bool ret = false; + do { + if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + ws_protocol_lacrosse_tx_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) { + furi_assert(context); + WSProtocolDecoderLaCrosse_TX* instance = context; + furi_string_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%lX Ch:%d Bat:%d\r\n" + "Temp:%3.1f C Hum:%d%%", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data), + instance->generic.id, + instance->generic.channel, + instance->generic.battery_low, + (double)instance->generic.temp, + instance->generic.humidity); +} diff --git a/applications/plugins/weather_station/protocols/lacrosse_tx.h b/applications/plugins/weather_station/protocols/lacrosse_tx.h new file mode 100644 index 000000000..e88455689 --- /dev/null +++ b/applications/plugins/weather_station/protocols/lacrosse_tx.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +#include +#include +#include +#include "ws_generic.h" +#include + +#define WS_PROTOCOL_LACROSSE_TX_NAME "LaCrosse_TX" + +typedef struct WSProtocolDecoderLaCrosse_TX WSProtocolDecoderLaCrosse_TX; +typedef struct WSProtocolEncoderLaCrosse_TX WSProtocolEncoderLaCrosse_TX; + +extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder; +extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder; +extern const SubGhzProtocol ws_protocol_lacrosse_tx; + +/** + * Allocate WSProtocolDecoderLaCrosse_TX. + * @param environment Pointer to a SubGhzEnvironment instance + * @return WSProtocolDecoderLaCrosse_TX* pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void ws_protocol_decoder_lacrosse_tx_free(void* context); + +/** + * Reset decoder WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + */ +void ws_protocol_decoder_lacrosse_tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @return hash Hash sum + */ +uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context); + +/** + * Serialize data WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data WSProtocolDecoderLaCrosse_TX. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance + * @param output Resulting text + */ +void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output); diff --git a/applications/plugins/weather_station/protocols/protocol_items.c b/applications/plugins/weather_station/protocols/protocol_items.c index 99c8344f4..2c9d751c7 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.c +++ b/applications/plugins/weather_station/protocols/protocol_items.c @@ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = { &ws_protocol_gt_wt_03, &ws_protocol_acurite_606tx, &ws_protocol_acurite_609txc, + &ws_protocol_lacrosse_tx, &ws_protocol_lacrosse_tx141thbv2, &ws_protocol_oregon2, &ws_protocol_acurite_592txr, diff --git a/applications/plugins/weather_station/protocols/protocol_items.h b/applications/plugins/weather_station/protocols/protocol_items.h index 9d5d096f8..f9e443abc 100644 --- a/applications/plugins/weather_station/protocols/protocol_items.h +++ b/applications/plugins/weather_station/protocols/protocol_items.h @@ -8,6 +8,7 @@ #include "gt_wt_03.h" #include "acurite_606tx.h" #include "acurite_609txc.h" +#include "lacrosse_tx.h" #include "lacrosse_tx141thbv2.h" #include "oregon2.h" #include "acurite_592txr.h" From a8e5f2250026c4260bb13fddf696acb7f9322b77 Mon Sep 17 00:00:00 2001 From: Angel Date: Sun, 29 Jan 2023 06:23:45 -0500 Subject: [PATCH 046/231] LF-RFID: add CRC calculation to paradox protocol (#2299) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Angel Co-authored-by: あく --- lib/lfrfid/protocols/protocol_paradox.c | 45 ++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/lib/lfrfid/protocols/protocol_paradox.c b/lib/lfrfid/protocols/protocol_paradox.c index 7e029f1de..26c9b55dc 100644 --- a/lib/lfrfid/protocols/protocol_paradox.c +++ b/lib/lfrfid/protocols/protocol_paradox.c @@ -136,17 +136,45 @@ LevelDuration protocol_paradox_encoder_yield(ProtocolParadox* protocol) { return level_duration_make(level, duration); }; +static uint8_t protocol_paradox_calculate_checksum(uint8_t fc, uint16_t card_id) { + uint8_t card_hi = (card_id >> 8) & 0xff; + uint8_t card_lo = card_id & 0xff; + + uint8_t arr[5] = {0, 0, fc, card_hi, card_lo}; + + uint8_t manchester[9]; + + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, false); + + for(uint8_t i = 6; i < 40; i += 1) { + if(bit_lib_get_bit(arr, i) == 0b1) { + bit_lib_push_bit(manchester, 9, true); + bit_lib_push_bit(manchester, 9, false); + } else { + bit_lib_push_bit(manchester, 9, false); + bit_lib_push_bit(manchester, 9, true); + } + } + + uint8_t output = bit_lib_crc8(manchester, 9, 0x31, 0x00, true, true, 0x06); + + return output; +} + void protocol_paradox_render_data(ProtocolParadox* protocol, FuriString* result) { uint8_t* decoded_data = protocol->data; uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); furi_string_cat_printf(result, "Facility: %u\r\n", fc); furi_string_cat_printf(result, "Card: %u\r\n", card_id); - furi_string_cat_printf(result, "Data: "); - for(size_t i = 0; i < PARADOX_DECODED_DATA_SIZE; i++) { - furi_string_cat_printf(result, "%02X", decoded_data[i]); - } + furi_string_cat_printf(result, "CRC: %u Calc CRC: %u\r\n", card_crc, calc_crc); + if(card_crc != calc_crc) furi_string_cat_printf(result, "CRC Mismatch, Invalid Card!\r\n"); }; void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* result) { @@ -154,8 +182,15 @@ void protocol_paradox_render_brief_data(ProtocolParadox* protocol, FuriString* r uint8_t fc = bit_lib_get_bits(decoded_data, 10, 8); uint16_t card_id = bit_lib_get_bits_16(decoded_data, 18, 16); + uint8_t card_crc = bit_lib_get_bits_16(decoded_data, 34, 8); + uint8_t calc_crc = protocol_paradox_calculate_checksum(fc, card_id); - furi_string_cat_printf(result, "FC: %03u, Card: %05u", fc, card_id); + furi_string_cat_printf(result, "FC: %03u, Card: %05u\r\n", fc, card_id); + if(calc_crc == card_crc) { + furi_string_cat_printf(result, "CRC : %03u", card_crc); + } else { + furi_string_cat_printf(result, "Card is Invalid!"); + } }; bool protocol_paradox_write_data(ProtocolParadox* protocol, void* data) { From 068b834ab3df7e4d0c039e809315ba85327df376 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sun, 29 Jan 2023 12:31:15 +0100 Subject: [PATCH 047/231] Update api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index bcd5825c7..aefd65d39 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.4,, +Version,+,12.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1416,8 +1416,8 @@ Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool Function,-,furi_init,void, -Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, @@ -4411,6 +4411,7 @@ Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" Function,+,validator_is_file_free,void,ValidatorIsFile* Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_int32,uint8_t,"const int32_t, const int32_t[], uint8_t" Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" Function,+,variable_item_get_context,void*,VariableItem* Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* From feac699dcbab1eb36609c938b94a9346b5c433a3 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 29 Jan 2023 19:35:20 +0000 Subject: [PATCH 048/231] Fix keyboard layouts on ble bad usb --- applications/main/bad_usb/bad_usb_script.c | 7 +++++++ applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c | 1 + .../main/bad_usb/scenes/bad_usb_scene_config_usb.c | 1 + 3 files changed, 9 insertions(+) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index a61bc1e06..6ad428b4d 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -56,6 +56,7 @@ struct BadUsbScript { FuriHalUsbHidConfig hid_cfg; BadUsbState st; FuriString* file_path; + FuriString* keyboard_layout; uint32_t defdelay; uint16_t layout[128]; FuriThread* thread; @@ -719,6 +720,7 @@ static int32_t bad_usb_worker(void* context) { bad_usb->repeat_cnt = 0; bad_usb->file_end = false; storage_file_seek(script_file, 0, true); + bad_usb_script_set_keyboard_layout(bad_usb, bad_usb->keyboard_layout); worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtDisconnect) { worker_state = BadUsbStateNotConnected; // USB disconnected @@ -748,6 +750,7 @@ static int32_t bad_usb_worker(void* context) { update_bt_timeout(bad_usb->bt); FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } + bad_usb_script_set_keyboard_layout(bad_usb, bad_usb->keyboard_layout); worker_state = BadUsbStateRunning; } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution worker_state = BadUsbStateNotConnected; @@ -861,6 +864,7 @@ static int32_t bad_usb_worker(void* context) { static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { furi_assert(bad_usb); + furi_string_set_str(bad_usb->keyboard_layout, ""); memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); } @@ -871,6 +875,7 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path, Bt* bt) { BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); + bad_usb->keyboard_layout = furi_string_alloc(); bad_usb_script_set_default_keyboard_layout(bad_usb); bad_usb->st.state = BadUsbStateInit; @@ -890,6 +895,7 @@ void bad_usb_script_close(BadUsbScript* bad_usb) { furi_thread_join(bad_usb->thread); furi_thread_free(bad_usb->thread); furi_string_free(bad_usb->file_path); + furi_string_free(bad_usb->keyboard_layout); free(bad_usb); } @@ -903,6 +909,7 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); if(!furi_string_empty(layout_path)) { + furi_string_set(bad_usb->keyboard_layout, layout_path); if(storage_file_open( layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint16_t layout[128]; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c index 494e56b1f..7071e748e 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c @@ -57,6 +57,7 @@ bool bad_usb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { } else if(event.event == VarItemListIndexConnection) { bad_usb_script_close(bad_usb->bad_usb_script); bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); scene_manager_previous_scene(bad_usb->scene_manager); if (bad_usb->is_bt) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c index 7cc81de11..af7abd570 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c @@ -49,6 +49,7 @@ bool bad_usb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { } else if(event.event == VarItemListIndexConnection) { bad_usb_script_close(bad_usb->bad_usb_script); bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); scene_manager_previous_scene(bad_usb->scene_manager); if (bad_usb->is_bt) { scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); From b1496ee9bd97ff8c34a67a49ec4ecdc6cfbd9436 Mon Sep 17 00:00:00 2001 From: Milk-Cool <43724263+Milk-Cool@users.noreply.github.com> Date: Mon, 30 Jan 2023 10:54:15 +0300 Subject: [PATCH 049/231] Furi: getter for current thread stdout write callback (#2344) --- firmware/targets/f7/api_symbols.csv | 5 +++-- furi/core/thread.c | 6 ++++++ furi/core/thread.h | 6 ++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a75e88bad..7b247daed 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.8,, +Version,+,11.9,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1389,8 +1389,8 @@ Function,+,furi_hal_version_uid_size,size_t, Function,-,furi_hal_vibro_init,void, Function,+,furi_hal_vibro_on,void,_Bool Function,-,furi_init,void, -Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, Function,+,furi_kernel_lock,int32_t, Function,+,furi_kernel_restore_lock,int32_t,int32_t Function,+,furi_kernel_unlock,int32_t, @@ -1515,6 +1515,7 @@ Function,+,furi_thread_get_name,const char*,FuriThreadId Function,+,furi_thread_get_return_code,int32_t,FuriThread* Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, Function,+,furi_thread_is_suspended,_Bool,FuriThreadId Function,+,furi_thread_join,_Bool,FuriThread* Function,+,furi_thread_mark_as_service,void,FuriThread* diff --git a/furi/core/thread.c b/furi/core/thread.c index c966dd572..ef9560b4a 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -530,6 +530,12 @@ bool furi_thread_set_stdout_callback(FuriThreadStdoutWriteCallback callback) { return true; } +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback() { + FuriThread* thread = furi_thread_get_current(); + + return thread->output.write_callback; +} + size_t furi_thread_stdout_write(const char* data, size_t size) { FuriThread* thread = furi_thread_get_current(); diff --git a/furi/core/thread.h b/furi/core/thread.h index c2f5a9130..1542d5bf0 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -227,6 +227,12 @@ const char* furi_thread_get_name(FuriThreadId thread_id); uint32_t furi_thread_get_stack_space(FuriThreadId thread_id); +/** Get STDOUT callback for thead + * + * @return STDOUT callback + */ +FuriThreadStdoutWriteCallback furi_thread_get_stdout_callback(); + /** Set STDOUT callback for thread * * @param callback callback or NULL to clear From 5db7fdf9852ec4a412765e265cd10d39c02305b7 Mon Sep 17 00:00:00 2001 From: Noam Drong Date: Mon, 30 Jan 2023 09:03:10 +0100 Subject: [PATCH 050/231] Add support for `GUI-CTRL` in bad_usb (#2315) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- applications/main/bad_usb/bad_usb_script.c | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index bbd721ed2..e2281133f 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -50,6 +50,7 @@ static const DuckyKey ducky_keys[] = { {"ALT-SHIFT", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_SHIFT}, {"ALT-GUI", KEY_MOD_LEFT_ALT | KEY_MOD_LEFT_GUI}, {"GUI-SHIFT", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_SHIFT}, + {"GUI-CTRL", KEY_MOD_LEFT_GUI | KEY_MOD_LEFT_CTRL}, {"CTRL", KEY_MOD_LEFT_CTRL}, {"CONTROL", KEY_MOD_LEFT_CTRL}, From 7f3ebcd110e57193d903fed588920402952b0d0d Mon Sep 17 00:00:00 2001 From: Konstantin Volkov <72250702+doomwastaken@users.noreply.github.com> Date: Mon, 30 Jan 2023 15:59:45 +0700 Subject: [PATCH 051/231] Changed bench target, stlink serial and added error for testing the run (#2275) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changed bench target, stlink serial and added error for testing the run * changed flipper name for macos and changed serial aquisition for device manager * tested broken pipeline, reverting test data * added timeout-minutes, testing if its int or float Co-authored-by: Konstantin Volkov Co-authored-by: あく --- .github/workflows/unit_tests.yml | 1 + .github/workflows/updater_test.yml | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index ac3fc3684..7e625229a 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -56,6 +56,7 @@ jobs: - name: 'Run units and validate results' id: run_units if: steps.copy.outcome == 'success' + timeout-minutes: 2.5 run: | source scripts/toolchain/fbtenv.sh python3 scripts/testing/units.py ${{steps.device.outputs.flipper}} diff --git a/.github/workflows/updater_test.yml b/.github/workflows/updater_test.yml index d4ca56fad..0b02920fa 100644 --- a/.github/workflows/updater_test.yml +++ b/.github/workflows/updater_test.yml @@ -10,7 +10,7 @@ env: jobs: test_updater_on_bench: - runs-on: [self-hosted, FlipperZeroTest] # currently on same bench as units, needs different bench + runs-on: [self-hosted, FlipperZeroTestMac1] steps: - name: 'Decontaminate previous build leftovers' run: | @@ -27,7 +27,8 @@ jobs: - name: 'Get flipper from device manager (mock)' id: device run: | - echo "flipper=/dev/ttyACM0" >> $GITHUB_OUTPUT + echo "flipper=/dev/tty.usbmodemflip_Rekigyn1" >> $GITHUB_OUTPUT + echo "stlink=0F020D026415303030303032" >> $GITHUB_OUTPUT - name: 'Flashing target firmware' id: first_full_flash @@ -67,7 +68,7 @@ jobs: - name: 'Flash last release' if: failure() run: | - ./fbt flash OPENOCD_ADAPTER_SERIAL=2A0906016415303030303032 FORCE=1 + ./fbt flash OPENOCD_ADAPTER_SERIAL=${{steps.device.outputs.stlink}} FORCE=1 - name: 'Wait for flipper and format ext' if: failure() From d9be81588955812942c13eda2d58864fc282b4e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Such=C3=A1nek?= Date: Mon, 30 Jan 2023 10:14:30 +0100 Subject: [PATCH 052/231] Print card CID in storage info (#2227) --- applications/services/storage/storage_cli.c | 16 +++++++- .../scenes/storage_settings_scene_sd_info.c | 19 +++++++-- firmware/targets/f7/fatfs/stm32_adafruit_sd.c | 41 ++++++++++--------- firmware/targets/f7/fatfs/stm32_adafruit_sd.h | 21 +++++----- 4 files changed, 62 insertions(+), 35 deletions(-) diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index c83f16499..eeaa7fce8 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -60,17 +61,28 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { SDInfo sd_info; + SD_CID sd_cid; FS_Error error = storage_sd_info(api, &sd_info); + BSP_SD_GetCIDRegister(&sd_cid); if(error != FSE_OK) { storage_cli_print_error(error); } else { printf( - "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n", + "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" + "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, - sd_info.kb_free); + sd_info.kb_free, + sd_cid.ManufacturerID, + sd_cid.OEM_AppliID, + sd_cid.ProdName, + sd_cid.ProdRev >> 4, + sd_cid.ProdRev & 0xf, + sd_cid.ProdSN, + sd_cid.ManufactMonth, + sd_cid.ManufactYear + 2000); } } else { storage_cli_print_usage(); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index 0c398ed5b..a7991bc19 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -1,4 +1,5 @@ #include "../storage_settings.h" +#include static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { StorageSettings* app = context; @@ -11,7 +12,10 @@ void storage_settings_scene_sd_info_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; SDInfo sd_info; + SD_CID sd_cid; FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); + BSP_SD_GetCIDRegister(&sd_cid); + scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); dialog_ex_set_context(dialog_ex, app); @@ -26,13 +30,22 @@ void storage_settings_scene_sd_info_on_enter(void* context) { } else { furi_string_printf( app->text_string, - "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free", + "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" + "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, - sd_info.kb_free); + sd_info.kb_free, + sd_cid.ManufacturerID, + sd_cid.OEM_AppliID, + sd_cid.ProdName, + sd_cid.ProdRev >> 4, + sd_cid.ProdRev & 0xf, + sd_cid.ProdSN, + sd_cid.ManufactMonth, + sd_cid.ManufactYear + 2000); dialog_ex_set_text( - dialog_ex, furi_string_get_cstr(app->text_string), 4, 4, AlignLeft, AlignTop); + dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } view_dispatcher_switch_to_view(app->view_dispatcher, StorageSettingsViewDialogEx); diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c index b9b65f06a..998adee29 100644 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c @@ -779,25 +779,10 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { Cid->ManufacturerID = CID_Tab[0]; /* Byte 1 */ - Cid->OEM_AppliID = CID_Tab[1] << 8; - - /* Byte 2 */ - Cid->OEM_AppliID |= CID_Tab[2]; + memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); /* Byte 3 */ - Cid->ProdName1 = CID_Tab[3] << 24; - - /* Byte 4 */ - Cid->ProdName1 |= CID_Tab[4] << 16; - - /* Byte 5 */ - Cid->ProdName1 |= CID_Tab[5] << 8; - - /* Byte 6 */ - Cid->ProdName1 |= CID_Tab[6]; - - /* Byte 7 */ - Cid->ProdName2 = CID_Tab[7]; + memcpy(Cid->ProdName, CID_Tab + 3, 5); /* Byte 8 */ Cid->ProdRev = CID_Tab[8]; @@ -815,11 +800,12 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { Cid->ProdSN |= CID_Tab[12]; /* Byte 13 */ - Cid->Reserved1 |= (CID_Tab[13] & 0xF0) >> 4; - Cid->ManufactDate = (CID_Tab[13] & 0x0F) << 8; + Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; + Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; /* Byte 14 */ - Cid->ManufactDate |= CID_Tab[14]; + Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; + Cid->ManufactMonth = (CID_Tab[14] & 0x0F); /* Byte 15 */ Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; @@ -837,6 +823,21 @@ uint8_t SD_GetCIDRegister(SD_CID* Cid) { return retr; } +uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) { + uint8_t retr = BSP_SD_ERROR; + + /* Slow speed init */ + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; + + memset(Cid, 0, sizeof(SD_CID)); + retr = SD_GetCIDRegister(Cid); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + return retr; +} + /** * @brief Sends 5 bytes command to the SD card and get response * @param Cmd: The user expected command to send to SD card. diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h index e0c5e3bef..a133c5922 100644 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h @@ -133,16 +133,16 @@ typedef struct { * @brief Card Identification Data: CID Register */ typedef struct { - __IO uint8_t ManufacturerID; /* ManufacturerID */ - __IO uint16_t OEM_AppliID; /* OEM/Application ID */ - __IO uint32_t ProdName1; /* Product Name part1 */ - __IO uint8_t ProdName2; /* Product Name part2*/ - __IO uint8_t ProdRev; /* Product Revision */ - __IO uint32_t ProdSN; /* Product Serial Number */ - __IO uint8_t Reserved1; /* Reserved1 */ - __IO uint16_t ManufactDate; /* Manufacturing Date */ - __IO uint8_t CID_CRC; /* CID CRC */ - __IO uint8_t Reserved2; /* always 1 */ + uint8_t ManufacturerID; /* ManufacturerID */ + char OEM_AppliID[2]; /* OEM/Application ID */ + char ProdName[5]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + uint32_t ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint8_t ManufactYear; /* Manufacturing Year */ + uint8_t ManufactMonth; /* Manufacturing Month */ + uint8_t CID_CRC; /* CID CRC */ + uint8_t Reserved2; /* always 1 */ } SD_CID; /** @@ -207,6 +207,7 @@ uint8_t uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); uint8_t BSP_SD_GetCardState(void); uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); +uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); /* Link functions for SD Card peripheral*/ void SD_SPI_Slow_Init(void); From 01a9854f8af8898a9a1138f1e1f496ff74b51b8d Mon Sep 17 00:00:00 2001 From: Giacomo Ferretti Date: Mon, 30 Jan 2023 10:49:51 +0100 Subject: [PATCH 053/231] Documentation: add BadUSB GUI-CTRL #2347 --- documentation/file_formats/BadUsbScriptFormat.md | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/file_formats/BadUsbScriptFormat.md b/documentation/file_formats/BadUsbScriptFormat.md index fa5038742..2ef1d3135 100644 --- a/documentation/file_formats/BadUsbScriptFormat.md +++ b/documentation/file_formats/BadUsbScriptFormat.md @@ -67,6 +67,7 @@ Can be combined with a special key command or a single character. |ALT-SHIFT|ALT+SHIFT| |ALT-GUI|ALT+WIN| |GUI-SHIFT|WIN+SHIFT| +|GUI-CTRL|WIN+CTRL| ## String From 3cd411d98ac7493eca61f37cb1023a6fd459b5ec Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 30 Jan 2023 12:14:14 +0000 Subject: [PATCH 054/231] Poweroff after setting pin (Fix #118) --- .../desktop/scenes/desktop_scene_lock_menu.c | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/applications/services/desktop/scenes/desktop_scene_lock_menu.c b/applications/services/desktop/scenes/desktop_scene_lock_menu.c index e55fe252e..a0a2c08e9 100644 --- a/applications/services/desktop/scenes/desktop_scene_lock_menu.c +++ b/applications/services/desktop/scenes/desktop_scene_lock_menu.c @@ -36,13 +36,21 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeTick) { - bool check_pin_changed = + int check_pin_changed = scene_manager_get_scene_state(desktop->scene_manager, DesktopSceneLockMenu); if(check_pin_changed) { DESKTOP_SETTINGS_LOAD(&desktop->settings); if(desktop->settings.pin_code.length > 0) { desktop_lock_menu_set_pin_state(desktop->lock_menu, true); scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 0); + if(check_pin_changed == 2) { + desktop_pin_lock(&desktop->settings); + desktop_lock(desktop); + Power* power = furi_record_open(RECORD_POWER); + furi_delay_ms(666); + power_off(power); + furi_record_close(RECORD_POWER); + } } } } else if(event.type == SceneManagerEventTypeCustom) { @@ -73,20 +81,20 @@ bool desktop_scene_lock_menu_on_event(void* context, SceneManagerEvent event) { if(desktop->settings.pin_code.length > 0) { desktop_pin_lock(&desktop->settings); desktop_lock(desktop); + Power* power = furi_record_open(RECORD_POWER); + furi_delay_ms(666); + power_off(power); + furi_record_close(RECORD_POWER); } else { LoaderStatus status = loader_start(desktop->loader, "Desktop", DESKTOP_SETTINGS_RUN_PIN_SETUP_ARG); if(status == LoaderStatusOk) { - scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 1); + scene_manager_set_scene_state(desktop->scene_manager, DesktopSceneLockMenu, 2); } else { FURI_LOG_E(TAG, "Unable to start desktop settings"); } } consumed = true; - Power* power = furi_record_open(RECORD_POWER); - furi_delay_ms(666); - power_off(power); - furi_record_close(RECORD_POWER); break; case DesktopLockMenuEventXtremeSettings: From fb1218c9a55e74c3a4bb67c06e19a1b5b3975d7c Mon Sep 17 00:00:00 2001 From: yocvito Date: Mon, 30 Jan 2023 13:31:24 +0100 Subject: [PATCH 055/231] Removes pin verif when using BT for bad-USB --- applications/main/bad_usb/bad_usb_script.c | 6 +++ applications/services/bt/bt_service/bt.c | 47 +++++++++---------- applications/services/bt/bt_service/bt.h | 4 ++ firmware/targets/f7/api_symbols.csv | 6 ++- firmware/targets/f7/ble_glue/gap.c | 13 ++++- firmware/targets/f7/furi_hal/furi_hal_bt.c | 10 ++++ .../targets/furi_hal_include/furi_hal_bt.h | 4 ++ 7 files changed, 63 insertions(+), 27 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 6ad428b4d..a93a18046 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -641,15 +641,19 @@ static int32_t bad_usb_worker(void* context) { int32_t delay_val = 0; FuriHalUsbInterface* usb_mode_prev = NULL; + GapPairing old_pairing_method = GapPairingNone; if (bad_usb->bt) { bt_timeout = bt_hid_delays[LevelRssi39_0]; bt_disconnect(bad_usb->bt); furi_delay_ms(200); bt_keys_storage_set_storage_path(bad_usb->bt, HID_BT_KEYS_STORAGE_PATH); + if(!bt_set_profile(bad_usb->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); return -1; } + old_pairing_method = bt_get_profile_pairing_method(bad_usb->bt); + bt_set_profile_pairing_method(bad_usb->bt, GapPairingNone); furi_hal_bt_start_advertising(); bt_set_status_changed_callback(bad_usb->bt, bad_usb_bt_hid_state_callback, bad_usb); } else { @@ -843,6 +847,8 @@ static int32_t bad_usb_worker(void* context) { bt_keys_storage_set_default_path(bad_usb->bt); + bt_set_profile_pairing_method(bad_usb->bt, old_pairing_method); + if(!bt_set_profile(bad_usb->bt, BtProfileSerial)) { FURI_LOG_E(TAG, "Failed to switch to Serial profile"); } diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index ad3ae71c9..d37216bad 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -370,12 +370,16 @@ static void bt_close_connection(Bt* bt) { furi_event_flag_set(bt->api_event, BT_API_UNLOCK_EVENT); } -static void bt_restart(Bt* bt) { - if(bt->profile == BtProfileHidKeyboard) { - furi_hal_bt_change_app(FuriHalBtProfileHidKeyboard, bt_on_gap_event_callback, bt); +static inline FuriHalBtProfile get_hal_bt_profile(BtProfile profile) { + if(profile == BtProfileHidKeyboard) { + return FuriHalBtProfileHidKeyboard; } else { - furi_hal_bt_change_app(FuriHalBtProfileSerial, bt_on_gap_event_callback, bt); + return FuriHalBtProfileSerial; } +} + +static void bt_restart(Bt* bt) { + furi_hal_bt_change_app(get_hal_bt_profile(bt->profile), bt_on_gap_event_callback, bt); furi_hal_bt_start_advertising(); } @@ -388,44 +392,28 @@ void bt_set_profile_adv_name(Bt* bt, const char* fmt, ...) { va_start(args, fmt); vsnprintf(name, sizeof(name), fmt, args); va_end(args); - if(bt->profile == BtProfileHidKeyboard) { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, name); - } else { - furi_hal_bt_set_profile_adv_name(FuriHalBtProfileSerial, name); - } + furi_hal_bt_set_profile_adv_name(get_hal_bt_profile(bt->profile), name); bt_restart(bt); } const char* bt_get_profile_adv_name(Bt* bt) { furi_assert(bt); - if(bt->profile == BtProfileHidKeyboard) { - return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileHidKeyboard); - } else { - return furi_hal_bt_get_profile_adv_name(FuriHalBtProfileSerial); - } + return furi_hal_bt_get_profile_adv_name(get_hal_bt_profile(bt->profile)); } void bt_set_profile_mac_address(Bt* bt, const uint8_t mac[6]) { furi_assert(bt); furi_assert(mac); - if(bt->profile == BtProfileHidKeyboard) { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, mac); - } else { - furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileSerial, mac); - } + furi_hal_bt_set_profile_mac_addr(get_hal_bt_profile(bt->profile), mac); bt_restart(bt); } const uint8_t* bt_get_profile_mac_address(Bt* bt) { furi_assert(bt); - if(bt->profile == BtProfileHidKeyboard) { - return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileHidKeyboard); - } else { - return furi_hal_bt_get_profile_mac_addr(FuriHalBtProfileSerial); - } + return furi_hal_bt_get_profile_mac_addr(get_hal_bt_profile(bt->profile)); } bool bt_remote_rssi(Bt* bt, BtRssi* rssi) { @@ -443,6 +431,17 @@ bool bt_remote_rssi(Bt* bt, BtRssi* rssi) { return true; } +void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) { + furi_assert(bt); + furi_hal_bt_set_profile_pairing_method(get_hal_bt_profile(bt->profile), pairing_method); + bt_restart(bt); +} + +GapPairing bt_get_profile_pairing_method(Bt* bt) { + furi_assert(bt); + return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); +} + int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index 60420a7f7..046887a2c 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -2,6 +2,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -48,6 +49,9 @@ const uint8_t* bt_get_profile_mac_address(Bt* bt); bool bt_remote_rssi(Bt* bt, BtRssi* rssi); +void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method); +GapPairing bt_get_profile_pairing_method(Bt* bt); + /** Disconnect from Central * * @param bt Bt instance diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index aabe45714..213e6061f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.0,, +Version,+,13.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -573,12 +573,14 @@ Function,+,bt_disconnect,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* Function,+,bt_get_profile_adv_name,const char*,Bt* Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* +Function,+,bt_get_profile_pairing_method,GapPairing,Bt* Function,+,bt_keys_storage_set_default_path,void,Bt* Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" Function,+,bt_remote_rssi,_Bool,"Bt*, BtRssi*" Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" Function,+,bt_set_profile_adv_name,void,"Bt*, const char*, ..." Function,+,bt_set_profile_mac_address,void,"Bt*, const uint8_t[6]" +Function,+,bt_set_profile_pairing_method,void,"Bt*, GapPairing" Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" Function,+,buffered_file_stream_alloc,Stream*,Storage* Function,+,buffered_file_stream_close,_Bool,Stream* @@ -1004,6 +1006,7 @@ Function,+,furi_hal_bt_get_conn_rssi,uint32_t,uint8_t* Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" Function,+,furi_hal_bt_get_profile_adv_name,const char*,FuriHalBtProfile Function,+,furi_hal_bt_get_profile_mac_addr,const uint8_t*,FuriHalBtProfile +Function,+,furi_hal_bt_get_profile_pairing_method,GapPairing,FuriHalBtProfile Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, Function,+,furi_hal_bt_get_rssi,float, Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, @@ -1039,6 +1042,7 @@ Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" +Function,+,furi_hal_bt_set_profile_pairing_method,void,"FuriHalBtProfile, GapPairing" Function,+,furi_hal_bt_start_advertising,void, Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 668509218..66786297f 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -377,15 +377,24 @@ static void gap_init_svc(Gap* gap) { aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); keypress_supported = true; } + + uint8_t conf_mitm = CFG_MITM_PROTECTION; + uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN; + + if (gap->config->pairing_method == GapPairingNone) { + conf_mitm = 0; + conf_used_fixed_pin = 0; + } + // Setup authentication aci_gap_set_authentication_requirement( gap->config->bonding_mode, - CFG_MITM_PROTECTION, + conf_mitm, CFG_SC_SUPPORT, keypress_supported, CFG_ENCRYPTION_KEY_SIZE_MIN, CFG_ENCRYPTION_KEY_SIZE_MAX, - CFG_USED_FIXED_PIN, // 0x0 for no pin + conf_used_fixed_pin, // 0x0 for no pin 0, PUBLIC_ADDR); // Configure whitelist diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index 1e7b80040..f33c92c62 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -495,3 +495,13 @@ const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile) { furi_assert(profile < FuriHalBtProfileNumber); return profile_config[profile].config.mac_address; } + +void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method) { + furi_assert(profile < FuriHalBtProfileNumber); + profile_config[profile].config.pairing_method = pairing_method; +} + +GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile) { + furi_assert(profile < FuriHalBtProfileNumber); + return profile_config[profile].config.pairing_method; +} diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index fb17436f4..3e554bb4f 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -246,6 +246,10 @@ const uint8_t* furi_hal_bt_get_profile_mac_addr(FuriHalBtProfile profile); uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi); +void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing pairing_method); + +GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile); + #ifdef __cplusplus } #endif From cf89c9db898286a374e653cd4e6e16bffdc93e30 Mon Sep 17 00:00:00 2001 From: yocvito Date: Mon, 30 Jan 2023 21:32:55 +0100 Subject: [PATCH 056/231] Automatically accepts PIN verif when GapPairingNone was selected, but the remote host refused the non-auth pairing and switched to numeric comparaison ethod (pin verif yes or no) --- applications/services/bt/bt_service/bt.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index d37216bad..6108a7790 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -76,7 +76,10 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); - + + if (bt_get_profile_pairing_method(bt) == GapPairingNone) + return true; + notification_message(bt->notification, &sequence_display_backlight_on); FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, XTREME_ASSETS()->I_BLE_Pairing_128x64, 0, 0); @@ -86,6 +89,7 @@ static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { dialog_message_set_buttons(bt->dialog_message, "Cancel", "OK", NULL); DialogMessageButton button = dialog_message_show(bt->dialogs, bt->dialog_message); furi_string_free(pin_str); + return button == DialogMessageButtonCenter; } @@ -431,7 +435,7 @@ bool bt_remote_rssi(Bt* bt, BtRssi* rssi) { return true; } -void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) { +void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method) { furi_assert(bt); furi_hal_bt_set_profile_pairing_method(get_hal_bt_profile(bt->profile), pairing_method); bt_restart(bt); From f3848724497f703c5e8020d3e045a812636b2b96 Mon Sep 17 00:00:00 2001 From: yocvito Date: Mon, 30 Jan 2023 22:01:38 +0100 Subject: [PATCH 057/231] BLE GAP: specifies Keyboard yes/no IO capability for GapPairingNone case during gap init, in case 'Just works' pairing method is not accepted by remote host --- firmware/targets/f7/ble_glue/gap.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 66786297f..892fbd2a9 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -371,21 +371,24 @@ static void gap_init_svc(Gap* gap) { hci_le_set_default_phy(ALL_PHYS_PREFERENCE, TX_2M_PREFERRED, RX_2M_PREFERRED); // Set I/O capability bool keypress_supported = false; + uint8_t conf_mitm = CFG_MITM_PROTECTION; + uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN; if(gap->config->pairing_method == GapPairingPinCodeShow) { aci_gap_set_io_capability(IO_CAP_DISPLAY_ONLY); } else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) { aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); keypress_supported = true; - } - - uint8_t conf_mitm = CFG_MITM_PROTECTION; - uint8_t conf_used_fixed_pin = CFG_USED_FIXED_PIN; - - if (gap->config->pairing_method == GapPairingNone) { + } else if (gap->config->pairing_method == GapPairingNone) { + // Just works pairing method (IOS accept it, it seems android and linux doesn't) conf_mitm = 0; conf_used_fixed_pin = 0; + // if just works isn't supported, we want the numeric comparaison method + aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); + keypress_supported = true; } + + // Setup authentication aci_gap_set_authentication_requirement( gap->config->bonding_mode, From de01e377659f32c84ccc6380ae2f46645cb74915 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Mon, 30 Jan 2023 23:22:22 +0100 Subject: [PATCH 058/231] Update Unitemp --- applications/plugins/unitemp/README.md | 28 +- applications/plugins/unitemp/Sensors.c | 90 ++-- applications/plugins/unitemp/Sensors.h | 14 +- .../plugins/unitemp/interfaces/I2CSensor.c | 2 +- .../plugins/unitemp/interfaces/I2CSensor.h | 2 +- .../unitemp/interfaces/OneWireSensor.c | 31 +- .../unitemp/interfaces/OneWireSensor.h | 2 +- .../plugins/unitemp/interfaces/SPISensor.c | 89 ++++ .../plugins/unitemp/interfaces/SPISensor.h | 66 +++ .../unitemp/interfaces/SingleWireSensor.c | 8 +- .../unitemp/interfaces/SingleWireSensor.h | 2 +- applications/plugins/unitemp/sensors/AM2320.c | 2 +- applications/plugins/unitemp/sensors/AM2320.h | 2 +- applications/plugins/unitemp/sensors/BME680.c | 431 ++++++++++++++++++ applications/plugins/unitemp/sensors/BME680.h | 112 +++++ applications/plugins/unitemp/sensors/BMP180.c | 172 +++++++ applications/plugins/unitemp/sensors/BMP180.h | 62 +++ applications/plugins/unitemp/sensors/BMx280.c | 19 +- applications/plugins/unitemp/sensors/BMx280.h | 2 +- applications/plugins/unitemp/sensors/DHT20.c | 154 +++++++ applications/plugins/unitemp/sensors/DHT20.h | 63 +++ .../plugins/unitemp/sensors/HDC1080.c | 94 ++++ .../plugins/unitemp/sensors/HDC1080.h | 62 +++ applications/plugins/unitemp/sensors/HTU21x.c | 107 +++++ applications/plugins/unitemp/sensors/HTU21x.h | 62 +++ applications/plugins/unitemp/sensors/LM75.c | 2 +- applications/plugins/unitemp/sensors/LM75.h | 2 +- .../plugins/unitemp/sensors/MAX31855.c | 93 ++++ .../plugins/unitemp/sensors/MAX31855.h | 65 +++ .../plugins/unitemp/sensors/MAX6675.c | 81 ++++ .../plugins/unitemp/sensors/MAX6675.h | 65 +++ applications/plugins/unitemp/sensors/SHT30.c | 90 ++++ applications/plugins/unitemp/sensors/SHT30.h | 70 +++ .../plugins/unitemp/sensors/Sensors.xlsx | Bin 11864 -> 12642 bytes applications/plugins/unitemp/unitemp.c | 11 +- applications/plugins/unitemp/unitemp.h | 14 +- .../plugins/unitemp/views/General_view.c | 41 +- .../plugins/unitemp/views/MainMenu_view.c | 2 +- .../plugins/unitemp/views/Popup_view.c | 3 +- .../unitemp/views/SensorActions_view.c | 2 +- .../plugins/unitemp/views/SensorEdit_view.c | 47 +- .../unitemp/views/SensorNameEdit_view.c | 3 +- .../plugins/unitemp/views/SensorsList_view.c | 9 +- .../plugins/unitemp/views/Settings_view.c | 2 +- .../plugins/unitemp/views/UnitempViews.h | 2 +- .../plugins/unitemp/views/Widgets_view.c | 7 +- 46 files changed, 2149 insertions(+), 140 deletions(-) create mode 100644 applications/plugins/unitemp/interfaces/SPISensor.c create mode 100644 applications/plugins/unitemp/interfaces/SPISensor.h create mode 100644 applications/plugins/unitemp/sensors/BME680.c create mode 100644 applications/plugins/unitemp/sensors/BME680.h create mode 100644 applications/plugins/unitemp/sensors/BMP180.c create mode 100644 applications/plugins/unitemp/sensors/BMP180.h create mode 100644 applications/plugins/unitemp/sensors/DHT20.c create mode 100644 applications/plugins/unitemp/sensors/DHT20.h create mode 100644 applications/plugins/unitemp/sensors/HDC1080.c create mode 100644 applications/plugins/unitemp/sensors/HDC1080.h create mode 100644 applications/plugins/unitemp/sensors/HTU21x.c create mode 100644 applications/plugins/unitemp/sensors/HTU21x.h create mode 100644 applications/plugins/unitemp/sensors/MAX31855.c create mode 100644 applications/plugins/unitemp/sensors/MAX31855.h create mode 100644 applications/plugins/unitemp/sensors/MAX6675.c create mode 100644 applications/plugins/unitemp/sensors/MAX6675.h create mode 100644 applications/plugins/unitemp/sensors/SHT30.c create mode 100644 applications/plugins/unitemp/sensors/SHT30.h diff --git a/applications/plugins/unitemp/README.md b/applications/plugins/unitemp/README.md index 2f753faea..436f3600e 100644 --- a/applications/plugins/unitemp/README.md +++ b/applications/plugins/unitemp/README.md @@ -1,10 +1,24 @@ -![Flipper usage](https://user-images.githubusercontent.com/10090793/206618263-c1e212e4-58dc-432e-87a8-5c19fd835b35.png) +![Flipper usage](https://user-images.githubusercontent.com/10090793/211182642-e41919c5-3091-4125-815a-2d6a77a859f6.png) # Unitemp - Universal temperature sensor reader [![GitHub release](https://img.shields.io/github/release/quen0n/unitemp-flipperzero?include_prereleases=&sort=semver&color=blue)](https://github.com/quen0n/unitemp-flipperzero/releases/) -[![GitHub all releases](https://img.shields.io/github/downloads/quen0n/unitemp-flipperzero/total)]() -[![GitHub](https://img.shields.io/github/license/quen0n/unitemp-flipperzero)](https://github.com/quen0n/unitemp-flipperzero/blob/dev/LICENSE.md) -[Flipper Zero](https://flipperzero.one/) application for reading temperature, humidity and pressure sensors using Onewire, Singlewire, I2C protocols. -## List of supported sensors (supplemented) -![image](https://user-images.githubusercontent.com/10090793/208480561-e98a6192-d44d-4ad9-8692-a91ccaae47c7.png) +[![GitHub](https://img.shields.io/github/license/quen0n/unitemp-flipperzero)](https://github.com/quen0n/unitemp-flipperzero/blob/dev/LICENSE.md) +[![Build dev](https://github.com/quen0n/unitemp-flipperzero/actions/workflows/build_dev.yml/badge.svg?branch=dev)](https://github.com/quen0n/unitemp-flipperzero/actions/workflows/build_dev.yml) +[Flipper Zero](https://flipperzero.one/) application for reading temperature, humidity and pressure sensors like a DHT11/22, DS18B20, BMP280, HTU21 and more. +## List of supported sensors +![image](https://user-images.githubusercontent.com/10090793/215605424-54b1c08c-e41b-4fb4-b966-dd959507200b.png) + ## Installation -Copy the contents of the repository to the `applications/plugins/unitemp` folder and build the project. Flash FZ along with resources. [More...](https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/fbt.md) +1) Download [latest version](https://cloud.quenon.ru/index.php/s/h98rT9UnaOL4wxR/download?path=%2F&files=unitemp-latest.fap) +2) Copy `unitemp-latest.fap` to `SD card/apps/GPIO` with qFlipper or mobile application +3) Open application on your Flipper: `Applications->GPIO->Temp sensors reader` +Note: If you get the message "API version mismatch" after updating the firmware, download and install Unitemp again +## Need help? Discussions? +Join the discussion, ask a question or just send a photo of the flipper with sensors to [Discord](https://discord.com/channels/740930220399525928/1056727938747351060) +## Gratitudes +Thanks to [@Svaarich](https://github.com/Svaarich) for the UI design and to the Unleashed firmware community for sensors testing and feedbacks. + +## Some community photos +![image](https://user-images.githubusercontent.com/10090793/210120132-7ddbc937-0a6b-4472-bd1c-7fbc3ecdf2ad.png) +![image](https://user-images.githubusercontent.com/10090793/210120135-12fc5810-77ff-49db-b799-e9479e1f57a7.png) +![image](https://user-images.githubusercontent.com/10090793/210120143-a2bae3ce-4190-421f-8c4f-c7c744903bd6.png) +![image](https://user-images.githubusercontent.com/10090793/215224085-8099408e-b3de-4a0c-854e-fe4e4faa8ea3.png) \ No newline at end of file diff --git a/applications/plugins/unitemp/Sensors.c b/applications/plugins/unitemp/Sensors.c index d35e31931..2fca40b53 100644 --- a/applications/plugins/unitemp/Sensors.c +++ b/applications/plugins/unitemp/Sensors.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,7 +17,6 @@ */ #include "Sensors.h" #include -#include //Порты ввода/вывода, которые не были обозначены в общем списке const GpioPin SWC_10 = {.pin = LL_GPIO_PIN_14, .port = GPIOA}; @@ -67,12 +66,36 @@ const Interface ONE_WIRE = { .allocator = unitemp_onewire_sensor_alloc, .mem_releaser = unitemp_onewire_sensor_free, .updater = unitemp_onewire_sensor_update}; +const Interface SPI = { + .name = "SPI", + .allocator = unitemp_spi_sensor_alloc, + .mem_releaser = unitemp_spi_sensor_free, + .updater = unitemp_spi_sensor_update}; //Перечень интерфейсов подключения -//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE}; +//static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE, &SPI}; //Перечень датчиков -static const SensorType* sensorTypes[] = - {&DHT11, &DHT12_SW, &DHT21, &DHT22, &AM2320_SW, &AM2320_I2C, &LM75, &BMP280, &BME280, &Dallas}; +static const SensorType* sensorTypes[] = { + &DHT11, + &DHT12_SW, + &DHT20, + &DHT21, + &DHT22, + &Dallas, + &AM2320_SW, + &AM2320_I2C, + &HTU21x, + &AHT10, + &SHT30, + &GXHT30, + &LM75, + &HDC1080, + &BMP180, + &BMP280, + &BME280, + &BME680, + &MAX31855, + &MAX6675}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; @@ -151,7 +174,7 @@ uint8_t unitemp_gpio_getAviablePortsCount(const Interface* interface, const GPIO } //Проверка для single wire - if(interface == &SINGLE_WIRE) { + if(interface == &SINGLE_WIRE || interface == &SPI) { if(gpio_interfaces_list[i] == NULL || (unitemp_gpio_getFromIndex(i) == extraport)) { aviable_ports_count++; } @@ -190,6 +213,13 @@ const GPIO* return NULL; } } + if(interface == &SPI) { + if(!((gpio_interfaces_list[0] == NULL || gpio_interfaces_list[0] == &SPI) && + (gpio_interfaces_list[1] == NULL || gpio_interfaces_list[1] == &SPI) && + (gpio_interfaces_list[3] == NULL || gpio_interfaces_list[3] == &SPI))) { + return NULL; + } + } uint8_t aviable_index = 0; for(uint8_t i = 0; i < GPIO_ITEMS; i++) { @@ -207,7 +237,7 @@ const GPIO* } } //Проверка для single wire - if(interface == &SINGLE_WIRE) { + if(interface == &SINGLE_WIRE || interface == &SPI) { if(gpio_interfaces_list[i] == NULL || unitemp_gpio_getFromIndex(i) == extraport) { if(aviable_index == index) { return unitemp_gpio_getFromIndex(i); @@ -268,9 +298,7 @@ void unitemp_sensors_add(Sensor* sensor) { } bool unitemp_sensors_load(void) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Loading sensors..."); -#endif + UNITEMP_DEBUG("Loading sensors..."); //Выделение памяти на поток app->file_stream = file_stream_alloc(app->storage); @@ -331,7 +359,7 @@ bool unitemp_sensors_load(void) { //Сколько байт до конца строки size_t line_end = 0; - while(line_end != STRING_FAILURE && line_end != (size_t)(file_size - 1)) { + while(line_end != ((size_t)-1) && line_end != (size_t)(file_size - 1)) { //Имя датчика char name[11] = {0}; //Тип датчика @@ -379,9 +407,7 @@ bool unitemp_sensors_load(void) { } bool unitemp_sensors_save(void) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Saving sensors..."); -#endif + UNITEMP_DEBUG("Saving sensors..."); //Выделение памяти для потока app->file_stream = file_stream_alloc(app->storage); @@ -424,6 +450,11 @@ bool unitemp_sensors_save(void) { stream_write_format( app->file_stream, "%d\n", unitemp_singlewire_sensorGetGPIO(sensor)->num); } + if(sensor->type->interface == &SPI) { + uint8_t gpio_num = ((SPISensor*)sensor->instance)->CS_pin->num; + stream_write_format(app->file_stream, "%d\n", gpio_num); + } + if(sensor->type->interface == &I2C) { stream_write_format( app->file_stream, "%X\n", ((I2CSensor*)sensor->instance)->currentI2CAdr); @@ -501,7 +532,7 @@ Sensor* unitemp_sensor_alloc(char* name, const SensorType* type, char* args) { //Выход если датчик успешно развёрнут if(status) { - FURI_LOG_I(APP_NAME, "Sensor %s allocated", name); + UNITEMP_DEBUG("Sensor %s allocated", name); return sensor; } //Выход с очисткой если память для датчика не была выделена @@ -527,17 +558,13 @@ void unitemp_sensor_free(Sensor* sensor) { bool status = false; //Высвобождение памяти под инстанс status = sensor->type->interface->mem_releaser(sensor); - UNUSED(status); -#ifdef UNITEMP_DEBUG if(status) { - FURI_LOG_D(APP_NAME, "Sensor %s memory successfully released", sensor->name); + UNITEMP_DEBUG("Sensor %s memory successfully released", sensor->name); } else { FURI_LOG_E(APP_NAME, "Sensor %s memory is not released", sensor->name); } -#endif free(sensor->name); - //free(sensor); } void unitemp_sensors_free(void) { @@ -556,9 +583,7 @@ bool unitemp_sensors_init(void) { //Может пропасть при отключении USB if(furi_hal_power_is_otg_enabled() != true) { furi_hal_power_enable_otg(); -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "OTG enabled"); -#endif + UNITEMP_DEBUG("OTG enabled"); } if(!(*app->sensors[i]->type->initializer)(app->sensors[i])) { FURI_LOG_E( @@ -567,9 +592,7 @@ bool unitemp_sensors_init(void) { app->sensors[i]->name); result = false; } -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Sensor %s successfully initialized", app->sensors[i]->name); -#endif + FURI_LOG_I(APP_NAME, "Sensor %s successfully initialized", app->sensors[i]->name); } app->sensors_ready = true; return result; @@ -580,9 +603,7 @@ bool unitemp_sensors_deInit(void) { //Выключение 5 В если до этого оно не было включено if(app->settings.lastOTGState != true) { furi_hal_power_disable_otg(); -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "OTG disabled"); -#endif + UNITEMP_DEBUG("OTG disabled"); } //Перебор датчиков из списка @@ -618,13 +639,14 @@ UnitempStatus unitemp_sensor_updateData(Sensor* sensor) { sensor->status = sensor->type->interface->updater(sensor); -#ifdef UNITEMP_DEBUG - if(sensor->status != UT_SENSORSTATUS_OK && sensor->status != UT_SENSORSTATUS_POLLING) - FURI_LOG_D(APP_NAME, "Sensor %s update status %d", sensor->name, sensor->status); -#endif + if(sensor->status != UT_SENSORSTATUS_OK && sensor->status != UT_SENSORSTATUS_POLLING) { + UNITEMP_DEBUG("Sensor %s update status %d", sensor->name, sensor->status); + } - if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) + if(app->settings.temp_unit == UT_TEMP_FAHRENHEIT && sensor->status == UT_SENSORSTATUS_OK) { uintemp_celsiumToFarengate(sensor); + } + if(sensor->status == UT_SENSORSTATUS_OK) { sensor->temp += sensor->temp_offset / 10.f; if(app->settings.pressure_unit == UT_PRESSURE_MM_HG) { diff --git a/applications/plugins/unitemp/Sensors.h b/applications/plugins/unitemp/Sensors.h index 52e6165f5..d2b7c07af 100644 --- a/applications/plugins/unitemp/Sensors.h +++ b/applications/plugins/unitemp/Sensors.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -136,7 +136,7 @@ typedef struct Sensor { extern const Interface SINGLE_WIRE; //Собственный однопроводной протокол датчиков DHTXX и AM23XX extern const Interface ONE_WIRE; //Однопроводной протокол Dallas extern const Interface I2C; //I2C_2 (PC0, PC1) -//extern const Interface SPI; +extern const Interface SPI; //SPI_1 (MOSI - 2, MISO - 3, CS - 4, SCK - 5) /* ============================= Датчик(и) ============================= */ /** @@ -318,7 +318,15 @@ const GPIO* //DS18x2x #include "./interfaces/OneWireSensor.h" #include "./sensors/LM75.h" -//BMP280, BME280 +//BMP280, BME280, BME680 #include "./sensors/BMx280.h" +#include "./sensors/BME680.h" #include "./sensors/AM2320.h" +#include "./sensors/DHT20.h" +#include "./sensors/SHT30.h" +#include "./sensors/BMP180.h" +#include "./sensors/HTU21x.h" +#include "./sensors/HDC1080.h" +#include "./sensors/MAX31855.h" +#include "./sensors/MAX6675.h" #endif diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.c b/applications/plugins/unitemp/interfaces/I2CSensor.c index 72d959e27..e5901c282 100644 --- a/applications/plugins/unitemp/interfaces/I2CSensor.c +++ b/applications/plugins/unitemp/interfaces/I2CSensor.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/interfaces/I2CSensor.h b/applications/plugins/unitemp/interfaces/I2CSensor.h index 3df709d9a..4d468aae1 100644 --- a/applications/plugins/unitemp/interfaces/I2CSensor.h +++ b/applications/plugins/unitemp/interfaces/I2CSensor.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.c b/applications/plugins/unitemp/interfaces/OneWireSensor.c index 740ba3365..f4f3ebcdc 100644 --- a/applications/plugins/unitemp/interfaces/OneWireSensor.c +++ b/applications/plugins/unitemp/interfaces/OneWireSensor.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -58,9 +58,7 @@ OneWireBus* uintemp_onewire_bus_alloc(const GPIO* gpio) { bus->device_count = 0; bus->gpio = gpio; bus->powerMode = PWR_PASSIVE; -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "one wire bus (port %d) allocated", gpio->num); -#endif + UNITEMP_DEBUG("one wire bus (port %d) allocated", gpio->num); return bus; } @@ -84,9 +82,7 @@ bool unitemp_onewire_bus_init(OneWireBus* bus) { return true; } bool unitemp_onewire_bus_deinit(OneWireBus* bus) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "devices on wire %d: %d", bus->gpio->num, bus->device_count); -#endif + UNITEMP_DEBUG("devices on wire %d: %d", bus->gpio->num, bus->device_count); bus->device_count--; if(bus->device_count <= 0) { bus->device_count = 0; @@ -236,15 +232,11 @@ void unitemp_onewire_bus_enum_init(void) { uint8_t* unitemp_onewire_bus_enum_next(OneWireBus* bus) { furi_delay_ms(10); if(!onewire_enum_fork_bit) { // Если на предыдущем шаге уже не было разногласий -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "All devices on wire %d is found", unitemp_gpio_toInt(bus->gpio)); -#endif + UNITEMP_DEBUG("All devices on wire %d is found", unitemp_gpio_toInt(bus->gpio)); return 0; // то просто выходим ничего не возвращая } if(!unitemp_onewire_bus_start(bus)) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Wire %d is empty", unitemp_gpio_toInt(bus->gpio)); -#endif + UNITEMP_DEBUG("Wire %d is empty", unitemp_gpio_toInt(bus->gpio)); return 0; } uint8_t bp = 8; @@ -278,9 +270,8 @@ uint8_t* unitemp_onewire_bus_enum_next(OneWireBus* bus) { if(!not1) { // Присутствует единица next |= 0x80; } else { // Нет ни нулей ни единиц - ошибочная ситуация -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Wrong wire %d situation", unitemp_gpio_toInt(bus->gpio)); -#endif + + UNITEMP_DEBUG("Wrong wire %d situation", unitemp_gpio_toInt(bus->gpio)); return 0; } } @@ -421,9 +412,7 @@ UnitempStatus unitemp_onewire_sensor_update(Sensor* sensor) { unitemp_onewire_bus_send_byte(instance->bus, 0xBE); // Read Scratch-pad unitemp_onewire_bus_read_byteArray(instance->bus, buff, 9); if(!unitemp_onewire_CRC_check(buff, 9)) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Sensor %s is not found", sensor->name); -#endif + UNITEMP_DEBUG("Sensor %s is not found", sensor->name); return UT_SENSORSTATUS_TIMEOUT; } } @@ -462,9 +451,7 @@ UnitempStatus unitemp_onewire_sensor_update(Sensor* sensor) { unitemp_onewire_bus_send_byte(instance->bus, 0xBE); // Read Scratch-pad unitemp_onewire_bus_read_byteArray(instance->bus, buff, 9); if(!unitemp_onewire_CRC_check(buff, 9)) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Failed CRC check: %s", sensor->name); -#endif + UNITEMP_DEBUG("Failed CRC check: %s", sensor->name); return UT_SENSORSTATUS_BADCRC; } int16_t raw = buff[0] | ((int16_t)buff[1] << 8); diff --git a/applications/plugins/unitemp/interfaces/OneWireSensor.h b/applications/plugins/unitemp/interfaces/OneWireSensor.h index f2b148535..ef94db820 100644 --- a/applications/plugins/unitemp/interfaces/OneWireSensor.h +++ b/applications/plugins/unitemp/interfaces/OneWireSensor.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/interfaces/SPISensor.c b/applications/plugins/unitemp/interfaces/SPISensor.c new file mode 100644 index 000000000..b53aed28d --- /dev/null +++ b/applications/plugins/unitemp/interfaces/SPISensor.c @@ -0,0 +1,89 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include "SPISensor.h" + +static uint8_t sensors_count = 0; + +bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args) { + if(args == NULL) return false; + + //Создание инстанса датчика SPI + SPISensor* instance = malloc(sizeof(SPISensor)); + if(instance == NULL) { + FURI_LOG_E(APP_NAME, "Sensor %s instance allocation error", sensor->name); + return false; + } + sensor->instance = instance; + + //Определение GPIO chip select + int gpio = 255; + sscanf(args, "%d", &gpio); + instance->CS_pin = unitemp_gpio_getFromInt(gpio); + if(instance->CS_pin == NULL) { + FURI_LOG_E(APP_NAME, "Sensor %s GPIO setting error", sensor->name); + free(instance); + return false; + } + + instance->spi = malloc(sizeof(FuriHalSpiBusHandle)); + memcpy(instance->spi, &furi_hal_spi_bus_handle_external, sizeof(FuriHalSpiBusHandle)); + + instance->spi->cs = instance->CS_pin->pin; + + bool status = sensor->type->allocator(sensor, args); + + //Блокировка портов GPIO + sensors_count++; + unitemp_gpio_lock(unitemp_gpio_getFromInt(2), &SPI); + unitemp_gpio_lock(unitemp_gpio_getFromInt(3), &SPI); + unitemp_gpio_lock(unitemp_gpio_getFromInt(5), &SPI); + unitemp_gpio_lock(instance->CS_pin, &SPI); + return status; +} + +bool unitemp_spi_sensor_free(Sensor* sensor) { + bool status = sensor->type->mem_releaser(sensor); + unitemp_gpio_unlock(((SPISensor*)sensor->instance)->CS_pin); + free(((SPISensor*)(sensor->instance))->spi); + free(sensor->instance); + + if(--sensors_count == 0) { + unitemp_gpio_unlock(unitemp_gpio_getFromInt(2)); + unitemp_gpio_unlock(unitemp_gpio_getFromInt(3)); + unitemp_gpio_unlock(unitemp_gpio_getFromInt(5)); + } + + return status; +} + +bool unitemp_spi_sensor_init(Sensor* sensor) { + return sensor->type->initializer(sensor); +} + +bool unitemp_spi_sensor_deinit(Sensor* sensor) { + UNUSED(sensor); + + return true; +} + +UnitempStatus unitemp_spi_sensor_update(Sensor* sensor) { + return sensor->type->updater(sensor); +} \ No newline at end of file diff --git a/applications/plugins/unitemp/interfaces/SPISensor.h b/applications/plugins/unitemp/interfaces/SPISensor.h new file mode 100644 index 000000000..40f284b04 --- /dev/null +++ b/applications/plugins/unitemp/interfaces/SPISensor.h @@ -0,0 +1,66 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SPI +#define UNITEMP_SPI + +#include "../unitemp.h" +#include + +//Структура SPI датчика +typedef struct SPISensor { + //Указатель на интерфейс SPI + FuriHalSpiBusHandle* spi; + //Порт подключения CS + const GPIO* CS_pin; +} SPISensor; + +/** + * @brief Выделение памяти для датчика с интерфейсом SPI + * @param sensor Указатель на датчик + * @param args Указатель на массив аргументов с параметрами датчика + * @return Истина если всё ок + */ +bool unitemp_spi_sensor_alloc(Sensor* sensor, char* args); + +/** + * @brief Высвобождение памяти инстанса датчика + * @param sensor Указатель на датчик + */ +bool unitemp_spi_sensor_free(Sensor* sensor); + +/** + * @brief Инициализации датчика с интерфейсом one wire + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_spi_sensor_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_spi_sensor_deinit(Sensor* sensor); + +/** + * @brief Обновить значение с датчка + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_spi_sensor_update(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.c b/applications/plugins/unitemp/interfaces/SingleWireSensor.c index 183bfc872..d6d1b092b 100644 --- a/applications/plugins/unitemp/interfaces/SingleWireSensor.c +++ b/applications/plugins/unitemp/interfaces/SingleWireSensor.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -43,7 +43,7 @@ const SensorType DHT12_SW = { .updater = unitemp_singlewire_update}; const SensorType DHT21 = { .typename = "DHT21", - .altname = "DHT21 (AM2301)", + .altname = "DHT21/AM2301", .interface = &SINGLE_WIRE, .datatype = UT_DATA_TYPE_TEMP_HUM, .pollingInterval = 1000, @@ -54,7 +54,7 @@ const SensorType DHT21 = { .updater = unitemp_singlewire_update}; const SensorType DHT22 = { .typename = "DHT22", - .altname = "DHT22 (AM2302)", + .altname = "DHT22/AM2302", .interface = &SINGLE_WIRE, .datatype = UT_DATA_TYPE_TEMP_HUM, .pollingInterval = 2000, @@ -262,7 +262,7 @@ UnitempStatus unitemp_singlewire_update(Sensor* sensor) { //Проверка на отрицательность температуры if(READ_BIT(raw, 1 << 15)) { //Проверка на способ кодирования данных - if(READ_BIT(raw, 0x60)) { + if(READ_BIT(raw, 0x6000)) { //Не оригинал sensor->temp = (float)((int16_t)raw) / 10; } else { diff --git a/applications/plugins/unitemp/interfaces/SingleWireSensor.h b/applications/plugins/unitemp/interfaces/SingleWireSensor.h index f5bc74734..c762ff0aa 100644 --- a/applications/plugins/unitemp/interfaces/SingleWireSensor.h +++ b/applications/plugins/unitemp/interfaces/SingleWireSensor.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/AM2320.c b/applications/plugins/unitemp/sensors/AM2320.c index e77707005..29b255e1d 100644 --- a/applications/plugins/unitemp/sensors/AM2320.c +++ b/applications/plugins/unitemp/sensors/AM2320.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/AM2320.h b/applications/plugins/unitemp/sensors/AM2320.h index f13105470..fa5f502d4 100644 --- a/applications/plugins/unitemp/sensors/AM2320.h +++ b/applications/plugins/unitemp/sensors/AM2320.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/BME680.c b/applications/plugins/unitemp/sensors/BME680.c new file mode 100644 index 000000000..397e702cb --- /dev/null +++ b/applications/plugins/unitemp/sensors/BME680.c @@ -0,0 +1,431 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by g0gg0 (https://github.com/g3gg0) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "BME680.h" + +const SensorType BME680 = { + .typename = "BME680", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_HUMIDITY | UT_PRESSURE, + .pollingInterval = 500, + .allocator = unitemp_BME680_alloc, + .mem_releaser = unitemp_BME680_free, + .initializer = unitemp_BME680_init, + .deinitializer = unitemp_BME680_deinit, + .updater = unitemp_BME680_update}; + +//Интервал обновления калибровочных значений +#define BOSCH_CAL_UPDATE_INTERVAL 60000 + +#define BME680_ID 0x61 + +#define BME680_I2C_ADDR_MIN (0x76 << 1) +#define BME680_I2C_ADDR_MAX (0x77 << 1) + +#define BME680_REG_STATUS 0x1D +#define BME680_REG_CTRL_MEAS 0x74 +#define BME680_REG_CONFIG 0x75 +#define BME680_REG_CTRL_HUM 0x72 +//Преддескретизация температуры +#define BME680_TEMP_OVERSAMPLING_SKIP 0b00000000 +#define BME680_TEMP_OVERSAMPLING_1 0b00100000 +#define BME680_TEMP_OVERSAMPLING_2 0b01000000 +#define BME680_TEMP_OVERSAMPLING_4 0b01100000 +#define BME680_TEMP_OVERSAMPLING_8 0b10000000 +#define BME680_TEMP_OVERSAMPLING_16 0b10100000 +//Преддескретизация давления +#define BME680_PRESS_OVERSAMPLING_SKIP 0b00000000 +#define BME680_PRESS_OVERSAMPLING_1 0b00000100 +#define BME680_PRESS_OVERSAMPLING_2 0b00001000 +#define BME680_PRESS_OVERSAMPLING_4 0b00001100 +#define BME680_PRESS_OVERSAMPLING_8 0b00010000 +#define BME680_PRESS_OVERSAMPLING_16 0b00010100 +//Преддескретизация влажности +#define BME680_HUM_OVERSAMPLING_SKIP 0b00000000 +#define BME680_HUM_OVERSAMPLING_1 0b00000001 +#define BME680_HUM_OVERSAMPLING_2 0b00000010 +#define BME680_HUM_OVERSAMPLING_4 0b00000011 +#define BME680_HUM_OVERSAMPLING_8 0b00000100 +#define BME680_HUM_OVERSAMPLING_16 0b00000101 +//Режимы работы датчика +#define BME680_MODE_SLEEP 0b00000000 //Наелся и спит +#define BME680_MODE_FORCED 0b00000001 //Обновляет значения 1 раз, после чего уходит в сон +//Коэффициент фильтрации значений +#define BME680_FILTER_COEFF_1 0b00000000 +#define BME680_FILTER_COEFF_2 0b00000100 +#define BME680_FILTER_COEFF_4 0b00001000 +#define BME680_FILTER_COEFF_8 0b00001100 +#define BME680_FILTER_COEFF_16 0b00010000 +//Разрешить работу по SPI +#define BME680_SPI_3W_ENABLE 0b00000001 +#define BME680_SPI_3W_DISABLE 0b00000000 + +/* https://github.com/boschsensortec/BME680_driver/blob/master/bme680.c or + https://github.com/boschsensortec/BME68x-Sensor-API */ +static float BME680_compensate_temperature(I2CSensor* i2c_sensor, int32_t temp_adc) { + BME680_instance* bme680_instance = (BME680_instance*)i2c_sensor->sensorInstance; + float var1 = 0; + float var2 = 0; + float calc_temp = 0; + + /* calculate var1 data */ + var1 = + ((((float)temp_adc / 16384.0f) - ((float)bme680_instance->temp_cal.dig_T1 / 1024.0f)) * + ((float)bme680_instance->temp_cal.dig_T2)); + + /* calculate var2 data */ + var2 = + (((((float)temp_adc / 131072.0f) - ((float)bme680_instance->temp_cal.dig_T1 / 8192.0f)) * + (((float)temp_adc / 131072.0f) - ((float)bme680_instance->temp_cal.dig_T1 / 8192.0f))) * + ((float)bme680_instance->temp_cal.dig_T3 * 16.0f)); + + /* t_fine value*/ + bme680_instance->t_fine = (var1 + var2); + + /* compensated temperature data*/ + calc_temp = ((bme680_instance->t_fine) / 5120.0f); + + return calc_temp; +} + +static float BME680_compensate_pressure(I2CSensor* i2c_sensor, int32_t pres_adc) { + BME680_instance* bme680_instance = (BME680_instance*)i2c_sensor->sensorInstance; + + float var1; + float var2; + float var3; + float calc_pres; + + var1 = (((float)bme680_instance->t_fine / 2.0f) - 64000.0f); + var2 = var1 * var1 * (((float)bme680_instance->press_cal.dig_P6) / (131072.0f)); + var2 = var2 + (var1 * ((float)bme680_instance->press_cal.dig_P5) * 2.0f); + var2 = (var2 / 4.0f) + (((float)bme680_instance->press_cal.dig_P4) * 65536.0f); + var1 = + (((((float)bme680_instance->press_cal.dig_P3 * var1 * var1) / 16384.0f) + + ((float)bme680_instance->press_cal.dig_P2 * var1)) / + 524288.0f); + var1 = ((1.0f + (var1 / 32768.0f)) * ((float)bme680_instance->press_cal.dig_P1)); + calc_pres = (1048576.0f - ((float)pres_adc)); + + /* Avoid exception caused by division by zero */ + if((int)var1 != 0) { + calc_pres = (((calc_pres - (var2 / 4096.0f)) * 6250.0f) / var1); + var1 = + (((float)bme680_instance->press_cal.dig_P9) * calc_pres * calc_pres) / 2147483648.0f; + var2 = calc_pres * (((float)bme680_instance->press_cal.dig_P8) / 32768.0f); + var3 = + ((calc_pres / 256.0f) * (calc_pres / 256.0f) * (calc_pres / 256.0f) * + (bme680_instance->press_cal.dig_P10 / 131072.0f)); + calc_pres = + (calc_pres + + (var1 + var2 + var3 + ((float)bme680_instance->press_cal.dig_P7 * 128.0f)) / 16.0f); + } else { + calc_pres = 0; + } + + return calc_pres; +} + +static float BME680_compensate_humidity(I2CSensor* i2c_sensor, int32_t hum_adc) { + BME680_instance* bme680_instance = (BME680_instance*)i2c_sensor->sensorInstance; + float calc_hum; + float var1; + float var2; + float var3; + float var4; + float temp_comp; + + /* compensated temperature data*/ + temp_comp = ((bme680_instance->t_fine) / 5120.0f); + var1 = + (float)((float)hum_adc) - (((float)bme680_instance->hum_cal.dig_H1 * 16.0f) + + (((float)bme680_instance->hum_cal.dig_H3 / 2.0f) * temp_comp)); + var2 = var1 * + ((float)(((float)bme680_instance->hum_cal.dig_H2 / 262144.0f) * + (1.0f + (((float)bme680_instance->hum_cal.dig_H4 / 16384.0f) * temp_comp) + + (((float)bme680_instance->hum_cal.dig_H5 / 1048576.0f) * temp_comp * temp_comp)))); + var3 = (float)bme680_instance->hum_cal.dig_H6 / 16384.0f; + var4 = (float)bme680_instance->hum_cal.dig_H7 / 2097152.0f; + calc_hum = var2 + ((var3 + (var4 * temp_comp)) * var2 * var2); + if(calc_hum > 100.0f) { + calc_hum = 100.0f; + } else if(calc_hum < 0.0f) { + calc_hum = 0.0f; + } + + return calc_hum; +} + +/* https://github.com/boschsensortec/BME680_driver/blob/master/bme680_defs.h */ +#define BME680_COEFF_SIZE UINT8_C(41) +#define BME680_COEFF_ADDR1_LEN UINT8_C(25) +#define BME680_COEFF_ADDR2_LEN UINT8_C(16) +#define BME680_COEFF_ADDR1 UINT8_C(0x89) +#define BME680_COEFF_ADDR2 UINT8_C(0xe1) +#define BME680_CONCAT_BYTES(msb, lsb) (((uint16_t)msb << 8) | (uint16_t)lsb) +#define BME680_T2_LSB_REG (1) +#define BME680_T2_MSB_REG (2) +#define BME680_T3_REG (3) +#define BME680_P1_LSB_REG (5) +#define BME680_P1_MSB_REG (6) +#define BME680_P2_LSB_REG (7) +#define BME680_P2_MSB_REG (8) +#define BME680_P3_REG (9) +#define BME680_P4_LSB_REG (11) +#define BME680_P4_MSB_REG (12) +#define BME680_P5_LSB_REG (13) +#define BME680_P5_MSB_REG (14) +#define BME680_P7_REG (15) +#define BME680_P6_REG (16) +#define BME680_P8_LSB_REG (19) +#define BME680_P8_MSB_REG (20) +#define BME680_P9_LSB_REG (21) +#define BME680_P9_MSB_REG (22) +#define BME680_P10_REG (23) +#define BME680_H2_MSB_REG (25) +#define BME680_H2_LSB_REG (26) +#define BME680_H1_LSB_REG (26) +#define BME680_H1_MSB_REG (27) +#define BME680_H3_REG (28) +#define BME680_H4_REG (29) +#define BME680_H5_REG (30) +#define BME680_H6_REG (31) +#define BME680_H7_REG (32) +#define BME680_T1_LSB_REG (33) +#define BME680_T1_MSB_REG (34) +#define BME680_GH2_LSB_REG (35) +#define BME680_GH2_MSB_REG (36) +#define BME680_GH1_REG (37) +#define BME680_GH3_REG (38) +#define BME680_HUM_REG_SHIFT_VAL UINT8_C(4) +#define BME680_BIT_H1_DATA_MSK UINT8_C(0x0F) + +static bool BME680_readCalValues(I2CSensor* i2c_sensor) { + BME680_instance* bme680_instance = (BME680_instance*)i2c_sensor->sensorInstance; + uint8_t coeff_array[BME680_COEFF_SIZE] = {0}; + + if(!unitemp_i2c_readRegArray( + i2c_sensor, BME680_COEFF_ADDR1, BME680_COEFF_ADDR1_LEN, &coeff_array[0])) + return false; + if(!unitemp_i2c_readRegArray( + i2c_sensor, + BME680_COEFF_ADDR2, + BME680_COEFF_ADDR2_LEN, + &coeff_array[BME680_COEFF_ADDR1_LEN])) + return false; + + /* Temperature related coefficients */ + bme680_instance->temp_cal.dig_T1 = (uint16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_T1_MSB_REG], coeff_array[BME680_T1_LSB_REG])); + bme680_instance->temp_cal.dig_T2 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_T2_MSB_REG], coeff_array[BME680_T2_LSB_REG])); + bme680_instance->temp_cal.dig_T3 = (int8_t)(coeff_array[BME680_T3_REG]); + + /* Pressure related coefficients */ + bme680_instance->press_cal.dig_P1 = (uint16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P1_MSB_REG], coeff_array[BME680_P1_LSB_REG])); + bme680_instance->press_cal.dig_P2 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P2_MSB_REG], coeff_array[BME680_P2_LSB_REG])); + bme680_instance->press_cal.dig_P3 = (int8_t)coeff_array[BME680_P3_REG]; + bme680_instance->press_cal.dig_P4 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P4_MSB_REG], coeff_array[BME680_P4_LSB_REG])); + bme680_instance->press_cal.dig_P5 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P5_MSB_REG], coeff_array[BME680_P5_LSB_REG])); + bme680_instance->press_cal.dig_P6 = (int8_t)(coeff_array[BME680_P6_REG]); + bme680_instance->press_cal.dig_P7 = (int8_t)(coeff_array[BME680_P7_REG]); + bme680_instance->press_cal.dig_P8 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P8_MSB_REG], coeff_array[BME680_P8_LSB_REG])); + bme680_instance->press_cal.dig_P9 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_P9_MSB_REG], coeff_array[BME680_P9_LSB_REG])); + bme680_instance->press_cal.dig_P10 = (uint8_t)(coeff_array[BME680_P10_REG]); + + /* Humidity related coefficients */ + bme680_instance->hum_cal.dig_H1 = + (uint16_t)(((uint16_t)coeff_array[BME680_H1_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | (coeff_array[BME680_H1_LSB_REG] & BME680_BIT_H1_DATA_MSK)); + bme680_instance->hum_cal.dig_H2 = + (uint16_t)(((uint16_t)coeff_array[BME680_H2_MSB_REG] << BME680_HUM_REG_SHIFT_VAL) | ((coeff_array[BME680_H2_LSB_REG]) >> BME680_HUM_REG_SHIFT_VAL)); + bme680_instance->hum_cal.dig_H3 = (int8_t)coeff_array[BME680_H3_REG]; + bme680_instance->hum_cal.dig_H4 = (int8_t)coeff_array[BME680_H4_REG]; + bme680_instance->hum_cal.dig_H5 = (int8_t)coeff_array[BME680_H5_REG]; + bme680_instance->hum_cal.dig_H6 = (uint8_t)coeff_array[BME680_H6_REG]; + bme680_instance->hum_cal.dig_H7 = (int8_t)coeff_array[BME680_H7_REG]; + + /* Gas heater related coefficients */ + bme680_instance->gas_cal.dig_GH1 = (int8_t)coeff_array[BME680_GH1_REG]; + bme680_instance->gas_cal.dig_GH2 = (int16_t)(BME680_CONCAT_BYTES( + coeff_array[BME680_GH2_MSB_REG], coeff_array[BME680_GH2_LSB_REG])); + bme680_instance->gas_cal.dig_GH3 = (int8_t)coeff_array[BME680_GH3_REG]; + +#ifdef UNITEMP_DEBUG + FURI_LOG_D( + APP_NAME, + "Sensor BME680 T1-T3: %d, %d, %d", + bme680_instance->temp_cal.dig_T1, + bme680_instance->temp_cal.dig_T2, + bme680_instance->temp_cal.dig_T3); + + FURI_LOG_D( + APP_NAME, + "Sensor BME680: P1-P10: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + bme680_instance->press_cal.dig_P1, + bme680_instance->press_cal.dig_P2, + bme680_instance->press_cal.dig_P3, + bme680_instance->press_cal.dig_P4, + bme680_instance->press_cal.dig_P5, + bme680_instance->press_cal.dig_P6, + bme680_instance->press_cal.dig_P7, + bme680_instance->press_cal.dig_P8, + bme680_instance->press_cal.dig_P9, + bme680_instance->press_cal.dig_P10); + + FURI_LOG_D( + APP_NAME, + "Sensor BME680: H1-H7: %d, %d, %d, %d, %d, %d, %d", + bme680_instance->hum_cal.dig_H1, + bme680_instance->hum_cal.dig_H2, + bme680_instance->hum_cal.dig_H3, + bme680_instance->hum_cal.dig_H4, + bme680_instance->hum_cal.dig_H5, + bme680_instance->hum_cal.dig_H6, + bme680_instance->hum_cal.dig_H7); + + FURI_LOG_D( + APP_NAME, + "Sensor BME680 GH1-GH3: %d, %d, %d", + bme680_instance->gas_cal.dig_GH1, + bme680_instance->gas_cal.dig_GH2, + bme680_instance->gas_cal.dig_GH3); + +#endif + + bme680_instance->last_cal_update_time = furi_get_tick(); + return true; +} +static bool BME680_isMeasuring(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + return (bool)(unitemp_i2c_readReg(i2c_sensor, BME680_REG_STATUS) & 0x20); +} + +bool unitemp_BME680_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + BME680_instance* bme680_instance = malloc(sizeof(BME680_instance)); + if(bme680_instance == NULL) { + FURI_LOG_E(APP_NAME, "Failed to allocation sensor %s instance", sensor->name); + return false; + } + + if(sensor->type == &BME680) bme680_instance->chip_id = BME680_ID; + + i2c_sensor->sensorInstance = bme680_instance; + + i2c_sensor->minI2CAdr = BME680_I2C_ADDR_MIN; + i2c_sensor->maxI2CAdr = BME680_I2C_ADDR_MAX; + return true; +} + +bool unitemp_BME680_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + //Перезагрузка + unitemp_i2c_writeReg(i2c_sensor, 0xE0, 0xB6); + //Чтение ID датчика + uint8_t id = unitemp_i2c_readReg(i2c_sensor, 0xD0); + if(id != BME680_ID) { + FURI_LOG_E( + APP_NAME, + "Sensor %s returned wrong ID 0x%02X, expected 0x%02X", + sensor->name, + id, + BME680_ID); + return false; + } + + unitemp_i2c_writeReg( + i2c_sensor, + BME680_REG_CTRL_HUM, + (unitemp_i2c_readReg(i2c_sensor, BME680_REG_CTRL_HUM) & ~7) | BME680_HUM_OVERSAMPLING_1); + unitemp_i2c_writeReg( + i2c_sensor, + BME680_REG_CTRL_MEAS, + BME680_TEMP_OVERSAMPLING_2 | BME680_PRESS_OVERSAMPLING_4 | BME680_MODE_FORCED); + //Настройка периода опроса и фильтрации значений + unitemp_i2c_writeReg( + i2c_sensor, BME680_REG_CONFIG, BME680_FILTER_COEFF_16 | BME680_SPI_3W_DISABLE); + //Чтение калибровочных значений + if(!BME680_readCalValues(i2c_sensor)) { + FURI_LOG_E(APP_NAME, "Failed to read calibration values sensor %s", sensor->name); + return false; + } + return true; +} + +bool unitemp_BME680_deinit(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + //Перевод в сон + unitemp_i2c_writeReg(i2c_sensor, BME680_REG_CTRL_MEAS, BME680_MODE_SLEEP); + return true; +} + +UnitempStatus unitemp_BME680_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + BME680_instance* instance = i2c_sensor->sensorInstance; + + uint32_t t = furi_get_tick(); + + uint8_t buff[3]; + //Проверка инициализированности датчика + unitemp_i2c_readRegArray(i2c_sensor, 0xF4, 2, buff); + if(buff[0] == 0) { + FURI_LOG_W(APP_NAME, "Sensor %s is not initialized!", sensor->name); + return UT_SENSORSTATUS_ERROR; + } + + unitemp_i2c_writeReg( + i2c_sensor, + BME680_REG_CTRL_MEAS, + unitemp_i2c_readReg(i2c_sensor, BME680_REG_CTRL_MEAS) | 1); + + while(BME680_isMeasuring(sensor)) { + if(furi_get_tick() - t > 100) { + return UT_SENSORSTATUS_TIMEOUT; + } + } + + if(furi_get_tick() - instance->last_cal_update_time > BOSCH_CAL_UPDATE_INTERVAL) { + BME680_readCalValues(i2c_sensor); + } + + if(!unitemp_i2c_readRegArray(i2c_sensor, 0x1F, 3, buff)) return UT_SENSORSTATUS_TIMEOUT; + int32_t adc_P = ((int32_t)buff[0] << 12) | ((int32_t)buff[1] << 4) | ((int32_t)buff[2] >> 4); + if(!unitemp_i2c_readRegArray(i2c_sensor, 0x22, 3, buff)) return UT_SENSORSTATUS_TIMEOUT; + int32_t adc_T = ((int32_t)buff[0] << 12) | ((int32_t)buff[1] << 4) | ((int32_t)buff[2] >> 4); + if(!unitemp_i2c_readRegArray(i2c_sensor, 0x25, 2, buff)) return UT_SENSORSTATUS_TIMEOUT; + int32_t adc_H = ((uint16_t)buff[0] << 8) | buff[1]; + + sensor->temp = BME680_compensate_temperature(i2c_sensor, adc_T); + sensor->pressure = BME680_compensate_pressure(i2c_sensor, adc_P); + sensor->hum = BME680_compensate_humidity(i2c_sensor, adc_H); + + return UT_SENSORSTATUS_OK; +} + +bool unitemp_BME680_free(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + free(i2c_sensor->sensorInstance); + return true; +} \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/BME680.h b/applications/plugins/unitemp/sensors/BME680.h new file mode 100644 index 000000000..b126c7c84 --- /dev/null +++ b/applications/plugins/unitemp/sensors/BME680.h @@ -0,0 +1,112 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + Contributed by g0gg0 (https://github.com/g3gg0) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_BME680 +#define UNITEMP_BME680 + +#include "../unitemp.h" +#include "../Sensors.h" +#include "../interfaces/I2CSensor.h" + +typedef struct { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; +} BME680_temp_cal; + +typedef struct { + uint16_t dig_GH1; + int16_t dig_GH2; + int16_t dig_GH3; +} BME680_gas_cal; + +typedef struct { + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + int16_t dig_P10; +} BME680_press_cal; + +typedef struct { + uint16_t dig_H1; + uint16_t dig_H2; + int8_t dig_H3; + int8_t dig_H4; + int8_t dig_H5; + uint8_t dig_H6; + int8_t dig_H7; +} BME680_hum_cal; + +typedef struct { + //Калибровочные значения температуры + BME680_temp_cal temp_cal; + //Калибровочные значения давления + BME680_press_cal press_cal; + //Калибровочные значения влажности воздуха + BME680_hum_cal hum_cal; + BME680_gas_cal gas_cal; + //Время последнего обновления калибровочных значений + uint32_t last_cal_update_time; + //Индификатор датчика + uint8_t chip_id; + //Корректировочное значение температуры + int32_t t_fine; +} BME680_instance; + +extern const SensorType BMP280; +extern const SensorType BME680; +/** + * @brief Выделение памяти и установка начальных значений датчика BMP280 + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_BME680_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика BMP280 + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_BME680_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * @param sensor Указатель на датчик + */ +bool unitemp_BME680_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * @param sensor Указатель на датчик + * @return Статус опроса датчика + */ +UnitempStatus unitemp_BME680_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * @param sensor Указатель на датчик + */ +bool unitemp_BME680_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/BMP180.c b/applications/plugins/unitemp/sensors/BMP180.c new file mode 100644 index 000000000..e94f38044 --- /dev/null +++ b/applications/plugins/unitemp/sensors/BMP180.c @@ -0,0 +1,172 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "BMP180.h" +#include "../interfaces/I2CSensor.h" + +typedef struct { + int16_t AC1; + int16_t AC2; + int16_t AC3; + uint16_t AC4; + uint16_t AC5; + uint16_t AC6; + int16_t B1; + int16_t B2; + int16_t MB; + int16_t MC; + int16_t MD; +} BMP180_cal; + +typedef struct { + //Калибровочные значения + BMP180_cal bmp180_cal; +} BMP180_instance; + +const SensorType BMP180 = { + .typename = "BMP180", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_PRESSURE, + .pollingInterval = 1000, + .allocator = unitemp_BMP180_I2C_alloc, + .mem_releaser = unitemp_BMP180_I2C_free, + .initializer = unitemp_BMP180_init, + .deinitializer = unitemp_BMP180_I2C_deinit, + .updater = unitemp_BMP180_I2C_update}; + +bool unitemp_BMP180_I2C_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Адреса на шине I2C (7 бит) + i2c_sensor->minI2CAdr = 0x77 << 1; + i2c_sensor->maxI2CAdr = 0x77 << 1; + + BMP180_instance* bmx280_instance = malloc(sizeof(BMP180_instance)); + i2c_sensor->sensorInstance = bmx280_instance; + return true; +} + +bool unitemp_BMP180_I2C_free(Sensor* sensor) { + UNUSED(sensor); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + free(i2c_sensor->sensorInstance); + return true; +} + +bool unitemp_BMP180_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Перезагрузка + if(!unitemp_i2c_writeReg(i2c_sensor, 0xE0, 0xB6)) return false; + furi_delay_ms(100); + + //Проверка ID + uint8_t id = unitemp_i2c_readReg(i2c_sensor, 0xD0); + if(id != 0x55) { + FURI_LOG_E( + APP_NAME, "Sensor %s returned wrong ID 0x%02X, expected 0x55", sensor->name, id); + return false; + } + + BMP180_instance* bmp180_instance = i2c_sensor->sensorInstance; + + uint8_t buff[22] = {0}; + + //Чтение калибровочных значений + if(!unitemp_i2c_readRegArray(i2c_sensor, 0xAA, 22, buff)) return false; + bmp180_instance->bmp180_cal.AC1 = (buff[0] << 8) | buff[1]; + bmp180_instance->bmp180_cal.AC2 = (buff[2] << 8) | buff[3]; + bmp180_instance->bmp180_cal.AC3 = (buff[4] << 8) | buff[5]; + bmp180_instance->bmp180_cal.AC4 = (buff[6] << 8) | buff[7]; + bmp180_instance->bmp180_cal.AC5 = (buff[8] << 8) | buff[9]; + bmp180_instance->bmp180_cal.AC6 = (buff[10] << 8) | buff[11]; + bmp180_instance->bmp180_cal.B1 = (buff[12] << 8) | buff[13]; + bmp180_instance->bmp180_cal.B2 = (buff[14] << 8) | buff[15]; + bmp180_instance->bmp180_cal.MB = (buff[16] << 8) | buff[17]; + bmp180_instance->bmp180_cal.MC = (buff[18] << 8) | buff[19]; + bmp180_instance->bmp180_cal.MD = (buff[20] << 8) | buff[21]; + + +UNITEMP_DEBUG( + "Sensor BMP180 (0x%02X) calibration values: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", + i2c_sensor->currentI2CAdr, + bmp180_instance->bmp180_cal.AC1, + bmp180_instance->bmp180_cal.AC2, + bmp180_instance->bmp180_cal.AC3, + bmp180_instance->bmp180_cal.AC4, + bmp180_instance->bmp180_cal.AC5, + bmp180_instance->bmp180_cal.AC6, + bmp180_instance->bmp180_cal.B1, + bmp180_instance->bmp180_cal.B2, + bmp180_instance->bmp180_cal.MB, + bmp180_instance->bmp180_cal.MC, + bmp180_instance->bmp180_cal.MD); + return true; +} + +bool unitemp_BMP180_I2C_deinit(Sensor* sensor) { + //Нечего деинициализировать + UNUSED(sensor); + return true; +} + +UnitempStatus unitemp_BMP180_I2C_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + BMP180_instance* bmp180_instance = i2c_sensor->sensorInstance; + + //Чтение температуры + if(!unitemp_i2c_writeReg(i2c_sensor, 0xF4, 0x2E)) return UT_SENSORSTATUS_TIMEOUT; + furi_delay_ms(5); + uint8_t buff[3] = {0}; + if(!unitemp_i2c_readRegArray(i2c_sensor, 0xF6, 2, buff)) return UT_SENSORSTATUS_TIMEOUT; + int32_t UT = ((uint16_t)buff[0] << 8) + buff[1]; + int32_t X1 = (UT - bmp180_instance->bmp180_cal.AC6) * bmp180_instance->bmp180_cal.AC5 >> 15; + int32_t X2 = (bmp180_instance->bmp180_cal.MC << 11) / (X1 + bmp180_instance->bmp180_cal.MD); + int32_t B5 = X1 + X2; + sensor->temp = ((B5 + 8) / 16) * 0.1f; + + //Чтение давления + if(!unitemp_i2c_writeReg(i2c_sensor, 0xF4, 0x34 + (0b11 << 6))) return UT_SENSORSTATUS_TIMEOUT; + furi_delay_ms(26); + if(!unitemp_i2c_readRegArray(i2c_sensor, 0xF6, 3, buff)) return UT_SENSORSTATUS_TIMEOUT; + uint32_t UP = ((buff[0] << 16) + (buff[1] << 8) + buff[2]) >> (8 - 0b11); + + int32_t B6, X3, B3, P; + uint32_t B4, B7; + B6 = B5 - 4000; + X1 = (bmp180_instance->bmp180_cal.B2 * ((B6 * B6) >> 12)) >> 11; + X2 = (bmp180_instance->bmp180_cal.AC2 * B6) >> 11; + X3 = X1 + X2; + B3 = (((bmp180_instance->bmp180_cal.AC1 * 4 + X3) << 0b11) + 2) >> 2; + X1 = (bmp180_instance->bmp180_cal.AC3 * B6) >> 13; + X2 = (bmp180_instance->bmp180_cal.B1 * ((B6 * B6) >> 12)) >> 16; + X3 = ((X1 + X2) + 2) >> 2; + B4 = (bmp180_instance->bmp180_cal.AC4 * (unsigned long)(X3 + 32768)) >> 15; + B7 = ((unsigned long)UP - B3) * (50000 >> 0b11); + if(B7 < 0x80000000) + P = (B7 * 2) / B4; + else + P = (B7 / B4) * 2; + X1 = (P >> 8) * (P >> 8); + X1 = (X1 * 3038) >> 16; + X2 = (-7357 * (P)) >> 16; + P = P + ((X1 + X2 + 3791) >> 4); + sensor->pressure = P; + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/BMP180.h b/applications/plugins/unitemp/sensors/BMP180.h new file mode 100644 index 000000000..ce2569092 --- /dev/null +++ b/applications/plugins/unitemp/sensors/BMP180.h @@ -0,0 +1,62 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_BMP180 +#define UNITEMP_BMP180 + +#include "../unitemp.h" +#include "../Sensors.h" +extern const SensorType BMP180; +/** + * @brief Выделение памяти и установка начальных значений датчика BMP180 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_BMP180_I2C_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика BMP180 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_BMP180_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_BMP180_I2C_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_BMP180_I2C_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_BMP180_I2C_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/BMx280.c b/applications/plugins/unitemp/sensors/BMx280.c index a64daaa1d..db445d330 100644 --- a/applications/plugins/unitemp/sensors/BMx280.c +++ b/applications/plugins/unitemp/sensors/BMx280.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -180,22 +180,19 @@ static bool bmx280_readCalValues(I2CSensor* i2c_sensor) { if(!unitemp_i2c_readRegArray( i2c_sensor, TEMP_CAL_START_ADDR, 6, (uint8_t*)&bmx280_instance->temp_cal)) return false; -#ifdef UNITEMP_DEBUG - FURI_LOG_D( - APP_NAME, + + UNITEMP_DEBUG( "Sensor BMx280 (0x%02X) T1-T3: %d, %d, %d", i2c_sensor->currentI2CAdr, bmx280_instance->temp_cal.dig_T1, bmx280_instance->temp_cal.dig_T2, bmx280_instance->temp_cal.dig_T3); -#endif if(!unitemp_i2c_readRegArray( i2c_sensor, PRESS_CAL_START_ADDR, 18, (uint8_t*)&bmx280_instance->press_cal)) return false; -#ifdef UNITEMP_DEBUG - FURI_LOG_D( - APP_NAME, + + UNITEMP_DEBUG( "Sensor BMx280 (0x%02X): P1-P9: %d, %d, %d, %d, %d, %d, %d, %d, %d", i2c_sensor->currentI2CAdr, bmx280_instance->press_cal.dig_P1, @@ -207,7 +204,6 @@ static bool bmx280_readCalValues(I2CSensor* i2c_sensor) { bmx280_instance->press_cal.dig_P7, bmx280_instance->press_cal.dig_P8, bmx280_instance->press_cal.dig_P9); -#endif if(bmx280_instance->chip_id == BME280_ID) { uint8_t buff[7] = {0}; @@ -221,9 +217,7 @@ static bool bmx280_readCalValues(I2CSensor* i2c_sensor) { bmx280_instance->hum_cal.dig_H5 = (buff[4] & 0x0F) | ((int16_t)buff[5] << 4); bmx280_instance->hum_cal.dig_H6 = buff[6]; -#ifdef UNITEMP_DEBUG - FURI_LOG_D( - APP_NAME, + UNITEMP_DEBUG( "Sensor BMx280 (0x%02X): H1-H6: %d, %d, %d, %d, %d, %d", i2c_sensor->currentI2CAdr, bmx280_instance->hum_cal.dig_H1, @@ -232,7 +226,6 @@ static bool bmx280_readCalValues(I2CSensor* i2c_sensor) { bmx280_instance->hum_cal.dig_H4, bmx280_instance->hum_cal.dig_H5, bmx280_instance->hum_cal.dig_H6); -#endif } bmx280_instance->last_cal_update_time = furi_get_tick(); diff --git a/applications/plugins/unitemp/sensors/BMx280.h b/applications/plugins/unitemp/sensors/BMx280.h index fe52a364e..32027f285 100644 --- a/applications/plugins/unitemp/sensors/BMx280.h +++ b/applications/plugins/unitemp/sensors/BMx280.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/DHT20.c b/applications/plugins/unitemp/sensors/DHT20.c new file mode 100644 index 000000000..ce11fe3d6 --- /dev/null +++ b/applications/plugins/unitemp/sensors/DHT20.c @@ -0,0 +1,154 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "DHT20.h" +#include "../interfaces/I2CSensor.h" + +const SensorType DHT20 = { + .typename = "DHT20", + .altname = "DHT20/AM2108/AHT20", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_HUMIDITY, + .pollingInterval = 1000, + .allocator = unitemp_DHT20_I2C_alloc, + .mem_releaser = unitemp_DHT20_I2C_free, + .initializer = unitemp_DHT20_init, + .deinitializer = unitemp_DHT20_I2C_deinit, + .updater = unitemp_DHT20_I2C_update}; +const SensorType AHT10 = { + .typename = "AHT10", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_HUMIDITY, + .pollingInterval = 1000, + .allocator = unitemp_DHT20_I2C_alloc, + .mem_releaser = unitemp_DHT20_I2C_free, + .initializer = unitemp_DHT20_init, + .deinitializer = unitemp_DHT20_I2C_deinit, + .updater = unitemp_DHT20_I2C_update}; + +static uint8_t DHT20_get_status(I2CSensor* i2c_sensor) { + uint8_t status[1] = {0}; + unitemp_i2c_readArray(i2c_sensor, 1, status); + return status[0]; +} + +static uint8_t DHT20_calc_CRC8(uint8_t* message, uint8_t Num) { + uint8_t i; + uint8_t byte; + uint8_t crc = 0xFF; + for(byte = 0; byte < Num; byte++) { + crc ^= (message[byte]); + for(i = 8; i > 0; --i) { + if(crc & 0x80) + crc = (crc << 1) ^ 0x31; + else + crc = (crc << 1); + } + } + return crc; +} + +static void DHT20_reset_reg(I2CSensor* i2c_sensor, uint8_t addr) { + uint8_t data[3] = {addr, 0x00, 0x00}; + + unitemp_i2c_writeArray(i2c_sensor, 3, data); + + furi_delay_ms(5); + + unitemp_i2c_readArray(i2c_sensor, 3, data); + + furi_delay_ms(10); + + data[0] = 0xB0 | addr; + unitemp_i2c_writeArray(i2c_sensor, 3, data); +} + +bool unitemp_DHT20_I2C_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Адреса на шине I2C (7 бит) + i2c_sensor->minI2CAdr = 0x38 << 1; + i2c_sensor->maxI2CAdr = (sensor->type == &DHT20) ? (0x38 << 1) : (0x39 << 1); + return true; +} + +bool unitemp_DHT20_I2C_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_DHT20_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + uint8_t data[3] = {0xA8, 0x00, 0x00}; + if(!unitemp_i2c_writeArray(i2c_sensor, 3, data)) return false; + furi_delay_ms(10); + data[0] = (sensor->type == &DHT20) ? 0xBE : 0xE1; + data[1] = 0x08; + if(!unitemp_i2c_writeArray(i2c_sensor, 3, data)) return false; + furi_delay_ms(10); + + return true; +} + +bool unitemp_DHT20_I2C_deinit(Sensor* sensor) { + //Нечего деинициализировать + UNUSED(sensor); + return true; +} + +UnitempStatus unitemp_DHT20_I2C_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + if(DHT20_get_status(i2c_sensor) != 0x18) { + DHT20_reset_reg(i2c_sensor, 0x1B); + DHT20_reset_reg(i2c_sensor, 0x1C); + DHT20_reset_reg(i2c_sensor, 0x1E); + } + furi_delay_ms(10); + + uint8_t data[7] = {0xAC, 0x33, 0x00}; + if(!unitemp_i2c_writeArray(i2c_sensor, 3, data)) return UT_SENSORSTATUS_TIMEOUT; + furi_delay_ms(80); + uint32_t t = furi_get_tick(); + while(DHT20_get_status(i2c_sensor) == 0x80) { + if(furi_get_tick() - t > 10) return UT_SENSORSTATUS_TIMEOUT; + } + + if(!unitemp_i2c_readArray(i2c_sensor, 7, data)) return UT_SENSORSTATUS_TIMEOUT; + + if(DHT20_calc_CRC8(data, 6) != data[6]) { + return UT_SENSORSTATUS_BADCRC; + } + uint32_t RetuData = 0; + RetuData = (RetuData | data[1]) << 8; + RetuData = (RetuData | data[2]) << 8; + RetuData = (RetuData | data[3]); + RetuData = RetuData >> 4; + sensor->hum = RetuData * 100 * 10 / 1024.0f / 1024.0f / 10.0f; + + RetuData = 0; + RetuData = (RetuData | data[3]) << 8; + RetuData = (RetuData | data[4]) << 8; + RetuData = (RetuData | data[5]); + RetuData = RetuData & 0xfffff; + sensor->temp = (RetuData * 200 * 10.0f / 1024.0f / 1024.0f - 500) / 10.0f; + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/DHT20.h b/applications/plugins/unitemp/sensors/DHT20.h new file mode 100644 index 000000000..417b0ed1d --- /dev/null +++ b/applications/plugins/unitemp/sensors/DHT20.h @@ -0,0 +1,63 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_DHT20 +#define UNITEMP_DHT20 + +#include "../unitemp.h" +#include "../Sensors.h" +extern const SensorType DHT20; +extern const SensorType AHT10; +/** + * @brief Выделение памяти и установка начальных значений датчика DHT20 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_DHT20_I2C_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика DHT20 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_DHT20_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_DHT20_I2C_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_DHT20_I2C_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_DHT20_I2C_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/HDC1080.c b/applications/plugins/unitemp/sensors/HDC1080.c new file mode 100644 index 000000000..5f2c59b24 --- /dev/null +++ b/applications/plugins/unitemp/sensors/HDC1080.c @@ -0,0 +1,94 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "HDC1080.h" +#include "../interfaces/I2CSensor.h" + +const SensorType HDC1080 = { + .typename = "HDC1080", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM, + .pollingInterval = 250, + .allocator = unitemp_HDC1080_alloc, + .mem_releaser = unitemp_HDC1080_free, + .initializer = unitemp_HDC1080_init, + .deinitializer = unitemp_HDC1080_deinit, + .updater = unitemp_HDC1080_update}; + +bool unitemp_HDC1080_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Адреса на шине I2C (7 бит) + i2c_sensor->minI2CAdr = 0x40 << 1; + i2c_sensor->maxI2CAdr = 0x40 << 1; + return true; +} + +bool unitemp_HDC1080_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_HDC1080_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + uint8_t data[2]; + if(!unitemp_i2c_readRegArray(i2c_sensor, 0xFF, 2, data)) return UT_SENSORSTATUS_TIMEOUT; + uint16_t device_id = ((uint16_t)data[0] << 8) | data[1]; + if(device_id != 0x1050) { + FURI_LOG_E( + APP_NAME, + "Sensor %s returned wrong ID 0x%02X, expected 0x1050", + sensor->name, + device_id); + return false; + } + data[0] = 0b0001000; + data[1] = 0; + //Установка режима работы и разрядности измерений + if(!unitemp_i2c_writeRegArray(i2c_sensor, 0x02, 2, data)) return UT_SENSORSTATUS_TIMEOUT; + + return true; +} + +bool unitemp_HDC1080_deinit(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + UNUSED(i2c_sensor); + return true; +} + +UnitempStatus unitemp_HDC1080_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + uint8_t data[2] = {0}; + //Запуск измерения + if(!unitemp_i2c_writeArray(i2c_sensor, 1, data)) return UT_SENSORSTATUS_TIMEOUT; + furi_delay_ms(10); + if(!unitemp_i2c_readArray(i2c_sensor, 2, data)) return UT_SENSORSTATUS_TIMEOUT; + + sensor->temp = ((float)(((uint16_t)data[0] << 8) | data[1]) / 65536) * 165 - 40; + + data[0] = 1; + if(!unitemp_i2c_writeArray(i2c_sensor, 1, data)) return UT_SENSORSTATUS_TIMEOUT; + furi_delay_ms(10); + if(!unitemp_i2c_readArray(i2c_sensor, 2, data)) return UT_SENSORSTATUS_TIMEOUT; + sensor->hum = ((float)(((uint16_t)data[0] << 8) | data[1]) / 65536) * 100; + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/HDC1080.h b/applications/plugins/unitemp/sensors/HDC1080.h new file mode 100644 index 000000000..59ba0673c --- /dev/null +++ b/applications/plugins/unitemp/sensors/HDC1080.h @@ -0,0 +1,62 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_HDC1080 +#define UNITEMP_HDC1080 + +#include "../unitemp.h" +#include "../Sensors.h" +extern const SensorType HDC1080; +/** + * @brief Выделение памяти и установка начальных значений датчика HDC1080 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_HDC1080_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика HDC1080 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_HDC1080_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_HDC1080_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_HDC1080_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_HDC1080_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/HTU21x.c b/applications/plugins/unitemp/sensors/HTU21x.c new file mode 100644 index 000000000..2e7222bc4 --- /dev/null +++ b/applications/plugins/unitemp/sensors/HTU21x.c @@ -0,0 +1,107 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "HTU21x.h" +#include "../interfaces/I2CSensor.h" + +const SensorType HTU21x = { + .typename = "HTU21x", + .altname = "HTU21x/SI70xx/SHT2x", + .interface = &I2C, + .datatype = UT_DATA_TYPE_TEMP_HUM, + .pollingInterval = 250, + .allocator = unitemp_HTU21x_alloc, + .mem_releaser = unitemp_HTU21x_free, + .initializer = unitemp_HTU21x_init, + .deinitializer = unitemp_HTU21x_deinit, + .updater = unitemp_HTU21x_update}; + +static uint8_t checkCRC(uint16_t data) { + for(uint8_t i = 0; i < 16; i++) { + if(data & 0x8000) + data = (data << 1) ^ 0x13100; + else + data <<= 1; + } + return (data >> 8); +} + +bool unitemp_HTU21x_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Адреса на шине I2C (7 бит) + i2c_sensor->minI2CAdr = 0x40 << 1; + i2c_sensor->maxI2CAdr = 0x41 << 1; + return true; +} + +bool unitemp_HTU21x_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_HTU21x_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + UNUSED(i2c_sensor); + return true; +} + +bool unitemp_HTU21x_deinit(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + UNUSED(i2c_sensor); + return true; +} + +UnitempStatus unitemp_HTU21x_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Датчик может быть всего один, так что норм + static bool temp_hum = false; + + uint8_t data[3]; + + if(sensor->status == UT_SENSORSTATUS_POLLING) { + if(!unitemp_i2c_readArray(i2c_sensor, 3, data)) return UT_SENSORSTATUS_TIMEOUT; + + uint16_t raw = ((uint16_t)data[0] << 8) | data[1]; + if(checkCRC(raw) != data[2]) return UT_SENSORSTATUS_BADCRC; + + if(temp_hum) { + sensor->temp = (0.002681f * raw - 46.85f); + } else { + sensor->hum = ((0.001907 * (raw ^ 0x02)) - 6); + } + temp_hum = !temp_hum; + if(temp_hum) return UT_SENSORSTATUS_EARLYPOOL; + return UT_SENSORSTATUS_OK; + } + + if(temp_hum) { + //Запрос температуры + data[0] = 0xF3; + if(!unitemp_i2c_writeArray(i2c_sensor, 1, data)) return UT_SENSORSTATUS_TIMEOUT; + } else { + //Запрос влажности + data[0] = 0xF5; + if(!unitemp_i2c_writeArray(i2c_sensor, 1, data)) return UT_SENSORSTATUS_TIMEOUT; + } + + return UT_SENSORSTATUS_POLLING; +} diff --git a/applications/plugins/unitemp/sensors/HTU21x.h b/applications/plugins/unitemp/sensors/HTU21x.h new file mode 100644 index 000000000..ffe062a24 --- /dev/null +++ b/applications/plugins/unitemp/sensors/HTU21x.h @@ -0,0 +1,62 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_HTU21x +#define UNITEMP_HTU21x + +#include "../unitemp.h" +#include "../Sensors.h" +extern const SensorType HTU21x; +/** + * @brief Выделение памяти и установка начальных значений датчика HTU21x + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_HTU21x_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика HTU21x + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_HTU21x_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_HTU21x_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_HTU21x_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_HTU21x_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/LM75.c b/applications/plugins/unitemp/sensors/LM75.c index e71376404..a9c8df84e 100644 --- a/applications/plugins/unitemp/sensors/LM75.c +++ b/applications/plugins/unitemp/sensors/LM75.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/LM75.h b/applications/plugins/unitemp/sensors/LM75.h index dc1fb791c..d5397b178 100644 --- a/applications/plugins/unitemp/sensors/LM75.h +++ b/applications/plugins/unitemp/sensors/LM75.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/sensors/MAX31855.c b/applications/plugins/unitemp/sensors/MAX31855.c new file mode 100644 index 000000000..2411eb09e --- /dev/null +++ b/applications/plugins/unitemp/sensors/MAX31855.c @@ -0,0 +1,93 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "MAX31855.h" + +const SensorType MAX31855 = { + .typename = "MAX31855", + .altname = "MAX31855 (Thermocouple)", + .interface = &SPI, + .datatype = UT_TEMPERATURE, + .pollingInterval = 500, + .allocator = unitemp_MAX31855_alloc, + .mem_releaser = unitemp_MAX31855_free, + .initializer = unitemp_MAX31855_init, + .deinitializer = unitemp_MAX31855_deinit, + .updater = unitemp_MAX31855_update}; + +bool unitemp_MAX31855_alloc(Sensor* sensor, char* args) { + UNUSED(sensor); + UNUSED(args); + return true; +} + +bool unitemp_MAX31855_free(Sensor* sensor) { + UNUSED(sensor); + return true; +} + +bool unitemp_MAX31855_init(Sensor* sensor) { + SPISensor* instance = sensor->instance; + furi_hal_spi_bus_handle_init(instance->spi); + UNUSED(instance); + return true; +} + +bool unitemp_MAX31855_deinit(Sensor* sensor) { + UNUSED(sensor); + return true; +} + +UnitempStatus unitemp_MAX31855_update(Sensor* sensor) { + SPISensor* instance = sensor->instance; + + furi_hal_spi_acquire(instance->spi); + furi_hal_gpio_write(instance->CS_pin->pin, false); + + uint8_t buff[4] = {0}; + + furi_hal_spi_bus_rx(instance->spi, buff, 4, 0xFF); + furi_hal_spi_release(instance->spi); + + uint32_t raw = (buff[0] << 24) | (buff[1] << 16) | (buff[2] << 8) | buff[3]; + + if(raw == 0xFFFFFFFF || raw == 0) return UT_SENSORSTATUS_TIMEOUT; + + //Определение состояния термопары + uint8_t state = raw & 0b111; + //Обрыв + if(state == 0x01) { + UNITEMP_DEBUG("%s has thermocouple open circuit", sensor->name); + return UT_SENSORSTATUS_ERROR; + } + //Короткое замыкание к земле + if(state == 0x02) { + UNITEMP_DEBUG("%s has thermocouple short to GND", sensor->name); + return UT_SENSORSTATUS_ERROR; + } + //Короткое замыкание к питанию + if(state == 0x04) { + UNITEMP_DEBUG("%s has thermocouple short to VCC", sensor->name); + return UT_SENSORSTATUS_ERROR; + } + + raw = (raw >> 16) & 0xFFFC; + + sensor->temp = (int16_t)(raw) / 16.0f; + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/MAX31855.h b/applications/plugins/unitemp/sensors/MAX31855.h new file mode 100644 index 000000000..d63c39885 --- /dev/null +++ b/applications/plugins/unitemp/sensors/MAX31855.h @@ -0,0 +1,65 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_MAX31855 +#define UNITEMP_MAX31855 + +#include "../unitemp.h" +#include "../Sensors.h" +#include "../interfaces/SPISensor.h" + +extern const SensorType MAX31855; + +/** + * @brief Выделение памяти и установка начальных значений датчика MAX31855 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_MAX31855_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика MAX31855 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_MAX31855_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_MAX31855_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_MAX31855_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_MAX31855_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/MAX6675.c b/applications/plugins/unitemp/sensors/MAX6675.c new file mode 100644 index 000000000..e97a96eb5 --- /dev/null +++ b/applications/plugins/unitemp/sensors/MAX6675.c @@ -0,0 +1,81 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "MAX6675.h" + +const SensorType MAX6675 = { + .typename = "MAX6675", + .altname = "MAX6675 (Thermocouple)", + .interface = &SPI, + .datatype = UT_TEMPERATURE, + .pollingInterval = 500, + .allocator = unitemp_MAX6675_alloc, + .mem_releaser = unitemp_MAX6675_free, + .initializer = unitemp_MAX6675_init, + .deinitializer = unitemp_MAX6675_deinit, + .updater = unitemp_MAX6675_update}; + +bool unitemp_MAX6675_alloc(Sensor* sensor, char* args) { + UNUSED(sensor); + UNUSED(args); + return true; +} + +bool unitemp_MAX6675_free(Sensor* sensor) { + UNUSED(sensor); + return true; +} + +bool unitemp_MAX6675_init(Sensor* sensor) { + SPISensor* instance = sensor->instance; + furi_hal_spi_bus_handle_init(instance->spi); + UNUSED(instance); + return true; +} + +bool unitemp_MAX6675_deinit(Sensor* sensor) { + UNUSED(sensor); + return true; +} + +UnitempStatus unitemp_MAX6675_update(Sensor* sensor) { + SPISensor* instance = sensor->instance; + + furi_hal_spi_acquire(instance->spi); + furi_hal_gpio_write(instance->CS_pin->pin, false); + + uint8_t buff[2] = {0}; + + furi_hal_spi_bus_rx(instance->spi, buff, 2, 0xFF); + furi_hal_spi_release(instance->spi); + + uint32_t raw = (buff[0] << 8) | buff[1]; + + if(raw == 0xFFFFFFFF || raw == 0) return UT_SENSORSTATUS_TIMEOUT; + + //Определение состояния термопары + uint8_t state = raw & 0b100; + //Обрыв + if(state == 0b100) { + UNITEMP_DEBUG("%s has thermocouple open circuit", sensor->name); + return UT_SENSORSTATUS_ERROR; + } + + sensor->temp = (int16_t)(raw) / 32.0f; + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/MAX6675.h b/applications/plugins/unitemp/sensors/MAX6675.h new file mode 100644 index 000000000..cce346590 --- /dev/null +++ b/applications/plugins/unitemp/sensors/MAX6675.h @@ -0,0 +1,65 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_MAX6675 +#define UNITEMP_MAX6675 + +#include "../unitemp.h" +#include "../Sensors.h" +#include "../interfaces/SPISensor.h" + +extern const SensorType MAX6675; + +/** + * @brief Выделение памяти и установка начальных значений датчика MAX6675 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_MAX6675_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика MAX6675 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_MAX6675_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_MAX6675_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_MAX6675_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_MAX6675_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/SHT30.c b/applications/plugins/unitemp/sensors/SHT30.c new file mode 100644 index 000000000..dd43e80af --- /dev/null +++ b/applications/plugins/unitemp/sensors/SHT30.c @@ -0,0 +1,90 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#include "SHT30.h" +#include "../interfaces/I2CSensor.h" + +const SensorType SHT30 = { + .typename = "SHT30", + .altname = "SHT30/31/35", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_HUMIDITY, + .pollingInterval = 1000, + .allocator = unitemp_SHT30_I2C_alloc, + .mem_releaser = unitemp_SHT30_I2C_free, + .initializer = unitemp_SHT30_init, + .deinitializer = unitemp_SHT30_I2C_deinit, + .updater = unitemp_SHT30_I2C_update}; +const SensorType GXHT30 = { + .typename = "GXHT30", + .altname = "GXHT30/31/35", + .interface = &I2C, + .datatype = UT_TEMPERATURE | UT_HUMIDITY, + .pollingInterval = 1000, + .allocator = unitemp_SHT30_I2C_alloc, + .mem_releaser = unitemp_SHT30_I2C_free, + .initializer = unitemp_GXHT30_init, + .deinitializer = unitemp_SHT30_I2C_deinit, + .updater = unitemp_SHT30_I2C_update}; + +bool unitemp_SHT30_I2C_alloc(Sensor* sensor, char* args) { + UNUSED(args); + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + + //Адреса на шине I2C (7 бит) + i2c_sensor->minI2CAdr = 0x44 << 1; + i2c_sensor->maxI2CAdr = 0x45 << 1; + return true; +} + +bool unitemp_SHT30_I2C_free(Sensor* sensor) { + //Нечего высвобождать, так как ничего не было выделено + UNUSED(sensor); + return true; +} + +bool unitemp_SHT30_init(Sensor* sensor) { + UNUSED(sensor); + return true; +} + +bool unitemp_GXHT30_init(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + //Включение режима автоматического преобразования 2 раза в сек + uint8_t data[2] = {0x22, 0x36}; + if(!unitemp_i2c_writeArray(i2c_sensor, 2, data)) return false; + return true; +} + +bool unitemp_SHT30_I2C_deinit(Sensor* sensor) { + //Нечего деинициализировать + UNUSED(sensor); + return true; +} + +UnitempStatus unitemp_SHT30_I2C_update(Sensor* sensor) { + I2CSensor* i2c_sensor = (I2CSensor*)sensor->instance; + //Получение данных + uint8_t data[6] = {0xE0, 0x00}; + if(!unitemp_i2c_writeArray(i2c_sensor, 2, data)) return UT_SENSORSTATUS_TIMEOUT; + if(!unitemp_i2c_readArray(i2c_sensor, 6, data)) return UT_SENSORSTATUS_TIMEOUT; + + sensor->temp = -45 + 175 * (((uint16_t)(data[0] << 8) | data[1]) / 65535.0f); + sensor->hum = 100 * (((uint16_t)(data[3] << 8) | data[4]) / 65535.0f); + + return UT_SENSORSTATUS_OK; +} diff --git a/applications/plugins/unitemp/sensors/SHT30.h b/applications/plugins/unitemp/sensors/SHT30.h new file mode 100644 index 000000000..4b5b74411 --- /dev/null +++ b/applications/plugins/unitemp/sensors/SHT30.h @@ -0,0 +1,70 @@ +/* + Unitemp - Universal temperature reader + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef UNITEMP_SHT30 +#define UNITEMP_SHT30 + +#include "../unitemp.h" +#include "../Sensors.h" +extern const SensorType SHT30; +extern const SensorType GXHT30; +/** + * @brief Выделение памяти и установка начальных значений датчика SHT30 + * + * @param sensor Указатель на создаваемый датчик + * @return Истина при успехе + */ +bool unitemp_SHT30_I2C_alloc(Sensor* sensor, char* args); + +/** + * @brief Инициализации датчика SHT30 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_SHT30_init(Sensor* sensor); +/** + * @brief Инициализации датчика GXHT30 + * + * @param sensor Указатель на датчик + * @return Истина если инициализация упспешная + */ +bool unitemp_GXHT30_init(Sensor* sensor); + +/** + * @brief Деинициализация датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_SHT30_I2C_deinit(Sensor* sensor); + +/** + * @brief Обновление значений из датчика + * + * @param sensor Указатель на датчик + * @return Статус обновления + */ +UnitempStatus unitemp_SHT30_I2C_update(Sensor* sensor); + +/** + * @brief Высвободить память датчика + * + * @param sensor Указатель на датчик + */ +bool unitemp_SHT30_I2C_free(Sensor* sensor); + +#endif \ No newline at end of file diff --git a/applications/plugins/unitemp/sensors/Sensors.xlsx b/applications/plugins/unitemp/sensors/Sensors.xlsx index 2b6578e0ecc66d8bd41e911ea600061bc3c70de0..c314098a88c2bbc3d75fcc900815cfbdf5bb4772 100644 GIT binary patch delta 7810 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

QN2@O-K#I67IzWRVh+za=FGuw9YhOsO)*UyH` zm=>o?^$KbuJ5-B2H*RPqr}VWS;bz<$>-v|nkqR1_Q%E3d_UQgB8vtoP3FDq1Tjw^YT12N#W&7FK7p!y`e#Rag~WgzFH~>o@#uD zTBa8y%VZj+wj#TvNd;tw810j`MtS!^ew9jNFP#3DP9+U396UQE=MJbm z`~vL93L*VT)yi-hBxhSJ5dsr;saKE0x;7Y4jArJ+ldj}jxIr|JJOcnv65iu&7Jjy| zvAwXhML`GZUM6G2@l3wuup~;*;e8F7bZ6VbsFC#iNC9;K5Fp*xIiCYQpEgcRE{7SF zB;e8adXF|Nr}r$YS9m7`qjjI%c`su0?5jS}d=4gRZzfLmo;s&nfe-tRb^(Tc#iDk^=A6O5RKZ-oe&} zYT6zfypNkX8`y;r>DEdkXg7cO8X=4MW<@5eBPmqIm7}1yEnZvh>x5L#2R?V+G~5Bq z^nsiR!$1c|#}10*#otGhth9?6EhsO_aY27?A-ot7r+JF{6ah)gogUEKN5>0<+N!m* z;A?uhy#=dvL~Jc;J4~lYeqFYp=O0 z)3rxqxf)u8n7&i#W0h!w!vaHDODorS$B`v%&_H?pQ7BX_2q<+*UeQEGVe!N>Is&vE^t1hby1ou zx2PRU@_P3J`l+mea4}Ez&4W&Tog6={z~3C&S3ex8JUgpa4`%IfH$pU<+JB`oi}1hU zR0=NOc(Gq`=7cX6F8i#dH&S?rd#4G#Y`AetH17K4D@E(!CLzg`hn$G;1_fOTsC@}@ zI6QDJa{k(Py&SWP?1V^;VttkNj zM1Qlaf15d6nw5p@UxEVw!2MVFcZ5j*Phh1Z`~PX$zdC*7@CjB*vVWs40D$+e^zVqA z8ji*$hA>74U!kUhyRb1MOwhyEXqe#rY*=LfIEC$B;wcUR0Kon~W*v<1Gd2+fLl!tE MJM}Ai_J4%`1F&Spa{vGU delta 7063 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 Si6d$=!$i30P$;4QO#To4U`1&F diff --git a/applications/plugins/unitemp/unitemp.c b/applications/plugins/unitemp/unitemp.c index ce1cd1dc9..181de0e78 100644 --- a/applications/plugins/unitemp/unitemp.c +++ b/applications/plugins/unitemp/unitemp.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,6 @@ #include "./views/UnitempViews.h" #include -#include /* Переменные */ //Данные приложения @@ -79,9 +78,7 @@ bool unitemp_saveSettings(void) { } bool unitemp_loadSettings(void) { -#ifdef UNITEMP_DEBUG - FURI_LOG_D(APP_NAME, "Loading settings..."); -#endif + UNITEMP_DEBUG("Loading settings..."); //Выделение памяти на поток app->file_stream = file_stream_alloc(app->storage); @@ -147,7 +144,7 @@ bool unitemp_loadSettings(void) { //Сколько байт до конца строки size_t line_end = 0; - while(line_end != STRING_FAILURE && line_end != (size_t)(file_size - 1)) { + while(line_end != ((size_t)-1) && line_end != (size_t)(file_size - 1)) { char buff[20] = {0}; sscanf(((char*)(file_buf + line_end)), "%s", buff); @@ -236,6 +233,8 @@ static bool unitemp_alloc(void) { */ static void unitemp_free(void) { popup_free(app->popup); + //Удаление вида после обработки + view_dispatcher_remove_view(app->view_dispatcher, UnitempViewPopup); unitemp_widgets_free(); unitemp_SensorActions_free(); diff --git a/applications/plugins/unitemp/unitemp.h b/applications/plugins/unitemp/unitemp.h index 144780968..bde8b0c17 100644 --- a/applications/plugins/unitemp/unitemp.h +++ b/applications/plugins/unitemp/unitemp.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -39,19 +39,25 @@ /* Объявление макроподстановок */ //Имя приложения #define APP_NAME "Unitemp" +//Версия приложения +#define UNITEMP_APP_VER "1.2" //Путь хранения файлов плагина #define APP_PATH_FOLDER "/ext/unitemp" //Имя файла с настройками #define APP_FILENAME_SETTINGS "settings.cfg" //Имя файла с датчиками #define APP_FILENAME_SENSORS "sensors.cfg" -//Версия приложения -#define UNITEMP_APP_VER "1.0" //Размер буффера текста #define BUFF_SIZE 32 -#define UNITEMP_DEBUG +#define UNITEMP_D + +#ifdef FURI_DEBUG +#define UNITEMP_DEBUG(msg, ...) FURI_LOG_D(APP_NAME, msg, ##__VA_ARGS__) +#else +#define UNITEMP_DEBUG(msg, ...) +#endif /* Объявление перечислений */ //Единицы измерения температуры diff --git a/applications/plugins/unitemp/views/General_view.c b/applications/plugins/unitemp/views/General_view.c index e21b04de2..dcf8420d9 100644 --- a/applications/plugins/unitemp/views/General_view.c +++ b/applications/plugins/unitemp/views/General_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,9 @@ #include "UnitempViews.h" #include "unitemp_icons.h" -#include +extern const Icon I_ButtonRight_4x7; +extern const Icon I_ButtonLeft_4x7; +extern const Icon I_Ok_btn_9x9; static View* view; @@ -74,7 +76,7 @@ static void _draw_temperature(Canvas* canvas, Sensor* sensor, uint8_t x, uint8_t app->buff[0] = '-'; offset = 1; } - snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int8_t)sensor->temp); + snprintf((char*)(app->buff + offset), BUFF_SIZE, "%d", (int16_t)sensor->temp); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned( canvas, @@ -235,6 +237,7 @@ static void _draw_carousel_values(Canvas* canvas) { canvas_draw_icon(canvas, 34, 23, frames[furi_get_tick() % 2250 / 750]); canvas_set_font(canvas, FontSecondary); + //TODO: Оптимизировать эту срань if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SINGLE_WIRE) { snprintf( app->buff, @@ -254,6 +257,9 @@ static void _draw_carousel_values(Canvas* canvas) { if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &I2C) { snprintf(app->buff, BUFF_SIZE, "Waiting for module on I2C pins"); } + if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SPI) { + snprintf(app->buff, BUFF_SIZE, "Waiting for module on SPI pins"); + } canvas_draw_str_aligned(canvas, 64, 19, AlignCenter, AlignCenter, app->buff); return; } @@ -302,6 +308,8 @@ static void _draw_carousel_values(Canvas* canvas) { break; } } + +//TODO: Оптимизировать вывод информации static void _draw_carousel_info(Canvas* canvas) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 10, 23, "Type:"); @@ -349,6 +357,25 @@ static void _draw_carousel_info(Canvas* canvas) { ->gpio->name); } + if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &SPI) { + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 10, 35, "MISO pin:"); + canvas_draw_str(canvas, 10, 46, "CS pin:"); + canvas_draw_str(canvas, 10, 58, "SCK pin:"); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str( + canvas, 41, 23, unitemp_sensor_getActive(generalview_sensor_index)->type->typename); + canvas_draw_str(canvas, 60, 35, unitemp_gpio_getFromInt(3)->name); + canvas_draw_str( + canvas, + 47, + 46, + ((SPISensor*)unitemp_sensor_getActive(generalview_sensor_index)->instance) + ->CS_pin->name); + canvas_draw_str(canvas, 54, 58, unitemp_gpio_getFromInt(5)->name); + } + if(unitemp_sensor_getActive(generalview_sensor_index)->type->interface == &I2C) { canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 10, 35, "I2C addr:"); @@ -362,7 +389,8 @@ static void _draw_carousel_info(Canvas* canvas) { BUFF_SIZE, "0x%02X", ((I2CSensor*)unitemp_sensor_getActive(generalview_sensor_index)->instance) - ->currentI2CAdr); + ->currentI2CAdr >> + 1); canvas_draw_str(canvas, 57, 35, app->buff); canvas_draw_str(canvas, 54, 46, "15 (C0)"); canvas_draw_str(canvas, 54, 58, "16 (C1)"); @@ -539,6 +567,10 @@ static bool _input_callback(InputEvent* event, void* context) { return true; } } + //Обработка длинного нажатия "Ок" + if(event->key == InputKeyOk && event->type == InputTypeLong) { + app->settings.temp_unit = !app->settings.temp_unit; + } return true; } @@ -558,5 +590,6 @@ void unitemp_General_switch(void) { } void unitemp_General_free(void) { + view_dispatcher_remove_view(app->view_dispatcher, UnitempViewGeneral); view_free(view); } diff --git a/applications/plugins/unitemp/views/MainMenu_view.c b/applications/plugins/unitemp/views/MainMenu_view.c index 4b2820eee..bfd0cb0cf 100644 --- a/applications/plugins/unitemp/views/MainMenu_view.c +++ b/applications/plugins/unitemp/views/MainMenu_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/views/Popup_view.c b/applications/plugins/unitemp/views/Popup_view.c index d495462b2..efa7e7ffa 100644 --- a/applications/plugins/unitemp/views/Popup_view.c +++ b/applications/plugins/unitemp/views/Popup_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,6 @@ #include "UnitempViews.h" #include #include -#include uint32_t _prev_view_id; diff --git a/applications/plugins/unitemp/views/SensorActions_view.c b/applications/plugins/unitemp/views/SensorActions_view.c index 6f375a50a..1d1a83076 100644 --- a/applications/plugins/unitemp/views/SensorActions_view.c +++ b/applications/plugins/unitemp/views/SensorActions_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/views/SensorEdit_view.c b/applications/plugins/unitemp/views/SensorEdit_view.c index 4d5bc17db..ccb07a48e 100644 --- a/applications/plugins/unitemp/views/SensorEdit_view.c +++ b/applications/plugins/unitemp/views/SensorEdit_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -63,13 +63,9 @@ bool _onewire_id_exist(uint8_t* id) { static void _onewire_scan(void) { OneWireSensor* ow_sensor = editable_sensor->instance; -#ifdef UNITEMP_DEBUG - FURI_LOG_D( - APP_NAME, - "devices on wire %d: %d", - ow_sensor->bus->gpio->num, - ow_sensor->bus->device_count); -#endif + + UNITEMP_DEBUG( + "devices on wire %d: %d", ow_sensor->bus->gpio->num, ow_sensor->bus->device_count); //Сканирование шины one wire unitemp_onewire_bus_init(ow_sensor->bus); @@ -101,9 +97,8 @@ static void _onewire_scan(void) { memcpy(ow_sensor->deviceID, id, 8); ow_sensor->familyCode = id[0]; -#ifdef UNITEMP_DEBUG - FURI_LOG_D( - APP_NAME, + + UNITEMP_DEBUG( "Found sensor's ID: %02X%02X%02X%02X%02X%02X%02X%02X", id[0], id[1], @@ -113,7 +108,6 @@ static void _onewire_scan(void) { id[5], id[6], id[7]); -#endif if(ow_sensor->familyCode != 0) { char id_buff[10]; @@ -199,6 +193,12 @@ static void _gpio_change_callback(VariableItem* item) { unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio); variable_item_set_current_value_text(item, instance->gpio->name); } + if(editable_sensor->type->interface == &SPI) { + SPISensor* instance = editable_sensor->instance; + instance->CS_pin = + unitemp_gpio_getAviablePort(editable_sensor->type->interface, index, initial_gpio); + variable_item_set_current_value_text(item, instance->CS_pin->name); + } if(editable_sensor->type->interface == &ONE_WIRE) { OneWireSensor* instance = editable_sensor->instance; instance->bus->gpio = @@ -214,9 +214,9 @@ static void _gpio_change_callback(VariableItem* item) { static void _i2caddr_change_callback(VariableItem* item) { uint8_t index = variable_item_get_current_value_index(item); ((I2CSensor*)editable_sensor->instance)->currentI2CAdr = - ((I2CSensor*)editable_sensor->instance)->minI2CAdr + index; + ((I2CSensor*)editable_sensor->instance)->minI2CAdr + index * 2; char buff[5]; - snprintf(buff, 5, "0x%2X", ((I2CSensor*)editable_sensor->instance)->currentI2CAdr); + snprintf(buff, 5, "0x%2X", ((I2CSensor*)editable_sensor->instance)->currentI2CAdr >> 1); variable_item_set_current_value_text(item, buff); } /** @@ -302,12 +302,15 @@ void unitemp_SensorEdit_switch(Sensor* sensor) { offset_buff, OFFSET_BUFF_SIZE, "%+1.1f", (double)(editable_sensor->temp_offset / 10.0)); variable_item_set_current_value_text(temp_offset_item, offset_buff); - //Порт подключения датчка (для one wire и single wire) - if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE) { + //Порт подключения датчка (для one wire, SPI и single wire) + if(sensor->type->interface == &ONE_WIRE || sensor->type->interface == &SINGLE_WIRE || + sensor->type->interface == &SPI) { if(sensor->type->interface == &ONE_WIRE) { initial_gpio = ((OneWireSensor*)editable_sensor->instance)->bus->gpio; - } else { + } else if(sensor->type->interface == &SINGLE_WIRE) { initial_gpio = ((SingleWireSensor*)editable_sensor->instance)->gpio; + } else if(sensor->type->interface == &SPI) { + initial_gpio = ((SPISensor*)editable_sensor->instance)->CS_pin; } uint8_t aviable_gpio_count = @@ -335,11 +338,15 @@ void unitemp_SensorEdit_switch(Sensor* sensor) { VariableItem* item = variable_item_list_add( variable_item_list, "I2C address", - ((I2CSensor*)sensor->instance)->maxI2CAdr - ((I2CSensor*)sensor->instance)->minI2CAdr + - 1, + (((I2CSensor*)sensor->instance)->maxI2CAdr >> 1) - + (((I2CSensor*)sensor->instance)->minI2CAdr >> 1) + 1, _i2caddr_change_callback, app); - snprintf(app->buff, 5, "0x%2X", ((I2CSensor*)sensor->instance)->currentI2CAdr); + snprintf(app->buff, 5, "0x%2X", ((I2CSensor*)sensor->instance)->currentI2CAdr >> 1); + variable_item_set_current_value_index( + item, + (((I2CSensor*)sensor->instance)->currentI2CAdr >> 1) - + (((I2CSensor*)sensor->instance)->minI2CAdr >> 1)); variable_item_set_current_value_text(item, app->buff); } diff --git a/applications/plugins/unitemp/views/SensorNameEdit_view.c b/applications/plugins/unitemp/views/SensorNameEdit_view.c index 1d36ac142..19df76edf 100644 --- a/applications/plugins/unitemp/views/SensorNameEdit_view.c +++ b/applications/plugins/unitemp/views/SensorNameEdit_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -42,5 +42,6 @@ void unitemp_SensorNameEdit_switch(Sensor* sensor) { view_dispatcher_switch_to_view(app->view_dispatcher, VIEW_ID); } void unitemp_SensorNameEdit_free(void) { + view_dispatcher_remove_view(app->view_dispatcher, VIEW_ID); text_input_free(text_input); } \ No newline at end of file diff --git a/applications/plugins/unitemp/views/SensorsList_view.c b/applications/plugins/unitemp/views/SensorsList_view.c index a7d3d5556..716ec260b 100644 --- a/applications/plugins/unitemp/views/SensorsList_view.c +++ b/applications/plugins/unitemp/views/SensorsList_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,8 @@ #include "UnitempViews.h" #include #include -#include + +extern const Icon I_Cry_dolph_55x52; //Текущий вид static View* view; @@ -85,8 +86,8 @@ static void _enter_callback(void* context, uint32_t index) { return; } - //Выбор первого доступного порта для датчика single wire - if(type->interface == &SINGLE_WIRE) { + //Выбор первого доступного порта для датчика single wire и SPI + if(type->interface == &SINGLE_WIRE || type->interface == &SPI) { snprintf( args, 4, diff --git a/applications/plugins/unitemp/views/Settings_view.c b/applications/plugins/unitemp/views/Settings_view.c index c3f82c14e..bff169129 100644 --- a/applications/plugins/unitemp/views/Settings_view.c +++ b/applications/plugins/unitemp/views/Settings_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/views/UnitempViews.h b/applications/plugins/unitemp/views/UnitempViews.h index b7c2467e0..b78cf2b28 100644 --- a/applications/plugins/unitemp/views/UnitempViews.h +++ b/applications/plugins/unitemp/views/UnitempViews.h @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by diff --git a/applications/plugins/unitemp/views/Widgets_view.c b/applications/plugins/unitemp/views/Widgets_view.c index 6d8702ca1..97505d925 100644 --- a/applications/plugins/unitemp/views/Widgets_view.c +++ b/applications/plugins/unitemp/views/Widgets_view.c @@ -1,6 +1,6 @@ /* Unitemp - Universal temperature reader - Copyright (C) 2022 Victor Nikitchuk (https://github.com/quen0n) + Copyright (C) 2022-2023 Victor Nikitchuk (https://github.com/quen0n) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,7 +18,7 @@ #include "UnitempViews.h" #include "unitemp_icons.h" -#include +extern const Icon I_DolphinCommon_56x48; void unitemp_widgets_alloc(void) { app->widget = widget_alloc(); @@ -27,6 +27,7 @@ void unitemp_widgets_alloc(void) { } void unitemp_widgets_free(void) { + view_dispatcher_remove_view(app->view_dispatcher, UnitempViewWidget); widget_free(app->widget); } @@ -132,7 +133,7 @@ void unitemp_widget_delete_switch(Sensor* sensor) { app->buff, BUFF_SIZE, "\e#I2C addr:\e# 0x%02X", - ((I2CSensor*)current_sensor->instance)->currentI2CAdr); + ((I2CSensor*)current_sensor->instance)->currentI2CAdr >> 1); widget_add_text_box_element( app->widget, 0, 28, 128, 23, AlignLeft, AlignTop, app->buff, false); } From 66eb191a599687606bc67758a85d8410163c87ee Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Mon, 30 Jan 2023 23:29:08 +0100 Subject: [PATCH 059/231] Fix build | update storage_settings_scene_sd_info.c --- .../scenes/storage_settings_scene_sd_info.c | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index f900d5c8c..f69f6c7ef 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -28,33 +28,6 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { - char unit_kb[] = "KB"; - char unit_mb[] = "MB"; - char unit_gb[] = "GB"; - - double sd_total_val = (double)sd_info.kb_total; - char* sd_total_unit = unit_kb; - double sd_free_val = (double)sd_info.kb_free; - char* sd_free_unit = unit_kb; - - if(sd_total_val > 1024) { - sd_total_val /= 1024; - sd_total_unit = unit_mb; - } - if(sd_total_val > 1024) { - sd_total_val /= 1024; - sd_total_unit = unit_gb; - } - - if(sd_free_val > 1024) { - sd_free_val /= 1024; - sd_free_unit = unit_mb; - } - if(sd_free_val > 1024) { - sd_free_val /= 1024; - sd_free_unit = unit_gb; - } - furi_string_printf( app->text_string, "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" @@ -109,4 +82,4 @@ void storage_settings_scene_sd_info_on_exit(void* context) { dialog_ex_reset(dialog_ex); furi_string_reset(app->text_string); -} +} \ No newline at end of file From 2354b51d30d11a8877a58682fe3975194c2161c8 Mon Sep 17 00:00:00 2001 From: Clara K Date: Tue, 31 Jan 2023 11:41:42 +0100 Subject: [PATCH 060/231] Update ReadMe.md --- ReadMe.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index 58123126d..bb167c543 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -4,7 +4,7 @@

-[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme#Known-bugs) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) +[Intro](https://github.com/ClaraCrazy/Flipper-Xtreme#What-makes-it-special) | [Animations](https://github.com/ClaraCrazy/Flipper-Xtreme#Animations--Asset-Packs) | [Docs](https://github.com/ClaraCrazy/Flipper-Xtreme/wiki) | [Changelog](https://github.com/ClaraCrazy/Flipper-Xtreme#list-of-changes) | [Known bugs](https://github.com/ClaraCrazy/Flipper-Xtreme/issues?q=is%3Aissue+is%3Aopen+label%3Arelease-pending) | [Install](https://github.com/ClaraCrazy/Flipper-Xtreme#Install) | [Build](https://github.com/ClaraCrazy/Flipper-Xtreme#build-it-yourself) | [Discord](https://discord.gg/flipper-xtreme) ----- This firmware is a complete overhaul of the [Official Firmware](https://github.com/flipperdevices/flipperzero-firmware), it also features lots of awesome code-bits from [Unleashed](https://github.com/DarkFlippers/unleashed-firmware). @@ -147,14 +147,6 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - Tons of unused code from FAPs and system calls ``` ----- -
-

Known Bugs:

- -```txt -- Nothing rn. Hopefully that wont change -``` - ----

Install:

From 36eace99966994fe0ef97ee3c391c5ec4060014b Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 31 Jan 2023 16:48:15 +0100 Subject: [PATCH 061/231] Update infrared assets --- assets/resources/infrared/assets/ac.ir | 2 +- assets/resources/infrared/assets/audio.ir | 34 +++++++++- assets/resources/infrared/assets/fans.ir | 64 ++++++++++++++++++- .../resources/infrared/assets/projectors.ir | 28 +++++++- assets/resources/infrared/assets/tv.ir | 22 ++++++- 5 files changed, 141 insertions(+), 9 deletions(-) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 20a72e0ca..fce68812f 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 # Last Updated 13th Jan, 2023 -# Last Checked 13th Jan, 2023 +# Last Checked 31th Jan, 2023 # name: POWER type: raw diff --git a/assets/resources/infrared/assets/audio.ir b/assets/resources/infrared/assets/audio.ir index 5a0b18658..ae29a43f0 100644 --- a/assets/resources/infrared/assets/audio.ir +++ b/assets/resources/infrared/assets/audio.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 13th Jan, 2023 -# Last Checked 13th Jan, 2023 +# Last Updated 30th Jan, 2023 +# Last Checked 31th Jan, 2023 # name: POWER type: parsed @@ -2002,3 +2002,33 @@ type: parsed protocol: NEC address: 20 00 00 00 command: 50 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 0C 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 47 00 00 00 +# +name: POWER +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 02 00 00 00 +# +name: VOL- +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 05 00 00 00 +# +name: MUTE +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 04 00 00 00 diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index b2c683cba..fd28c501f 100644 --- a/assets/resources/infrared/assets/fans.ir +++ b/assets/resources/infrared/assets/fans.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 13th Jan, 2023 -# Last Checked 13th Jan, 2023 +# Last Updated 30th Jan, 2023 +# Last Checked 31th Jan, 2023 # name: POWER type: raw @@ -68,6 +68,12 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1294 371 1351 322 443 1200 1295 346 1297 373 444 1224 445 1198 1300 345 498 1170 473 1171 496 1173 471 8057 1295 372 1297 347 444 1224 1296 347 1296 349 494 1172 470 1174 1293 376 469 1174 469 1200 468 1175 468 8082 1293 351 1318 349 468 1175 1292 377 1294 349 468 1177 491 1175 1293 350 442 1226 468 1175 468 1201 467 8083 1293 350 1268 401 467 1175 1293 351 1318 350 467 1175 442 1226 1293 350 467 1177 466 1200 468 1176 441 8133 1292 351 1267 400 468 1175 1292 351 1292 376 466 1177 467 1202 1291 352 442 1202 465 1203 441 1201 442 8132 1267 376 1267 402 440 1202 1266 377 1290 379 439 1226 416 1253 1241 402 415 1228 439 1229 414 1228 415 8161 1239 403 1240 404 439 1229 1240 403 1240 429 414 1229 414 1231 1264 404 413 1229 414 1255 413 1229 413 8161 1238 404 1239 405 437 1230 1239 404 1238 430 412 1230 412 1232 1262 405 412 1231 412 1256 412 1231 412 8162 1238 406 1237 406 411 1258 1237 406 1237 431 412 1256 387 1256 1213 456 386 1256 386 1257 412 1256 386 8164 1237 431 1212 431 386 1283 1212 431 1212 432 411 1257 386 1257 1211 457 386 1257 386 1258 411 1258 385 8164 1212 431 1212 432 411 1256 1213 431 1236 433 386 1256 387 1257 1238 430 386 1257 386 1282 386 1256 386 +# ON/SPEED+ +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1330 364 1385 320 523 1158 1332 365 1329 364 479 1213 481 1213 481 1213 481 1213 481 1213 1332 390 480 8072 1358 362 1329 364 478 1216 1328 366 1328 367 476 1218 476 1218 476 1218 476 1218 476 1218 1328 367 476 8103 1328 367 1327 367 476 1218 1327 367 1327 367 476 1218 476 1218 476 1218 476 1218 476 1218 1327 367 476 8103 1327 367 1327 367 476 1218 1327 368 1326 367 476 1218 476 1219 475 1219 475 1219 475 1219 1326 368 475 8104 1327 368 1326 368 475 1219 1327 368 1326 368 475 1219 475 1219 475 1219 475 1219 475 1219 1326 368 475 8105 1326 368 1326 368 475 1219 1327 368 1326 368 475 1220 474 1220 474 1220 475 1220 474 1220 1325 368 475 8105 1326 369 1325 369 474 1220 1326 369 1325 369 474 1220 474 1220 474 1220 474 1220 474 1220 1326 369 474 8106 1325 369 1325 369 474 1220 1325 369 1326 369 474 1220 474 1220 474 1220 474 1220 474 1221 1324 370 473 8132 1273 422 1272 422 446 1248 1272 422 1297 397 421 1275 419 1300 419 1276 419 1275 419 1250 1297 398 445 8135 1297 397 1297 397 447 1247 1298 397 1297 397 447 1247 447 1247 447 1247 447 1247 447 1247 1298 397 447 8134 1298 397 1298 397 447 1248 1297 397 1297 397 447 1248 446 1248 446 1248 447 1248 447 1248 1297 397 447 8133 1298 397 1297 397 447 1248 1297 397 1297 397 447 1248 446 1248 447 1248 446 1248 446 1248 1297 398 446 8134 1297 398 1296 398 446 1248 1297 398 1296 398 446 1248 446 1248 446 1248 446 1248 446 1248 1296 398 446 8134 1295 398 1296 398 446 1248 1296 398 1296 398 446 1249 445 1249 445 1249 445 1249 445 1249 1295 399 445 8134 1295 399 1295 399 445 1249 1295 399 1295 399 445 1249 445 1249 445 1250 444 1249 445 1249 1295 399 445 8135 1295 400 1294 400 444 1250 1294 400 1295 400 444 1250 444 1250 444 1250 444 1250 444 1250 1294 401 443 8137 1293 401 1294 401 442 1251 1294 401 1293 401 442 1253 442 1252 442 1252 442 1252 442 1252 1293 427 417 8140 1292 426 1268 427 417 1277 1243 452 1267 427 417 1278 417 1277 417 1278 417 1277 417 1277 1268 427 417 8163 1242 452 1242 452 417 1278 1242 453 1241 453 391 1303 391 1303 392 1303 391 1303 391 1303 1242 453 415 8165 1241 453 1241 453 391 1304 1241 453 1241 454 390 1305 389 1304 390 1304 390 1305 389 1305 1240 480 364 8216 1214 480 1214 480 363 1331 1214 480 1214 480 364 1331 363 1331 363 1331 363 1331 363 1331 1214 481 363 8218 1213 481 1213 481 363 1333 1212 508 1186 508 335 1359 335 1359 335 1359 335 1359 335 1360 1186 508 335 8247 1184 509 1185 535 307 1387 1159 536 1158 589 253 1389 306 1415 278 1416 252 1442 199 1575 1052 # name: POWER type: raw @@ -1287,3 +1293,57 @@ type: parsed protocol: NEC address: 01 00 00 00 command: 8B 00 00 00 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2256 695 788 1354 789 1349 789 1340 792 702 762 697 763 692 789 661 788 720 786 693 786 689 785 685 784 681 784 676 784 1334 784 1330 783 102265 2255 695 787 1356 786 1352 785 1348 785 681 783 676 783 671 784 666 784 724 784 696 783 691 784 686 784 681 783 676 784 1335 783 1330 783 +# OSC +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2227 749 733 1382 761 1379 866 1292 841 566 898 591 868 588 866 582 760 1412 867 612 866 576 898 603 867 598 866 1256 759 695 867 1246 759 101611 2335 615 868 1245 899 1268 869 1266 867 566 898 591 760 694 761 689 760 1411 760 720 760 715 759 710 759 705 759 1363 760 696 758 1352 761 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2224 708 755 1419 757 1443 756 685 756 1470 757 709 758 710 784 657 836 708 756 763 785 1468 782 1444 755 737 756 712 754 1471 781 1419 780 101298 2250 656 778 1420 809 1391 753 686 755 1472 779 687 779 687 752 688 831 714 778 741 750 1502 776 1422 752 740 752 741 751 1448 751 1475 749 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2203 675 786 1388 867 1361 786 678 786 1444 758 708 759 707 760 707 786 735 757 760 786 735 757 736 781 711 757 734 759 733 785 734 758 101185 2198 708 757 1416 757 1442 756 685 755 1445 780 685 780 662 776 689 803 742 778 740 753 766 779 715 777 714 779 714 778 690 776 742 752 +# StrengthUp +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2224 685 782 1419 808 1418 779 687 757 1470 757 710 757 684 809 657 809 736 758 1469 758 736 756 1470 757 762 755 1446 782 1418 781 712 755 101352 2223 707 758 1417 834 1392 781 685 781 1446 780 687 754 687 754 713 804 741 779 1447 779 715 778 1447 779 740 752 1448 778 1422 779 690 775 +# StrengthDown +name: SPEED- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2222 685 781 1419 808 1419 780 686 755 1447 781 684 783 686 780 686 807 1470 757 1470 756 1471 756 1497 756 1445 780 1447 780 1421 779 1421 754 101509 2250 684 777 1421 781 1444 754 687 754 1472 779 687 779 688 753 688 830 1448 777 1449 778 1448 752 1501 752 1450 773 1451 778 1422 778 1423 777 +# +name: POWER +type: parsed +protocol: NECext +address: 41 59 00 00 +command: 05 FA 00 00 +# +name: SPEED+ +type: parsed +protocol: NECext +address: 41 59 00 00 +command: 44 BB 00 00 +# OFF +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 1332 391 1303 359 485 1241 1304 391 1303 391 452 1242 452 1242 452 1243 451 1243 452 1243 452 1242 1303 7275 1303 391 1303 390 453 1240 1330 364 1328 366 477 1218 476 1218 476 1220 474 1220 474 1220 474 1220 1325 7252 1326 369 1325 369 475 1219 1325 369 1325 369 475 1219 475 1219 475 1219 475 1219 475 1219 475 1219 1326 7254 1326 369 1326 369 475 1219 1326 369 1326 369 475 1219 475 1220 475 1219 475 1219 475 1220 475 1220 1325 7256 1325 370 1325 370 474 1220 1325 370 1325 370 474 1220 475 1220 474 1220 474 1220 474 1221 474 1221 1324 7281 1301 370 1325 370 474 1220 1325 370 1324 370 474 1221 474 1221 473 1221 473 1221 473 1221 474 1220 1325 7256 1325 370 1324 371 473 1221 1324 370 1324 370 474 1221 473 1221 473 1220 474 1220 474 1220 474 1220 1324 7256 1324 371 1323 371 473 1221 1323 371 1323 371 473 1221 473 1221 473 1221 473 1221 473 1221 473 1221 1323 7256 1323 371 1323 371 473 1222 1322 372 1322 372 472 1222 472 1222 472 1222 472 1222 472 1222 472 1222 1322 7281 1297 372 1322 373 471 1246 1298 396 1298 397 447 1247 447 1247 447 1247 447 1247 447 1247 447 1247 1297 7281 1297 397 1297 397 447 1247 1297 397 1297 397 447 1247 447 1247 447 1247 447 1247 447 1248 446 1248 1296 7282 1296 398 1296 398 446 1248 1296 398 1296 398 447 1248 446 1248 446 1248 446 1247 447 1247 447 1248 1295 7281 1296 398 1296 398 446 1248 1296 399 1295 399 446 1248 446 1248 446 1248 446 1248 446 1248 446 1248 1296 7282 1296 399 1295 399 445 1248 1295 399 1295 399 445 1248 446 1249 444 1249 445 1249 445 1249 445 1249 1294 7282 1294 399 1295 400 444 1249 1295 399 1294 400 444 1250 444 1249 445 1250 444 1249 445 1250 444 1250 1294 7282 1294 400 1294 400 444 1250 1294 400 1294 401 443 1251 443 1250 444 1250 444 1250 444 1250 443 1251 1292 7284 1292 401 1293 402 442 1251 1292 402 1291 403 442 1276 418 1252 442 1276 418 1277 417 1277 417 1277 1242 7334 1268 427 1242 452 392 1302 1242 452 1242 452 392 1302 392 1302 392 1302 392 1302 391 1302 392 1302 1242 7335 1241 453 1240 453 391 1302 1241 453 1240 453 391 1303 390 1303 390 1303 390 1304 390 1303 391 1304 1240 7361 1216 479 1216 479 364 1330 1215 479 1215 480 363 1330 364 1330 364 1330 364 1330 364 1330 364 1330 1214 7362 1213 480 1214 480 363 1331 1213 481 1213 482 361 1332 362 1332 362 1331 363 1331 362 1332 362 1332 1212 7391 1185 508 1185 509 334 1359 1185 509 1184 509 334 1359 335 1359 334 1386 307 1386 307 1386 307 1386 1159 7445 1131 562 1131 616 196 1471 1104 diff --git a/assets/resources/infrared/assets/projectors.ir b/assets/resources/infrared/assets/projectors.ir index db90997a7..9f1d8a0a0 100644 --- a/assets/resources/infrared/assets/projectors.ir +++ b/assets/resources/infrared/assets/projectors.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 13th Jan, 2023 -# Last Checked 13th Jan, 2023 +# Last Updated 31th Jan, 2023 +# Last Checked 31th Jan, 2023 # # ON name: POWER @@ -778,3 +778,27 @@ type: parsed protocol: NEC address: 31 00 00 00 command: 81 00 00 00 +# +name: POWER +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: VOL+ +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 +# +name: VOL- +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 +# +name: MUTE +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 diff --git a/assets/resources/infrared/assets/tv.ir b/assets/resources/infrared/assets/tv.ir index 6126a6239..a7a8c306a 100644 --- a/assets/resources/infrared/assets/tv.ir +++ b/assets/resources/infrared/assets/tv.ir @@ -1,7 +1,7 @@ Filetype: IR library file Version: 1 -# Last Updated 13th Jan, 2023 -# Last Checked 13th Jan, 2023 +# Last Updated 19th Jan, 2023 +# Last Checked 31th Jan, 2023 # name: POWER type: parsed @@ -1821,3 +1821,21 @@ type: parsed protocol: NEC address: 28 00 00 00 command: 10 00 00 00 +# +name: CH+ +type: parsed +protocol: RC5 +address: 01 00 00 00 +command: 14 00 00 00 +# +name: CH+ +type: parsed +protocol: SIRC20 +address: 5A 0E 00 00 +command: 10 00 00 00 +# +name: CH- +type: parsed +protocol: SIRC20 +address: 5A 0E 00 00 +command: 11 00 00 00 From bbba74b79b3d956d8030b7e1e5aa84dc6bfeda70 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 31 Jan 2023 19:07:05 +0100 Subject: [PATCH 062/231] Update IR again, to prove a point --- assets/resources/infrared/assets/fans.ir | 46 ++++++++++++++++-------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/assets/resources/infrared/assets/fans.ir b/assets/resources/infrared/assets/fans.ir index fd28c501f..942c2df13 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 30th Jan, 2023 +# Last Updated 31th Jan, 2023 # Last Checked 31th Jan, 2023 # name: POWER @@ -1221,19 +1221,7 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1349 364 1321 363 490 1219 1324 364 1294 389 463 1245 1324 363 462 1219 491 1221 462 1223 461 1226 484 8019 1292 393 1292 420 433 1252 1292 393 1292 420 433 1252 1292 393 460 1252 433 1252 460 1226 459 1252 433 8021 1317 393 1292 394 459 1226 1318 393 1292 394 458 1226 1318 394 432 1253 459 1226 459 1253 432 1253 459 -# -name: TEMP+ -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1355 364 1348 363 462 1189 1355 363 1322 389 436 1245 492 1192 1351 389 436 1221 490 1197 487 1249 434 8019 1319 393 1292 393 459 1252 1292 393 1292 393 459 1252 432 1252 1292 393 459 1252 433 1252 433 1252 460 8020 1291 393 1292 420 432 1252 1292 393 1292 420 432 1253 459 1226 1291 420 432 1253 459 1226 459 1252 432 -# -name: TEMP- -type: raw -frequency: 38000 -duty_cycle: 0.330000 -data: 1354 363 1322 363 489 1219 1325 364 1295 389 463 1245 464 1219 466 1219 1324 416 435 1223 461 1249 461 8019 1292 393 1318 393 432 1252 1292 393 1318 393 433 1252 460 1225 460 1252 1292 393 460 1225 459 1252 433 8020 1318 393 1292 393 459 1253 1291 393 1291 394 459 1253 431 1253 432 1253 1291 420 432 1253 432 1253 458 -# +# name: TIMER type: raw frequency: 38000 @@ -1347,3 +1335,33 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 1332 391 1303 359 485 1241 1304 391 1303 391 452 1242 452 1242 452 1243 451 1243 452 1243 452 1242 1303 7275 1303 391 1303 390 453 1240 1330 364 1328 366 477 1218 476 1218 476 1220 474 1220 474 1220 474 1220 1325 7252 1326 369 1325 369 475 1219 1325 369 1325 369 475 1219 475 1219 475 1219 475 1219 475 1219 475 1219 1326 7254 1326 369 1326 369 475 1219 1326 369 1326 369 475 1219 475 1220 475 1219 475 1219 475 1220 475 1220 1325 7256 1325 370 1325 370 474 1220 1325 370 1325 370 474 1220 475 1220 474 1220 474 1220 474 1221 474 1221 1324 7281 1301 370 1325 370 474 1220 1325 370 1324 370 474 1221 474 1221 473 1221 473 1221 473 1221 474 1220 1325 7256 1325 370 1324 371 473 1221 1324 370 1324 370 474 1221 473 1221 473 1220 474 1220 474 1220 474 1220 1324 7256 1324 371 1323 371 473 1221 1323 371 1323 371 473 1221 473 1221 473 1221 473 1221 473 1221 473 1221 1323 7256 1323 371 1323 371 473 1222 1322 372 1322 372 472 1222 472 1222 472 1222 472 1222 472 1222 472 1222 1322 7281 1297 372 1322 373 471 1246 1298 396 1298 397 447 1247 447 1247 447 1247 447 1247 447 1247 447 1247 1297 7281 1297 397 1297 397 447 1247 1297 397 1297 397 447 1247 447 1247 447 1247 447 1247 447 1248 446 1248 1296 7282 1296 398 1296 398 446 1248 1296 398 1296 398 447 1248 446 1248 446 1248 446 1247 447 1247 447 1248 1295 7281 1296 398 1296 398 446 1248 1296 399 1295 399 446 1248 446 1248 446 1248 446 1248 446 1248 446 1248 1296 7282 1296 399 1295 399 445 1248 1295 399 1295 399 445 1248 446 1249 444 1249 445 1249 445 1249 445 1249 1294 7282 1294 399 1295 400 444 1249 1295 399 1294 400 444 1250 444 1249 445 1250 444 1249 445 1250 444 1250 1294 7282 1294 400 1294 400 444 1250 1294 400 1294 401 443 1251 443 1250 444 1250 444 1250 444 1250 443 1251 1292 7284 1292 401 1293 402 442 1251 1292 402 1291 403 442 1276 418 1252 442 1276 418 1277 417 1277 417 1277 1242 7334 1268 427 1242 452 392 1302 1242 452 1242 452 392 1302 392 1302 392 1302 392 1302 391 1302 392 1302 1242 7335 1241 453 1240 453 391 1302 1241 453 1240 453 391 1303 390 1303 390 1303 390 1304 390 1303 391 1304 1240 7361 1216 479 1216 479 364 1330 1215 479 1215 480 363 1330 364 1330 364 1330 364 1330 364 1330 364 1330 1214 7362 1213 480 1214 480 363 1331 1213 481 1213 482 361 1332 362 1332 362 1331 363 1331 362 1332 362 1332 1212 7391 1185 508 1185 509 334 1359 1185 509 1184 509 334 1359 335 1359 334 1386 307 1386 307 1386 307 1386 1159 7445 1131 562 1131 616 196 1471 1104 +# +name: POWER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2265 691 792 690 792 684 793 1342 821 1310 821 640 822 634 822 660 792 718 791 690 791 685 791 680 791 675 791 670 790 1330 789 662 788 99517 2230 723 760 721 760 715 761 1374 761 1368 762 699 762 694 762 689 762 747 761 719 761 715 761 710 761 705 761 701 760 1360 760 691 760 +# +name: SPEED+ +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2312 617 867 641 840 577 899 1269 866 1265 865 622 839 593 864 612 838 670 839 1306 839 611 865 1271 864 627 839 1259 865 590 866 611 840 102129 2316 670 813 667 813 664 811 1323 810 1319 759 702 759 697 759 692 759 750 759 1386 758 717 759 1376 758 706 759 1365 759 696 759 691 760 +# +name: SPEED- +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2232 725 759 722 759 717 759 1375 760 1370 760 702 759 697 759 692 788 1384 788 1356 788 1351 788 1347 787 1342 787 1338 786 670 785 1330 784 99591 2229 724 760 721 785 690 785 1349 784 1345 784 677 783 673 782 669 781 1391 781 1363 781 1359 780 1353 781 1349 780 1343 781 675 780 1334 780 +# +name: TIMER +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2230 724 760 720 761 715 761 1374 761 1370 760 701 760 696 760 691 786 1385 760 720 787 689 760 710 760 705 786 1339 785 670 785 665 784 98757 2224 729 754 726 755 721 754 1380 754 1375 754 706 754 701 754 696 754 1418 754 726 754 720 755 716 755 710 755 1369 755 700 755 696 755 +# +name: ROTATE +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 2256 698 786 695 786 689 787 1348 787 1342 787 673 788 668 787 663 787 722 786 696 784 1353 786 1375 759 706 759 702 783 672 783 1332 782 102265 2310 645 838 668 812 664 811 1323 810 1319 810 651 809 647 808 642 808 701 807 673 807 1332 807 1327 807 658 808 653 807 648 807 1307 807 \ No newline at end of file From 3efa6a195aef0ca4214172242c0056971d4c368e Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 31 Jan 2023 21:24:46 +0100 Subject: [PATCH 063/231] Delete orgasmotron.fap --- applications/plugins/orgasmotron/orgasmotron.fap | Bin 2312 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 applications/plugins/orgasmotron/orgasmotron.fap diff --git a/applications/plugins/orgasmotron/orgasmotron.fap b/applications/plugins/orgasmotron/orgasmotron.fap deleted file mode 100644 index 23d7bebb334321ec1f7f5edd4869374a887158da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2312 zcmbVNO>Epm6n^$@+9fS?Q=p`j2JezUMAX%8Xi?QugxYadPDoUhDhH6`?9OhidTrPC zrUfB22P8Q3P@x{t{o0`&Tw!0Zg0{KkPL<1YX;Fo5qwonF#M!Gj_OZE#e@{wmm`U1jWOwYh zKh#30z$UfVX72ZkpWpBQeyujUuGX}@m_e;x)AleZud7v@onlp7t4`lgt6IKXW2?w2 z$nqkt$xgB#)Wwa>eqNb+H1N_VZyfH8*GFa*c2j+&PR|;fVq@CNGZ+JLT^-X-VuTTl za0CY^@~zFShaX<==d1aNLhpKip!(^?QQ;XrNt2tnBQ)AZol(r9(BFO_O?=ba!skZs z(Su~;kC~i_*6WYaXP3{LOP-?`u4|bMBQPBs<~{EF%ABSIUC;Iv$5vLz2F`Me8$M5I zTCGUc?wVjbf!T^jh2v`Vtn$LM{!|ynOhwu1*;%rCIn-y-gFw8H%{+0i? zXgSDko#LdoQk=4PQ(XAdqL47&TEcT8{*NghwnAuKM0hToiYM`7 z8J_-jq(7h0FK76b3}4IeFEjkR4DV(5PZ|Dah7Tos1EBm$$Z3hr@SrTgDJ1XU{F#WK z%h;P4{$7TEoZ(+(`1cup7dhQKqlol=7!6>w2@S(uF?_x08JBcF@WAI6yWDPY-RFTW zYFfuqTrv42-F3VmG;A1_wPZ9dkWn0=fralutLvG%#~Y5<)Ek!L^Khku-D0%4j4bOZ5QX2N~+~?4%lD1(QIPUQLfuwugYKCwSV4e_5;9P_Y5mGJPObx#Ia0>1#U&$CB&amKsW+$necgl_X(c@SS5TK-~+-J z06rqz2XK|}S%7PV2QbNs|86HRmJkCk#N~MAajO#W%K=n8r)dNRR9c4H;WTO6UA@Vd zx^2s}FJP4aEVyOSyL%#!F9`KOA5R2gxcr+(RTkGAFwNj Date: Tue, 31 Jan 2023 21:46:02 +0100 Subject: [PATCH 064/231] format --- applications/plugins/unitemp/Sensors.c | 25 +++---------------- applications/plugins/unitemp/sensors/BMP180.c | 3 +-- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/applications/plugins/unitemp/Sensors.c b/applications/plugins/unitemp/Sensors.c index 2fca40b53..d9304ab32 100644 --- a/applications/plugins/unitemp/Sensors.c +++ b/applications/plugins/unitemp/Sensors.c @@ -75,27 +75,10 @@ const Interface SPI = { //Перечень интерфейсов подключения //static const Interface* interfaces[] = {&SINGLE_WIRE, &I2C, &ONE_WIRE, &SPI}; //Перечень датчиков -static const SensorType* sensorTypes[] = { - &DHT11, - &DHT12_SW, - &DHT20, - &DHT21, - &DHT22, - &Dallas, - &AM2320_SW, - &AM2320_I2C, - &HTU21x, - &AHT10, - &SHT30, - &GXHT30, - &LM75, - &HDC1080, - &BMP180, - &BMP280, - &BME280, - &BME680, - &MAX31855, - &MAX6675}; +static const SensorType* sensorTypes[] = {&DHT11, &DHT12_SW, &DHT20, &DHT21, &DHT22, + &Dallas, &AM2320_SW, &AM2320_I2C, &HTU21x, &AHT10, + &SHT30, &GXHT30, &LM75, &HDC1080, &BMP180, + &BMP280, &BME280, &BME680, &MAX31855, &MAX6675}; const SensorType* unitemp_sensors_getTypeFromInt(uint8_t index) { if(index > SENSOR_TYPES_COUNT) return NULL; diff --git a/applications/plugins/unitemp/sensors/BMP180.c b/applications/plugins/unitemp/sensors/BMP180.c index e94f38044..aa0198289 100644 --- a/applications/plugins/unitemp/sensors/BMP180.c +++ b/applications/plugins/unitemp/sensors/BMP180.c @@ -101,8 +101,7 @@ bool unitemp_BMP180_init(Sensor* sensor) { bmp180_instance->bmp180_cal.MC = (buff[18] << 8) | buff[19]; bmp180_instance->bmp180_cal.MD = (buff[20] << 8) | buff[21]; - -UNITEMP_DEBUG( + UNITEMP_DEBUG( "Sensor BMP180 (0x%02X) calibration values: %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d", i2c_sensor->currentI2CAdr, bmp180_instance->bmp180_cal.AC1, From 5ebed42995aa0f121c9589e02323f66391ccbd95 Mon Sep 17 00:00:00 2001 From: yocvito Date: Tue, 31 Jan 2023 22:05:36 +0100 Subject: [PATCH 065/231] moves faps_copy syntax to new fap_deploy one in fbt help message --- scripts/fbt_tools/fbt_help.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fbt_tools/fbt_help.py b/scripts/fbt_tools/fbt_help.py index 9921588ed..afdb36665 100644 --- a/scripts/fbt_tools/fbt_help.py +++ b/scripts/fbt_tools/fbt_help.py @@ -11,7 +11,7 @@ Building: Build all FAP apps fap_{APPID}, launch_app APPSRC={APPID}: Build FAP app with appid={APPID}; upload & start it over USB - faps_copy: + fap_deploy: Build and upload all FAP apps over USB Flashing & debugging: From e4c642b72c758984a6fe7daca0719d43dd8c8846 Mon Sep 17 00:00:00 2001 From: yocvito Date: Wed, 1 Feb 2023 12:01:55 +0100 Subject: [PATCH 066/231] Restores bt mac @ and adv name before quitting bad usb app --- applications/main/bad_usb/bad_usb_app.c | 13 +++++++++++++ applications/main/bad_usb/bad_usb_app_i.h | 10 ++++++++++ applications/main/bad_usb/bad_usb_script.c | 11 +++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 63f95a860..43ed76c37 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -99,9 +99,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { app->bt = bt; const char* adv_name = bt_get_profile_adv_name(bt); memcpy(app->name, adv_name, BAD_USB_ADV_NAME_MAX_LEN); + memcpy(app->bt_old_config.name, adv_name, BAD_USB_ADV_NAME_MAX_LEN); const uint8_t* mac_addr = bt_get_profile_mac_address(bt); memcpy(app->mac, mac_addr, BAD_USB_MAC_ADDRESS_LEN); + memcpy(app->bt_old_config.mac, mac_addr, BAD_USB_MAC_ADDRESS_LEN); // Custom Widget app->widget = widget_alloc(); @@ -180,6 +182,17 @@ void bad_usb_app_free(BadUsbApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); + // restores bt config + // BtProfile have already been switched to the previous one + // so we directly modify the right profile + if (strcmp(app->bt_old_config.name, app->name) != 0) { + furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); + } + if (memcmp(app->bt_old_config.mac, app->mac, BAD_USB_MAC_ADDRESS_LEN) != 0) { + furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac); + } + + // Close records furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index ba0dee88b..abd252bb4 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -35,6 +35,15 @@ typedef enum BadUsbCustomEvent { BadUsbCustomEventErrorBack } BadUsbCustomEvent; +typedef struct { + uint8_t mac[BAD_USB_MAC_ADDRESS_LEN]; + char name[BAD_USB_ADV_NAME_MAX_LEN + 1]; + + // number of bt keys before starting the app (all keys added in + // the bt keys file then will be removed) + uint16_t n_keys; +} BadUsbBtConfig; + struct BadUsbApp { Gui* gui; ViewDispatcher* view_dispatcher; @@ -50,6 +59,7 @@ struct BadUsbApp { ByteInput* byte_input; uint8_t mac[BAD_USB_MAC_ADDRESS_LEN]; char name[BAD_USB_ADV_NAME_MAX_LEN + 1]; + BadUsbBtConfig bt_old_config; BadUsbAppError error; FuriString* file_path; diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 2e4c35a83..51ce49df9 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -648,8 +648,7 @@ static int32_t bad_usb_worker(void* context) { bt_disconnect(bad_usb->bt); furi_delay_ms(200); bt_keys_storage_set_storage_path(bad_usb->bt, HID_BT_KEYS_STORAGE_PATH); - - if(!bt_set_profile(bad_usb->bt, BtProfileHidKeyboard)) { + if (!bt_set_profile(bad_usb->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); return -1; } @@ -849,10 +848,10 @@ static int32_t bad_usb_worker(void* context) { bt_keys_storage_set_default_path(bad_usb->bt); bt_set_profile_pairing_method(bad_usb->bt, old_pairing_method); - - if(!bt_set_profile(bad_usb->bt, BtProfileSerial)) { - FURI_LOG_E(TAG, "Failed to switch to Serial profile"); - } + + // fails if ble radio stack isn't ready when switching profile + // if it happens, maybe we should increase the delay after bt_disconnect + bt_set_profile(bad_usb->bt, BtProfileSerial); } else { furi_hal_hid_set_state_callback(NULL, NULL); From 46fb86265c1a450eecdb05e0f1f49dcf54339535 Mon Sep 17 00:00:00 2001 From: AloneLiberty <111039319+AloneLiberty@users.noreply.github.com> Date: Thu, 2 Feb 2023 15:18:39 +0000 Subject: [PATCH 067/231] NFC: fix creating MF Classic tags from "Add Manually" menu (BCC calulation and ATQA/SAK writing) (#2342) * NFC: fix creating MF Classic cards from "Add Manually" menu (BCC calculation and AQTA/SAK writing) * NFC: Fix BCC/SAK/ATQA in unit_tests and SAK in nfc_generate_mf_classic Co-authored-by: gornekich --- applications/debug/unit_tests/nfc/nfc_test.c | 28 +++++++++++++-- lib/nfc/helpers/nfc_generators.c | 36 +++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/applications/debug/unit_tests/nfc/nfc_test.c b/applications/debug/unit_tests/nfc/nfc_test.c index d613be2b9..54bdd5909 100644 --- a/applications/debug/unit_tests/nfc/nfc_test.c +++ b/applications/debug/unit_tests/nfc/nfc_test.c @@ -348,13 +348,37 @@ static void mf_classic_generator_test(uint8_t uid_len, MfClassicType type) { memcpy(atqa, nfc_dev->dev_data.nfc_data.atqa, 2); MfClassicData* mf_data = &nfc_dev->dev_data.mf_classic_data; - // Check the manufacturer block (should be uid[uid_len] + 0xFF[rest]) + // Check the manufacturer block (should be uid[uid_len] + BCC (for 4byte only) + SAK + ATQA0 + ATQA1 + 0xFF[rest]) uint8_t manufacturer_block[16] = {0}; memcpy(manufacturer_block, nfc_dev->dev_data.mf_classic_data.block[0].value, 16); mu_assert( memcmp(manufacturer_block, uid, uid_len) == 0, "manufacturer_block uid doesn't match the file\r\n"); - for(uint8_t i = uid_len; i < 16; i++) { + + uint8_t position = 0; + if(uid_len == 4) { + position = uid_len; + + uint8_t bcc = 0; + + for(int i = 0; i < uid_len; i++) { + bcc ^= uid[i]; + } + + mu_assert(manufacturer_block[position] == bcc, "manufacturer_block bcc assert failed\r\n"); + } else { + position = uid_len - 1; + } + + mu_assert(manufacturer_block[position + 1] == sak, "manufacturer_block sak assert failed\r\n"); + + mu_assert( + manufacturer_block[position + 2] == atqa[0], "manufacturer_block atqa0 assert failed\r\n"); + + mu_assert( + manufacturer_block[position + 3] == atqa[1], "manufacturer_block atqa1 assert failed\r\n"); + + for(uint8_t i = position + 4; i < 16; i++) { mu_assert( manufacturer_block[i] == 0xFF, "manufacturer_block[i] == 0xFF assert failed\r\n"); } diff --git a/lib/nfc/helpers/nfc_generators.c b/lib/nfc/helpers/nfc_generators.c index 590ff4d50..50c89aba8 100644 --- a/lib/nfc/helpers/nfc_generators.c +++ b/lib/nfc/helpers/nfc_generators.c @@ -30,12 +30,32 @@ static void nfc_generate_mf_classic_uid(uint8_t* uid, uint8_t length) { furi_hal_random_fill_buf(&uid[1], length - 1); } -static void nfc_generate_mf_classic_block_0(uint8_t* block, uint8_t uid_len) { +static void nfc_generate_mf_classic_block_0( + uint8_t* block, + uint8_t uid_len, + uint8_t sak, + uint8_t atqa0, + uint8_t atqa1) { // Block length is always 16 bytes, and the UID can be either 4 or 7 bytes furi_assert(uid_len == 4 || uid_len == 7); furi_assert(block); - nfc_generate_mf_classic_uid(block, uid_len); - for(int i = uid_len; i < 16; i++) { + + if(uid_len == 4) { + // Calculate BCC + block[uid_len] = 0; + + for(int i = 0; i < uid_len; i++) { + block[uid_len] ^= block[i]; + } + } else { + uid_len -= 1; + } + + block[uid_len + 1] = sak; + block[uid_len + 2] = atqa0; + block[uid_len + 3] = atqa1; + + for(int i = uid_len + 4; i < 16; i++) { block[i] = 0xFF; } } @@ -74,7 +94,6 @@ static void data->nfc_data.type = FuriHalNfcTypeA; data->nfc_data.interface = FuriHalNfcInterfaceRf; data->nfc_data.uid_len = uid_len; - nfc_generate_mf_classic_block_0(data->mf_classic_data.block[0].value, uid_len); data->nfc_data.atqa[0] = 0x44; data->nfc_data.atqa[1] = 0x00; data->nfc_data.sak = 0x08; @@ -316,6 +335,7 @@ static void nfc_generate_ntag_i2c_plus_2k(NfcDeviceData* data) { void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType type) { nfc_generate_common_start(data); + nfc_generate_mf_classic_uid(data->mf_classic_data.block[0].value, uid_len); nfc_generate_mf_classic_common(data, uid_len, type); // Set the UID @@ -339,7 +359,6 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType } // Set SAK to 18 data->nfc_data.sak = 0x18; - } else if(type == MfClassicType1k) { // Set every block to 0xFF for(uint16_t i = 1; i < MF_CLASSIC_1K_TOTAL_SECTORS_NUM * 4; i += 1) { @@ -366,6 +385,13 @@ void nfc_generate_mf_classic(NfcDeviceData* data, uint8_t uid_len, MfClassicType data->nfc_data.sak = 0x09; } + nfc_generate_mf_classic_block_0( + data->mf_classic_data.block[0].value, + uid_len, + data->nfc_data.sak, + data->nfc_data.atqa[0], + data->nfc_data.atqa[1]); + mfc->type = type; } From e33023463a19b0338cfd81d7517cff4902f0f1bd Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 2 Feb 2023 21:38:45 +0000 Subject: [PATCH 068/231] Format --- .../debug/locale_test/application.fam | 2 +- applications/main/application.fam | 6 +- applications/plugins/application.fam | 2 +- .../plugins/asteroids/application.fam | 2 +- .../plugins/blackjack/application.fam | 6 +- .../plugins/cli_bridge/application.fam | 2 +- .../plugins/clock_app/application.fam | 2 +- applications/plugins/counter/application.fam | 2 +- .../plugins/dolphinbackup/application.fam | 2 +- .../plugins/dolphinrestorer/application.fam | 6 +- .../plugins/flashlight/application.fam | 2 +- .../plugins/flipper_i2ctools/application.fam | 2 +- .../plugins/game_2048/application.fam | 6 +- .../plugins/gpioreader2/application.fam | 2 +- applications/plugins/hc_sr04/application.fam | 2 +- .../htu21d_temp_sensor/application.fam | 2 +- applications/plugins/ifttt/application.fam | 2 +- .../plugins/morse_code/application.fam | 5 +- .../plugins/mouse_jiggler/application.fam | 2 +- .../plugins/namechanger/application.fam | 6 +- applications/plugins/paint/application.fam | 2 +- applications/plugins/passgen/application.fam | 2 +- .../plugins/protoview/application.fam | 2 +- applications/plugins/qrcode/application.fam | 4 +- .../plugins/solitaire/application.fam | 6 +- applications/plugins/subbrute/application.fam | 2 +- .../plugins/timelapse/application.fam | 17 ++-- applications/plugins/totp/application.fam | 9 +- applications/plugins/unitemp/application.fam | 8 +- .../plugins/wii_ec_anal/application.fam | 56 +++++------ applications/plugins/yatzee/application.fam | 1 - .../services/namechangersrv/application.fam | 2 +- assets/SConscript | 2 +- .../resources/subghz/assets/extend_range.txt | 4 +- lib/STM32CubeWB.scons | 10 +- scripts/FlipperPlaylist.py | 9 +- scripts/User/decode.py | 92 +++++++++++-------- scripts/User/encode.py | 14 +-- scripts/User/icondecode.py | 84 ++++++++++------- scripts/User/iconencode.py | 79 +++++++++------- scripts/asset_packer.py | 25 +++-- scripts/assets.py | 38 ++++++-- scripts/flipper/assets/dolphin.py | 14 ++- scripts/runfap.py | 2 +- scripts/sconsdist.py | 12 ++- scripts/version.py | 25 ++--- site_scons/cc.scons | 2 +- site_scons/commandline.scons | 2 +- site_scons/environ.scons | 2 +- site_scons/extapps.scons | 2 +- 50 files changed, 336 insertions(+), 256 deletions(-) diff --git a/applications/debug/locale_test/application.fam b/applications/debug/locale_test/application.fam index 32d065299..e46eeff51 100644 --- a/applications/debug/locale_test/application.fam +++ b/applications/debug/locale_test/application.fam @@ -8,4 +8,4 @@ App( stack_size=2 * 1024, order=70, fap_category="Debug", -) \ No newline at end of file +) diff --git a/applications/main/application.fam b/applications/main/application.fam index 376af8c42..9820ee3ac 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -25,13 +25,13 @@ App( apptype=FlipperAppType.METAPACKAGE, provides=[ "gpio", - #"ibutton", + # "ibutton", "infrared", "lfrfid", "nfc", "subghz", - #"bad_usb", - #"u2f", + # "bad_usb", + # "u2f", "fap_loader", "archive", ], diff --git a/applications/plugins/application.fam b/applications/plugins/application.fam index 3e5a654aa..3331888f2 100644 --- a/applications/plugins/application.fam +++ b/applications/plugins/application.fam @@ -3,7 +3,7 @@ App( name="Basic applications for plug-in menu", apptype=FlipperAppType.METAPACKAGE, provides=[ - "music_player", + "music_player", "music_beeper", "snake_game", "bt_hid", diff --git a/applications/plugins/asteroids/application.fam b/applications/plugins/asteroids/application.fam index 0a56122e7..f5ad2cdb9 100644 --- a/applications/plugins/asteroids/application.fam +++ b/applications/plugins/asteroids/application.fam @@ -5,7 +5,7 @@ App( entry_point="asteroids_app_entry", cdefines=["APP_PROTOVIEW"], requires=["gui"], - stack_size=8*1024, + stack_size=8 * 1024, order=50, fap_icon="appicon.png", fap_category="Games", diff --git a/applications/plugins/blackjack/application.fam b/applications/plugins/blackjack/application.fam index 8230cd047..489ce2aeb 100644 --- a/applications/plugins/blackjack/application.fam +++ b/applications/plugins/blackjack/application.fam @@ -4,10 +4,10 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="blackjack_app", cdefines=["APP_BLACKJACK"], - requires=["gui","storage","canvas"], + requires=["gui", "storage", "canvas"], stack_size=2 * 1024, order=30, fap_icon="blackjack_10px.png", fap_category="Games", - fap_icon_assets="assets" -) \ No newline at end of file + fap_icon_assets="assets", +) diff --git a/applications/plugins/cli_bridge/application.fam b/applications/plugins/cli_bridge/application.fam index 60cd9648d..f5f294d9b 100644 --- a/applications/plugins/cli_bridge/application.fam +++ b/applications/plugins/cli_bridge/application.fam @@ -3,7 +3,7 @@ App( name="CLI (subghz chat)", apptype=FlipperAppType.EXTERNAL, entry_point="cligui_main", - requires=["gui","cli"], + requires=["gui", "cli"], stack_size=8 * 1024, fap_icon="cligui.png", fap_category="Tools", diff --git a/applications/plugins/clock_app/application.fam b/applications/plugins/clock_app/application.fam index 644fbb65f..a6a2eff3e 100644 --- a/applications/plugins/clock_app/application.fam +++ b/applications/plugins/clock_app/application.fam @@ -7,4 +7,4 @@ App( stack_size=2 * 1024, fap_icon="clock.png", fap_category="Tools", -) \ No newline at end of file +) diff --git a/applications/plugins/counter/application.fam b/applications/plugins/counter/application.fam index bb2e58df1..5d164f20c 100644 --- a/applications/plugins/counter/application.fam +++ b/applications/plugins/counter/application.fam @@ -9,4 +9,4 @@ App( fap_category="Misc", fap_icon="icons/counter_icon.png", fap_icon_assets="icons", -) \ No newline at end of file +) diff --git a/applications/plugins/dolphinbackup/application.fam b/applications/plugins/dolphinbackup/application.fam index 024c1b197..166f9a22b 100644 --- a/applications/plugins/dolphinbackup/application.fam +++ b/applications/plugins/dolphinbackup/application.fam @@ -9,4 +9,4 @@ App( order=85, fap_icon="bckupIcon.png", fap_category="Tools", -) \ No newline at end of file +) diff --git a/applications/plugins/dolphinrestorer/application.fam b/applications/plugins/dolphinrestorer/application.fam index 46699404a..afd263c45 100644 --- a/applications/plugins/dolphinrestorer/application.fam +++ b/applications/plugins/dolphinrestorer/application.fam @@ -4,9 +4,9 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="drestorer_app", cdefines=["APP_DRESTORER"], - requires=["gui","storage"], - stack_size= 2 * 1024, + requires=["gui", "storage"], + stack_size=2 * 1024, order=90, fap_icon="restoreIcon.png", fap_category="Tools", -) \ No newline at end of file +) diff --git a/applications/plugins/flashlight/application.fam b/applications/plugins/flashlight/application.fam index 368e14833..6d70a036f 100644 --- a/applications/plugins/flashlight/application.fam +++ b/applications/plugins/flashlight/application.fam @@ -11,4 +11,4 @@ App( order=20, fap_icon="flash10px.png", fap_category="GPIO", -) \ No newline at end of file +) diff --git a/applications/plugins/flipper_i2ctools/application.fam b/applications/plugins/flipper_i2ctools/application.fam index d8d10dfce..f6522a86e 100644 --- a/applications/plugins/flipper_i2ctools/application.fam +++ b/applications/plugins/flipper_i2ctools/application.fam @@ -10,4 +10,4 @@ App( fap_icon="i2ctools.png", fap_category="GPIO", fap_icon_assets="images", -) \ No newline at end of file +) diff --git a/applications/plugins/game_2048/application.fam b/applications/plugins/game_2048/application.fam index c8940c881..f2456668e 100644 --- a/applications/plugins/game_2048/application.fam +++ b/applications/plugins/game_2048/application.fam @@ -10,6 +10,6 @@ App( ], stack_size=1 * 1024, order=90, - fap_icon="game_2048.png", - fap_category="Games" -) \ No newline at end of file + fap_icon="game_2048.png", + fap_category="Games", +) diff --git a/applications/plugins/gpioreader2/application.fam b/applications/plugins/gpioreader2/application.fam index 57792fd0c..bf25c323d 100644 --- a/applications/plugins/gpioreader2/application.fam +++ b/applications/plugins/gpioreader2/application.fam @@ -8,4 +8,4 @@ App( fap_category="GPIO", fap_icon="icon.png", order=1, -) \ No newline at end of file +) diff --git a/applications/plugins/hc_sr04/application.fam b/applications/plugins/hc_sr04/application.fam index da91a0864..bfbb2daa8 100644 --- a/applications/plugins/hc_sr04/application.fam +++ b/applications/plugins/hc_sr04/application.fam @@ -11,4 +11,4 @@ App( order=20, fap_icon="dist_sensor10px.png", fap_category="GPIO", -) \ No newline at end of file +) diff --git a/applications/plugins/htu21d_temp_sensor/application.fam b/applications/plugins/htu21d_temp_sensor/application.fam index 5807d7f51..724f55cd5 100644 --- a/applications/plugins/htu21d_temp_sensor/application.fam +++ b/applications/plugins/htu21d_temp_sensor/application.fam @@ -9,6 +9,6 @@ App( ], stack_size=2 * 1024, order=90, - fap_icon="temperature_sensor.png", + fap_icon="temperature_sensor.png", fap_category="GPIO", ) diff --git a/applications/plugins/ifttt/application.fam b/applications/plugins/ifttt/application.fam index 495627429..75e5beb67 100644 --- a/applications/plugins/ifttt/application.fam +++ b/applications/plugins/ifttt/application.fam @@ -11,4 +11,4 @@ App( order=20, fap_icon="icon.png", fap_category="GPIO", -) \ No newline at end of file +) diff --git a/applications/plugins/morse_code/application.fam b/applications/plugins/morse_code/application.fam index 1cc7bdaa1..73ad2ba81 100644 --- a/applications/plugins/morse_code/application.fam +++ b/applications/plugins/morse_code/application.fam @@ -10,6 +10,5 @@ App( stack_size=1 * 1024, order=20, fap_icon="morse_code_10px.png", - fap_category="Music" - -) \ No newline at end of file + fap_category="Music", +) diff --git a/applications/plugins/mouse_jiggler/application.fam b/applications/plugins/mouse_jiggler/application.fam index 6c529a883..6115315f5 100644 --- a/applications/plugins/mouse_jiggler/application.fam +++ b/applications/plugins/mouse_jiggler/application.fam @@ -9,4 +9,4 @@ App( order=150, fap_icon="mouse_10px.png", fap_category="Misc", -) \ No newline at end of file +) diff --git a/applications/plugins/namechanger/application.fam b/applications/plugins/namechanger/application.fam index 545f0b905..704b643c5 100644 --- a/applications/plugins/namechanger/application.fam +++ b/applications/plugins/namechanger/application.fam @@ -4,10 +4,10 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="namechanger_app", cdefines=["APP_NAMECHANGER"], - requires=["gui","storage"], + requires=["gui", "storage"], stack_size=2 * 1024, order=90, fap_icon="namechanger_10px.png", fap_category="Tools", - fap_icon_assets="icons", -) \ No newline at end of file + fap_icon_assets="icons", +) diff --git a/applications/plugins/paint/application.fam b/applications/plugins/paint/application.fam index 0630abbce..d727ab2d2 100644 --- a/applications/plugins/paint/application.fam +++ b/applications/plugins/paint/application.fam @@ -9,4 +9,4 @@ App( order=175, fap_icon="paintIcon.png", fap_category="Misc", -) \ No newline at end of file +) diff --git a/applications/plugins/passgen/application.fam b/applications/plugins/passgen/application.fam index 78d810a1d..94005e716 100644 --- a/applications/plugins/passgen/application.fam +++ b/applications/plugins/passgen/application.fam @@ -9,4 +9,4 @@ App( fap_category="Tools", fap_icon="icons/passgen_icon.png", fap_icon_assets="icons", -) \ No newline at end of file +) diff --git a/applications/plugins/protoview/application.fam b/applications/plugins/protoview/application.fam index 6cd31372e..d3614524c 100644 --- a/applications/plugins/protoview/application.fam +++ b/applications/plugins/protoview/application.fam @@ -5,7 +5,7 @@ App( entry_point="protoview_app_entry", cdefines=["APP_PROTOVIEW"], requires=["gui"], - stack_size=8*1024, + stack_size=8 * 1024, order=50, fap_icon="appicon.png", fap_category="Tools", diff --git a/applications/plugins/qrcode/application.fam b/applications/plugins/qrcode/application.fam index 6de585837..68a378fa4 100644 --- a/applications/plugins/qrcode/application.fam +++ b/applications/plugins/qrcode/application.fam @@ -1,7 +1,7 @@ App( appid="QRCode", name="QR Code", - fap_version=(1,0), + fap_version=(1, 0), fap_description="Display qrcodes", fap_author="Bob Matcuk", fap_weburl="https://github.com/bmatcuk/flipperzero-qrcode", @@ -17,4 +17,4 @@ App( fap_icon="icons/qrcode_10px.png", fap_icon_assets="icons", fap_icon_assets_symbol="qrcode", -) \ No newline at end of file +) diff --git a/applications/plugins/solitaire/application.fam b/applications/plugins/solitaire/application.fam index 66e4567ec..4011c343e 100644 --- a/applications/plugins/solitaire/application.fam +++ b/applications/plugins/solitaire/application.fam @@ -4,10 +4,10 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="solitaire_app", cdefines=["APP_SOLITAIRE"], - requires=["gui","storage","canvas"], + requires=["gui", "storage", "canvas"], stack_size=2 * 1024, order=30, fap_icon="solitaire_10px.png", fap_category="Games", - fap_icon_assets="assets" -) \ No newline at end of file + fap_icon_assets="assets", +) diff --git a/applications/plugins/subbrute/application.fam b/applications/plugins/subbrute/application.fam index 455bb822f..20a5f5367 100644 --- a/applications/plugins/subbrute/application.fam +++ b/applications/plugins/subbrute/application.fam @@ -4,7 +4,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="subbrute_app", cdefines=["APP_SUB_BRUTE"], - requires=["gui","dialogs"], + requires=["gui", "dialogs"], stack_size=2 * 1024, order=11, fap_icon="images/subbrute_10px.png", diff --git a/applications/plugins/timelapse/application.fam b/applications/plugins/timelapse/application.fam index ee725fe52..f5eaae551 100644 --- a/applications/plugins/timelapse/application.fam +++ b/applications/plugins/timelapse/application.fam @@ -4,18 +4,13 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="zeitraffer_app", cdefines=["APP_ZEITRAFFER"], - requires=[ - "gui", - "input", - "notification", - "gpio" - ], + requires=["gui", "input", "notification", "gpio"], stack_size=2 * 1024, order=90, - fap_icon_assets="icons", - fap_icon="zeitraffer.png", + fap_icon_assets="icons", + fap_icon="zeitraffer.png", fap_category="GPIO", - fap_description="Simple intervalometer app", + fap_description="Simple intervalometer app", fap_author="Aurelius Rosenbaum", - fap_weburl="https://github.com/theageoflove/flipperzero-zeitraffer", -) \ No newline at end of file + fap_weburl="https://github.com/theageoflove/flipperzero-zeitraffer", +) diff --git a/applications/plugins/totp/application.fam b/applications/plugins/totp/application.fam index ffb6d6137..81b0e22c3 100644 --- a/applications/plugins/totp/application.fam +++ b/applications/plugins/totp/application.fam @@ -4,14 +4,7 @@ App( apptype=FlipperAppType.EXTERNAL, entry_point="totp_app", cdefines=["APP_TOTP"], - requires=[ - "gui", - "cli", - "dialogs", - "storage", - "input", - "notification" - ], + requires=["gui", "cli", "dialogs", "storage", "input", "notification"], stack_size=2 * 1024, order=20, fap_category="Misc", diff --git a/applications/plugins/unitemp/application.fam b/applications/plugins/unitemp/application.fam index 3ac128eef..d975da5ff 100644 --- a/applications/plugins/unitemp/application.fam +++ b/applications/plugins/unitemp/application.fam @@ -9,11 +9,11 @@ App( ], stack_size=2 * 1024, order=100, - fap_description = "Universal temperature sensors reader", - fap_author = "Quenon", - fap_weburl = "https://github.com/quen0n/Unitemp-Flipper-Zero-Plugin", + fap_description="Universal temperature sensors reader", + fap_author="Quenon", + fap_weburl="https://github.com/quen0n/Unitemp-Flipper-Zero-Plugin", fap_category="GPIO", fap_icon="icon.png", fap_icon_assets="assets", fap_libs=["assets"], -) \ No newline at end of file +) diff --git a/applications/plugins/wii_ec_anal/application.fam b/applications/plugins/wii_ec_anal/application.fam index 655b5f938..4dc251713 100644 --- a/applications/plugins/wii_ec_anal/application.fam +++ b/applications/plugins/wii_ec_anal/application.fam @@ -1,36 +1,28 @@ # qv. https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/AppManifests.md App( - # --- App Info - appid="wii_ec_anal", - name="Wii EC Analyser", - - # --- Entry point - apptype=FlipperAppType.EXTERNAL, - entry_point="wii_ec_anal", - - # --- Interaction - cdefines=["APP_WII_EC_ANAL"], - requires=[ - "gui", - ], - -# conflicts="", -# sdk_headers="", - - # --- Run-time info - stack_size=2 * 1024, - order=20, - - # --- FAP details - sources=["wii_*.c", "gfx/*.c"], - -# fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/", -# fap_author="BlueChip", - -# fap_description="Wii Extension Controller Protocol Analyser", -# fap_version=(1,0), - - fap_icon="WiiEC.png", - fap_category="GPIO", + # --- App Info + appid="wii_ec_anal", + name="Wii EC Analyser", + # --- Entry point + apptype=FlipperAppType.EXTERNAL, + entry_point="wii_ec_anal", + # --- Interaction + cdefines=["APP_WII_EC_ANAL"], + requires=[ + "gui", + ], + # conflicts="", + # sdk_headers="", + # --- Run-time info + stack_size=2 * 1024, + order=20, + # --- FAP details + sources=["wii_*.c", "gfx/*.c"], + # fap_weburl="https://github.com/csBlueChip/FlipperZero_plugin_WiiChuck/", + # fap_author="BlueChip", + # fap_description="Wii Extension Controller Protocol Analyser", + # fap_version=(1,0), + fap_icon="WiiEC.png", + fap_category="GPIO", ) diff --git a/applications/plugins/yatzee/application.fam b/applications/plugins/yatzee/application.fam index 183021854..b30e90d23 100644 --- a/applications/plugins/yatzee/application.fam +++ b/applications/plugins/yatzee/application.fam @@ -9,5 +9,4 @@ App( fap_icon="images/yatzee_icon_10px.png", fap_category="Games", fap_icon_assets="images", - ) diff --git a/applications/services/namechangersrv/application.fam b/applications/services/namechangersrv/application.fam index b4bace40d..3263a3708 100644 --- a/applications/services/namechangersrv/application.fam +++ b/applications/services/namechangersrv/application.fam @@ -4,4 +4,4 @@ App( entry_point="namechanger_on_system_start", requires=["storage"], order=1000, -) \ No newline at end of file +) diff --git a/assets/SConscript b/assets/SConscript index aace521d2..ef5d83c79 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -96,4 +96,4 @@ if assetsenv["IS_BASE_FIRMWARE"]: env.Replace(FW_RESOURCES=resources) assetsenv.Alias("resources", resources) -Return("assetslib") \ No newline at end of file +Return("assetslib") diff --git a/assets/resources/subghz/assets/extend_range.txt b/assets/resources/subghz/assets/extend_range.txt index e66e7ceb2..1b404a0e5 100644 --- a/assets/resources/subghz/assets/extend_range.txt +++ b/assets/resources/subghz/assets/extend_range.txt @@ -2,7 +2,7 @@ Filetype: Flipper SubGhz Setting File Version: 1 # Whether to allow extended ranges that can break your flipper -use_ext_range_at_own_risk: true +use_ext_range_at_own_risk: false # Whether to ignore the default TX region settings -ignore_default_tx_region: true +ignore_default_tx_region: false diff --git a/lib/STM32CubeWB.scons b/lib/STM32CubeWB.scons index cbdde9814..a1b484ad1 100644 --- a/lib/STM32CubeWB.scons +++ b/lib/STM32CubeWB.scons @@ -14,9 +14,15 @@ env.Append( "USE_FULL_LL_DRIVER", ], SDK_HEADERS=[ - *env.GlobRecursive("*_ll_*.h", "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/", exclude="*usb.h"), + *env.GlobRecursive( + "*_ll_*.h", + "#/lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/", + exclude="*usb.h", + ), File("STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/FreeRTOS.h"), - File("STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h"), + File( + "STM32CubeWB/Middlewares/Third_Party/FreeRTOS/Source/include/stream_buffer.h" + ), ], ) diff --git a/scripts/FlipperPlaylist.py b/scripts/FlipperPlaylist.py index 4a688f2fb..6db250717 100644 --- a/scripts/FlipperPlaylist.py +++ b/scripts/FlipperPlaylist.py @@ -1,12 +1,14 @@ # Flipper Zero SUB-GHZ Playlist Generator import os import pip + try: from easygui import diropenbox except ImportError: - pip.main(['Install'], "easygui") + pip.main(["Install"], "easygui") from easygui import diropenbox + def main(): folder_path = diropenbox("Select the folder with Subghz files", "Subghz selector") output_path = diropenbox("Select your output location", "Output location") @@ -23,5 +25,6 @@ def main(): playlist_file.close() print("Done!") -if __name__ == '__main__': - main() \ No newline at end of file + +if __name__ == "__main__": + main() diff --git a/scripts/User/decode.py b/scripts/User/decode.py index f6779d967..64072b82f 100644 --- a/scripts/User/decode.py +++ b/scripts/User/decode.py @@ -6,69 +6,85 @@ import os import sys - def padded_hex(i, l): given_int = i given_len = l - hex_result = hex(given_int)[2:] # remove '0x' from beginning of str + hex_result = hex(given_int)[2:] # remove '0x' from beginning of str num_hex_chars = len(hex_result) - extra_zeros = '0' * (given_len - num_hex_chars) # may not get used.. + extra_zeros = "0" * (given_len - num_hex_chars) # may not get used.. - return ('0x' + hex_result if num_hex_chars == given_len else - '?' * given_len if num_hex_chars > given_len else - '0x' + extra_zeros + hex_result if num_hex_chars < given_len else - None) + return ( + "0x" + hex_result + if num_hex_chars == given_len + else "?" * given_len + if num_hex_chars > given_len + else "0x" + extra_zeros + hex_result + if num_hex_chars < given_len + else None + ) -parser = argparse.ArgumentParser(description='Turn cooked Flipper .bm files back into .xbm') -parser.add_argument('infile', metavar='i', - help='Input file') -parser.add_argument('outfile', metavar='o', - help='File to write to') -parser.add_argument('Width', metavar='W', type=int, nargs="?", default="128", - help='Width of the image. Find from meta.txt or directory name') -parser.add_argument('Height', metavar='H', type=int, nargs="?", default="64", - help='Height of the image. Find from meta.txt or directory name') +parser = argparse.ArgumentParser( + description="Turn cooked Flipper .bm files back into .xbm" +) + +parser.add_argument("infile", metavar="i", help="Input file") +parser.add_argument("outfile", metavar="o", help="File to write to") +parser.add_argument( + "Width", + metavar="W", + type=int, + nargs="?", + default="128", + help="Width of the image. Find from meta.txt or directory name", +) +parser.add_argument( + "Height", + metavar="H", + type=int, + nargs="?", + default="64", + help="Height of the image. Find from meta.txt or directory name", +) args = vars(parser.parse_args()) -r = open(args["infile"],"rb") -w = open(args["outfile"],"w") +r = open(args["infile"], "rb") +w = open(args["outfile"], "w") -fileStream=r.read() -filename=os.path.splitext(os.path.basename(args["outfile"]))[0] +fileStream = r.read() +filename = os.path.splitext(os.path.basename(args["outfile"]))[0] -imageWidth=args["Width"] -imageHeight=args["Height"] +imageWidth = args["Width"] +imageHeight = args["Height"] -#remove headers and padding -if(fileStream[0:2] == bytes([0x01,0x00])): - unpad=fileStream[4:] +# remove headers and padding +if fileStream[0:2] == bytes([0x01, 0x00]): + unpad = fileStream[4:] else: - if(fileStream[0:1] == bytes([0x00])): - unpad=fileStream[2:] + if fileStream[0:1] == bytes([0x00]): + unpad = fileStream[2:] - -#lzss decompress +# lzss decompress data_decoded_str = subprocess.check_output( - ["heatshrink", "-d","-w8","-l4"], input=unpad + ["heatshrink", "-d", "-w8", "-l4"], input=unpad ) -#turn it back into xbm +# turn it back into xbm -b=list(data_decoded_str) -c=', '.join(padded_hex(my_int,2) for my_int in b) +b = list(data_decoded_str) +c = ", ".join(padded_hex(my_int, 2) for my_int in b) -width_out = "#define "+ filename+ "_width "+ str(imageWidth) + "\n" -height_out = "#define "+ filename+ "_height "+ str(imageHeight) + "\n" -bytes_out = "static unsigned char "+ filename+ "_bits[] = {"+ str(c) + "};" +width_out = "#define " + filename + "_width " + str(imageWidth) + "\n" +height_out = "#define " + filename + "_height " + str(imageHeight) + "\n" +bytes_out = "static unsigned char " + filename + "_bits[] = {" + str(c) + "};" -data=width_out+height_out+bytes_out +data = width_out + height_out + bytes_out w.write(data) r.close() -w.close() \ No newline at end of file +w.close() diff --git a/scripts/User/encode.py b/scripts/User/encode.py index d2d533ea3..d88ec25f1 100644 --- a/scripts/User/encode.py +++ b/scripts/User/encode.py @@ -6,17 +6,17 @@ import os import sys -parser = argparse.ArgumentParser(description='Turn .xbm files into cooked .bm files for flipper FS') +parser = argparse.ArgumentParser( + description="Turn .xbm files into cooked .bm files for flipper FS" +) -parser.add_argument('infile', metavar='i', - help='Input file') -parser.add_argument('outfile', metavar='o', - help='File to write to') +parser.add_argument("infile", metavar="i", help="Input file") +parser.add_argument("outfile", metavar="o", help="File to write to") args = vars(parser.parse_args()) -r = open(args["infile"],"r") -w = open(args["outfile"],"wb") +r = open(args["infile"], "r") +w = open(args["outfile"], "wb") output = subprocess.check_output(["cat", args["infile"]]) diff --git a/scripts/User/icondecode.py b/scripts/User/icondecode.py index 596545269..c1177bc18 100644 --- a/scripts/User/icondecode.py +++ b/scripts/User/icondecode.py @@ -5,60 +5,82 @@ import io import os import sys + def padded_hex(i, l): given_int = i given_len = l - hex_result = hex(given_int)[2:] # remove '0x' from beginning of str + hex_result = hex(given_int)[2:] # remove '0x' from beginning of str num_hex_chars = len(hex_result) - extra_zeros = '0' * (given_len - num_hex_chars) # may not get used.. + extra_zeros = "0" * (given_len - num_hex_chars) # may not get used.. - return ('0x' + hex_result if num_hex_chars == given_len else - '?' * given_len if num_hex_chars > given_len else - '0x' + extra_zeros + hex_result if num_hex_chars < given_len else - None) + return ( + "0x" + hex_result + if num_hex_chars == given_len + else "?" * given_len + if num_hex_chars > given_len + else "0x" + extra_zeros + hex_result + if num_hex_chars < given_len + else None + ) -parser = argparse.ArgumentParser(description='Turn icon char arrays back into .xbm') +parser = argparse.ArgumentParser(description="Turn icon char arrays back into .xbm") -parser.add_argument('infile', metavar='i', - help='Input file') -parser.add_argument('outfile', metavar='o', - help='File to write to') -parser.add_argument('Width', metavar='W', type=int, nargs="?", default="128", - help='Width of the image. Find from meta.txt or directory name') -parser.add_argument('Height', metavar='H', type=int, nargs="?", default="64", - help='Height of the image. Find from meta.txt or directory name') -parser.add_argument('Trim', metavar='T', type=int, nargs="?", default="8", - help='Number of bytes off the start/header to trim. Multiples of 2 required.') +parser.add_argument("infile", metavar="i", help="Input file") +parser.add_argument("outfile", metavar="o", help="File to write to") +parser.add_argument( + "Width", + metavar="W", + type=int, + nargs="?", + default="128", + help="Width of the image. Find from meta.txt or directory name", +) +parser.add_argument( + "Height", + metavar="H", + type=int, + nargs="?", + default="64", + help="Height of the image. Find from meta.txt or directory name", +) +parser.add_argument( + "Trim", + metavar="T", + type=int, + nargs="?", + default="8", + help="Number of bytes off the start/header to trim. Multiples of 2 required.", +) args = vars(parser.parse_args()) -r = open(args["infile"],"r") -w = open(args["outfile"],"w") -imageWidth=args["Width"] -imageHeight=args["Height"] -trimStart=args["Trim"] +r = open(args["infile"], "r") +w = open(args["outfile"], "w") +imageWidth = args["Width"] +imageHeight = args["Height"] +trimStart = args["Trim"] -output = subprocess.check_output(["cat", args["infile"]]) #yes this is terrible. +output = subprocess.check_output(["cat", args["infile"]]) # yes this is terrible. f = io.StringIO(output.decode().strip()) -data = f.read().strip().replace(";","").replace("{","").replace("}","") +data = f.read().strip().replace(";", "").replace("{", "").replace("}", "") data_str = data.replace(",", "").replace("0x", "") data_bin = bytearray.fromhex(data_str[trimStart:]) data_decoded_str = subprocess.check_output( - ["heatshrink", "-d","-w8","-l4"], input=data_bin + ["heatshrink", "-d", "-w8", "-l4"], input=data_bin ) -b=list(data_decoded_str) +b = list(data_decoded_str) -c=', '.join(padded_hex(my_int,2) for my_int in b) +c = ", ".join(padded_hex(my_int, 2) for my_int in b) -width_out = "#define icon_width "+ str(imageWidth) + "\n" -height_out = "#define icon_height "+ str(imageHeight) + "\n" -bytes_out = "static unsigned char icon_bits[] = {"+ str(c) + "};" +width_out = "#define icon_width " + str(imageWidth) + "\n" +height_out = "#define icon_height " + str(imageHeight) + "\n" +bytes_out = "static unsigned char icon_bits[] = {" + str(c) + "};" -data=width_out+height_out+bytes_out +data = width_out + height_out + bytes_out w.write(data) r.close() diff --git a/scripts/User/iconencode.py b/scripts/User/iconencode.py index 67ba05d1b..e41e597dd 100644 --- a/scripts/User/iconencode.py +++ b/scripts/User/iconencode.py @@ -5,64 +5,81 @@ import io import os import sys + def padded_hex(i, l): given_int = i given_len = l - hex_result = hex(given_int)[2:] # remove '0x' from beginning of str + hex_result = hex(given_int)[2:] # remove '0x' from beginning of str num_hex_chars = len(hex_result) - extra_zeros = '0' * (given_len - num_hex_chars) # may not get used.. + extra_zeros = "0" * (given_len - num_hex_chars) # may not get used.. - return ('0x' + hex_result if num_hex_chars == given_len else - '?' * given_len if num_hex_chars > given_len else - '0x' + extra_zeros + hex_result if num_hex_chars < given_len else - None) + return ( + "0x" + hex_result + if num_hex_chars == given_len + else "?" * given_len + if num_hex_chars > given_len + else "0x" + extra_zeros + hex_result + if num_hex_chars < given_len + else None + ) -parser = argparse.ArgumentParser(description='Turn icon char arrays back into .xbm') +parser = argparse.ArgumentParser(description="Turn icon char arrays back into .xbm") -parser.add_argument('infile', metavar='i', - help='Input file') -parser.add_argument('Width', metavar='W', type=int, nargs="?", default="128", - help='Width of the image. Find from meta.txt or directory name') -parser.add_argument('Height', metavar='H', type=int, nargs="?", default="64", - help='Height of the image. Find from meta.txt or directory name') +parser.add_argument("infile", metavar="i", help="Input file") +parser.add_argument( + "Width", + metavar="W", + type=int, + nargs="?", + default="128", + help="Width of the image. Find from meta.txt or directory name", +) +parser.add_argument( + "Height", + metavar="H", + type=int, + nargs="?", + default="64", + help="Height of the image. Find from meta.txt or directory name", +) args = vars(parser.parse_args()) -r = open(args["infile"],"r") -infile=args["infile"].split(".")[0] +r = open(args["infile"], "r") +infile = args["infile"].split(".")[0] -imageWidth=args["Width"] -imageHeight=args["Height"] -dims=str(imageWidth)+"x"+str(imageHeight) +imageWidth = args["Width"] +imageHeight = args["Height"] +dims = str(imageWidth) + "x" + str(imageHeight) -output = subprocess.check_output(["cat", args["infile"]]) #yes this is terrible. +output = subprocess.check_output(["cat", args["infile"]]) # yes this is terrible. f = io.StringIO(output.decode().strip()) -data = f.read().strip().replace(";","").replace("{","").replace("}","") +data = f.read().strip().replace(";", "").replace("{", "").replace("}", "") data_str = data.replace(",", "").replace("0x", "") data_bin = bytearray.fromhex(data_str) data_encoded_str = subprocess.check_output( - ["heatshrink", "-e","-w8","-l4"], input=data_bin + ["heatshrink", "-e", "-w8", "-l4"], input=data_bin ) -b=list(data_encoded_str) +b = list(data_encoded_str) -c=','.join(padded_hex(my_int,2) for my_int in b) +c = ",".join(padded_hex(my_int, 2) for my_int in b) # a bit ugly. -framename="_I_"+infile+"_"+dims +framename = "_I_" + infile + "_" + dims print(len(b)) -#d=len(b) +# d=len(b) # if b > 255 split 0x1234 into 0x34,0x12 -#d=hex(len(b)) +# d=hex(len(b)) -char_out = "const uint8_t "+framename+"_0[] = {"+ str(c) + ",};" -char_out2 = "const uint8_t "+framename+"[] = {"+framename+"_0};" -#data=bytes_out +char_out = "const uint8_t " + framename + "_0[] = {" + str(c) + ",};" +char_out2 = "const uint8_t " + framename + "[] = {" + framename + "_0};" +# data=bytes_out print(char_out) print(char_out2) -#w.write(data) -#w.close() +# w.write(data) +# w.close() diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index 54c598ea5..aea755811 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -43,7 +43,9 @@ def convert_bmx(img: "Image.Image | pathlib.Path") -> bytes: return data -def pack(input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typing.Callable): +def pack( + input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typing.Callable +): input = pathlib.Path(input) output = pathlib.Path(output) output.mkdir(parents=True, exist_ok=True) @@ -68,7 +70,9 @@ def pack(input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typi if (source / "Anims/manifest.txt").exists(): (packed / "Anims").mkdir(parents=True, exist_ok=True) - shutil.copyfile(source / "Anims/manifest.txt", packed / "Anims/manifest.txt") + shutil.copyfile( + source / "Anims/manifest.txt", packed / "Anims/manifest.txt" + ) for anim in (source / "Anims").iterdir(): if not anim.is_dir(): continue @@ -77,9 +81,13 @@ def pack(input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typi if not frame.is_file(): continue if frame.name == "meta.txt": - shutil.copyfile(frame, packed / "Anims" / anim.name / "meta.txt") + shutil.copyfile( + frame, packed / "Anims" / anim.name / "meta.txt" + ) elif frame.name.startswith("frame_"): - (packed / "Anims" / anim.name / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + ( + packed / "Anims" / anim.name / frame.with_suffix(".bm").name + ).write_bytes(convert_bm(frame)) if (source / "Icons").is_dir(): for icons in (source / "Icons").iterdir(): @@ -89,7 +97,9 @@ def pack(input: "str | pathlib.Path", output: "str | pathlib.Path", logger: typi for icon in icons.iterdir(): if not icon.is_file(): continue - (packed / "Icons" / icons.name / icon.with_suffix(".bmx").name).write_bytes(convert_bmx(icon)) + ( + packed / "Icons" / icons.name / icon.with_suffix(".bmx").name + ).write_bytes(convert_bmx(icon)) if __name__ == "__main__": @@ -105,7 +115,4 @@ if __name__ == "__main__": pack(here, here / "dolphin_custom", logger=print) end = time.perf_counter() - input( - f"\nFinished in {round(end - start, 2)}s\n" - "Press [Enter] to exit" - ) + input(f"\nFinished in {round(end - start, 2)}s\n" "Press [Enter] to exit") diff --git a/scripts/assets.py b/scripts/assets.py index c616a4ab1..e7c5a6d12 100755 --- a/scripts/assets.py +++ b/scripts/assets.py @@ -30,11 +30,16 @@ valid_dirs = list() # This will not stay, dont worry! This is temp code until we got time to rewrite this all root_dir = pathlib.Path(__file__).absolute().parent.parent dolphin_external = root_dir / "assets/dolphin/external/" -potential_directories = [item for item in dolphin_external.iterdir() if item.is_dir()] # Get all animation directories +potential_directories = [ + item for item in dolphin_external.iterdir() if item.is_dir() +] # Get all animation directories + +for directory in potential_directories: # loop through all of them + if ( + directory / "manifest.txt" + ).exists(): # check if they contain a manifest.txt TODO: This code should be moved to wherever manifest.txt files are validated! + valid_dirs.append(directory) # append valid directory to list -for directory in potential_directories: # loop through all of them - if (directory / "manifest.txt").exists(): # check if they contain a manifest.txt TODO: This code should be moved to wherever manifest.txt files are validated! - valid_dirs.append(directory) # append valid directory to list class Main(App): def init(self): @@ -259,13 +264,24 @@ class Main(App): self.logger.info("Manifest is up-to-date!") # This will not stay, dont worry! This is temp code until we got time to rewrite this all - global valid_dirs # access our global variable - for valid_dir in valid_dirs: # We can copy the manifest for all of the valid dirs! - (root_dir / f"assets/resources/dolphin/{valid_dir.name}").mkdir(parents=True, exist_ok=True) - shutil.copyfile(valid_dir / "manifest.txt", root_dir / f"assets/resources/dolphin/{valid_dir.name}/manifest.txt") + global valid_dirs # access our global variable + for ( + valid_dir + ) in valid_dirs: # We can copy the manifest for all of the valid dirs! + (root_dir / f"assets/resources/dolphin/{valid_dir.name}").mkdir( + parents=True, exist_ok=True + ) + shutil.copyfile( + valid_dir / "manifest.txt", + root_dir / f"assets/resources/dolphin/{valid_dir.name}/manifest.txt", + ) (root_dir / "assets/resources/dolphin/manifest.txt").unlink() self.logger.info("Packing custom asset packs") - asset_packer.pack(root_dir / "assets/dolphin/custom", root_dir / f"assets/resources/dolphin_custom", self.logger.info) + asset_packer.pack( + root_dir / "assets/dolphin/custom", + root_dir / f"assets/resources/dolphin_custom", + self.logger.info, + ) self.logger.info(f"Complete") @@ -295,7 +311,9 @@ class Main(App): self.logger.info(f"Processing Dolphin sources") dolphin = Dolphin() self.logger.info(f"Loading data") - if f"dolphin{os.sep}external" in str(self.args.input_directory): # AHEM... oopsie. This script apparently just loads all assets, not only external assets, lol. + if f"dolphin{os.sep}external" in str( + self.args.input_directory + ): # AHEM... oopsie. This script apparently just loads all assets, not only external assets, lol. dolphin.load(self.args.input_directory, valid_dirs) else: dolphin.load(self.args.input_directory) diff --git a/scripts/flipper/assets/dolphin.py b/scripts/flipper/assets/dolphin.py index 899a8a615..45ed3e19f 100644 --- a/scripts/flipper/assets/dolphin.py +++ b/scripts/flipper/assets/dolphin.py @@ -35,7 +35,7 @@ class DolphinBubbleAnimation: min_level: int, max_level: int, weight: int, - subpath: str = None + subpath: str = None, ): # Manifest self.name = name @@ -188,7 +188,9 @@ class DolphinBubbleAnimation: def save(self, output_directory: str): if self.subpath: - animation_directory = os.path.join(output_directory, self.subpath, self.name) + animation_directory = os.path.join( + output_directory, self.subpath, self.name + ) else: animation_directory = os.path.join(output_directory, self.name) os.makedirs(animation_directory, exist_ok=True) @@ -294,7 +296,13 @@ class DolphinManifest: # Initialize animation animation = DolphinBubbleAnimation( - name, min_butthurt, max_butthurt, min_level, max_level, weight, subpath + name, + min_butthurt, + max_butthurt, + min_level, + max_level, + weight, + subpath, ) # Load Animation meta and frames diff --git a/scripts/runfap.py b/scripts/runfap.py index 14d885e8a..9f544fd90 100644 --- a/scripts/runfap.py +++ b/scripts/runfap.py @@ -103,4 +103,4 @@ class Main(App): if __name__ == "__main__": - Main()() \ No newline at end of file + Main()() diff --git a/scripts/sconsdist.py b/scripts/sconsdist.py index 0fa120d87..ce261954c 100644 --- a/scripts/sconsdist.py +++ b/scripts/sconsdist.py @@ -166,11 +166,13 @@ class Main(App): ) ) bundle_args.extend(self.other_args) - log_custom_fz_name = ( - environ.get("CUSTOM_FLIPPER_NAME", None) - or "" - ) - if (log_custom_fz_name != "") and (len(log_custom_fz_name) <= 8) and (log_custom_fz_name.isalnum()) and (log_custom_fz_name.isascii()): + log_custom_fz_name = environ.get("CUSTOM_FLIPPER_NAME", None) or "" + if ( + (log_custom_fz_name != "") + and (len(log_custom_fz_name) <= 8) + and (log_custom_fz_name.isalnum()) + and (log_custom_fz_name.isascii()) + ): self.logger.info( f"Flipper Custom Name is set:\n\tName: {log_custom_fz_name} : length - {len(log_custom_fz_name)} chars" ) diff --git a/scripts/version.py b/scripts/version.py index 363df0aeb..7f0a11349 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -11,7 +11,11 @@ from datetime import date, datetime class GitVersion: def __init__(self, source_dir): self.source_dir = source_dir - self.gitlist = [("commit", "rev-parse --short HEAD"), ("branch", "rev-parse --abbrev-ref") , ("branch_num", "rev-list -count HEAD")] + self.gitlist = [ + ("commit", "rev-parse --short HEAD"), + ("branch", "rev-parse --abbrev-ref"), + ("branch_num", "rev-list -count HEAD"), + ] def get_version_info(self): commit = branch = branch_num = "XFW-0040" @@ -36,20 +40,19 @@ class GitVersion: # If WORKFLOW_BRANCH_OR_TAG is set in environment, is has precedence # (set by CI) - custom_fz_name = ( - os.environ.get("CUSTOM_FLIPPER_NAME", None) - or "" - ) + custom_fz_name = os.environ.get("CUSTOM_FLIPPER_NAME", None) or "" - force_no_dirty = ( - os.environ.get("FORCE_NO_DIRTY", None) - or "" - ) + force_no_dirty = os.environ.get("FORCE_NO_DIRTY", None) or "" - if (force_no_dirty != ""): + if force_no_dirty != "": dirty = False - if (custom_fz_name != "") and (len(custom_fz_name) <= 8) and (custom_fz_name.isalnum()) and (custom_fz_name.isascii()): + if ( + (custom_fz_name != "") + and (len(custom_fz_name) <= 8) + and (custom_fz_name.isalnum()) + and (custom_fz_name.isascii()) + ): return { "GIT_COMMIT": commit, "GIT_BRANCH": "dev", diff --git a/site_scons/cc.scons b/site_scons/cc.scons index 95888dc95..1eb6a3376 100644 --- a/site_scons/cc.scons +++ b/site_scons/cc.scons @@ -44,4 +44,4 @@ ENV.AppendUnique( "-mlittle-endian", "-mthumb", ], -) \ No newline at end of file +) diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index 837fd0b85..722dd3cfa 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -243,4 +243,4 @@ vars.AddVariables( ), ) -Return("vars") \ No newline at end of file +Return("vars") diff --git a/site_scons/environ.scons b/site_scons/environ.scons index ab60d4e71..0ffc8a6a2 100644 --- a/site_scons/environ.scons +++ b/site_scons/environ.scons @@ -88,4 +88,4 @@ SetOption("max_drift", 1) wrap_tempfile(coreenv, "LINKCOM") wrap_tempfile(coreenv, "ARCOM") -Return("coreenv") \ No newline at end of file +Return("coreenv") diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index ea16d1e78..bff9a8c30 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -152,4 +152,4 @@ if appenv["FORCE"]: appenv.AlwaysBuild(sdk_source, sdk_tree, sdk_apicheck, sdk_apisyms) -Return("extapps") \ No newline at end of file +Return("extapps") From 64fa597e19943452ae0fcf057830f5573ccf7d39 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 05:01:00 +0000 Subject: [PATCH 069/231] Fix animation load fail, size too big --- applications/services/desktop/animations/animation_storage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index c717b0c36..a80a958a3 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -336,7 +336,7 @@ static bool animation_storage_load_frames( FileInfo file_info; FuriString* filename; filename = furi_string_alloc(); - size_t max_filesize = ROUND_UP_TO(width, 8) * height + 1; + size_t max_filesize = ROUND_UP_TO(width, 8) * height + 2; for(int i = 0; i < icon->frame_count; ++i) { frames_ok = false; From 195aea9c37d9a1e8342edac992b053942a034455 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 3 Feb 2023 11:43:16 +0100 Subject: [PATCH 070/231] Update storage_settings_scene_sd_info.c --- .../scenes/storage_settings_scene_sd_info.c | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index f69f6c7ef..4901e960e 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -28,14 +28,44 @@ void storage_settings_scene_sd_info_on_enter(void* context) { dialog_ex, "Try to reinsert\nor format SD\ncard.", 3, 19, AlignLeft, AlignTop); dialog_ex_set_center_button_text(dialog_ex, "Ok"); } else { + char unit_kb[] = "KB"; + char unit_mb[] = "MB"; + char unit_gb[] = "GB"; + + double sd_total_val = (double)sd_info.kb_total; + char* sd_total_unit = unit_kb; + double sd_free_val = (double)sd_info.kb_free; + char* sd_free_unit = unit_kb; + + if(sd_total_val > 1024) { + sd_total_val /= 1024; + sd_total_unit = unit_mb; + } + if(sd_total_val > 1024) { + sd_total_val /= 1024; + sd_total_unit = unit_gb; + } + + if(sd_free_val > 1024) { + sd_free_val /= 1024; + sd_free_unit = unit_mb; + } + if(sd_free_val > 1024) { + sd_free_val /= 1024; + sd_free_unit = unit_gb; + } + furi_string_printf( app->text_string, - "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" + "Label: %s\nType: %s\n%.2f %s total\n%.2f %s free %.2f%% free\n" "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), - sd_info.kb_total, - sd_info.kb_free, + sd_total_val, + sd_total_unit, + sd_free_val, + sd_free_unit, + (double)(((int)sd_info.kb_free * 100.0) / (int)sd_info.kb_total), sd_cid.ManufacturerID, sd_cid.OEM_AppliID, sd_cid.ProdName, From f3bb068fd71a864e360fec037e836f183a1d5c52 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 17:43:59 +0000 Subject: [PATCH 071/231] Rename bad usb to bad kb --- applications/ReadMe.md | 2 +- .../file_browser_test/file_browser_app.c | 2 +- .../icons/{badusb_10px.png => badkb_10px.png} | Bin .../scenes/file_browser_scene_start.c | 2 +- applications/main/application.fam | 4 +- .../main/archive/helpers/archive_browser.h | 6 +- .../main/archive/helpers/archive_files.c | 6 +- .../main/archive/helpers/archive_files.h | 2 +- .../archive/scenes/archive_scene_browser.c | 2 +- .../main/archive/views/archive_browser_view.c | 12 +- .../main/archive/views/archive_browser_view.h | 2 +- .../main/{bad_usb => bad_kb}/application.fam | 10 +- .../bad_usb_app.c => bad_kb/bad_kb_app.c} | 116 ++--- applications/main/bad_kb/bad_kb_app.h | 13 + applications/main/bad_kb/bad_kb_app_i.h | 80 ++++ .../bad_kb_script.c} | 426 +++++++++--------- applications/main/bad_kb/bad_kb_script.h | 49 ++ .../main/bad_kb/bad_kb_settings_filename.h | 3 + .../main/bad_kb/scenes/bad_kb_scene.c | 30 ++ .../scenes/bad_kb_scene.h} | 16 +- .../main/bad_kb/scenes/bad_kb_scene_config.h | 8 + .../bad_kb/scenes/bad_kb_scene_config_bt.c | 84 ++++ .../scenes/bad_kb_scene_config_layout.c | 48 ++ .../bad_kb/scenes/bad_kb_scene_config_mac.c | 57 +++ .../bad_kb/scenes/bad_kb_scene_config_name.c | 46 ++ .../bad_kb/scenes/bad_kb_scene_config_usb.c | 72 +++ .../scenes/bad_kb_scene_error.c} | 30 +- .../bad_kb/scenes/bad_kb_scene_file_select.c | 52 +++ .../main/bad_kb/scenes/bad_kb_scene_work.c | 58 +++ .../views/bad_kb_view.c} | 114 ++--- applications/main/bad_kb/views/bad_kb_view.h | 21 + applications/main/bad_usb/bad_usb_app.h | 13 - applications/main/bad_usb/bad_usb_app_i.h | 80 ---- applications/main/bad_usb/bad_usb_script.h | 49 -- .../main/bad_usb/bad_usb_settings_filename.h | 3 - .../main/bad_usb/scenes/bad_usb_scene.c | 30 -- .../bad_usb/scenes/bad_usb_scene_config.h | 8 - .../bad_usb/scenes/bad_usb_scene_config_bt.c | 84 ---- .../scenes/bad_usb_scene_config_layout.c | 48 -- .../bad_usb/scenes/bad_usb_scene_config_mac.c | 57 --- .../scenes/bad_usb_scene_config_name.c | 46 -- .../bad_usb/scenes/bad_usb_scene_config_usb.c | 72 --- .../scenes/bad_usb_scene_file_select.c | 52 --- .../main/bad_usb/scenes/bad_usb_scene_work.c | 58 --- .../main/bad_usb/views/bad_usb_view.h | 21 - .../dolphinbackup/storage_DolphinBackup.c | 2 +- .../icons/{badusb_10px.png => badkb_10px.png} | Bin .../{badusb_10px.png => badkb_10px.png} | Bin .../plugins/mousejacker/mousejacker.c | 4 +- .../plugins/totp/services/config/config.c | 4 +- .../totp_scene_generate_token.c | 40 +- .../services/dolphin/helpers/dolphin_deed.c | 4 +- .../services/dolphin/helpers/dolphin_deed.h | 4 +- .../gui/modules/file_browser_worker.c | 4 +- .../{badusb_10px.png => badkb_10px.png} | Bin .../icons/{BadUsb => BadKb}/Clock_18x18.png | Bin .../icons/{BadUsb => BadKb}/Error_18x18.png | Bin .../{BadUsb => BadKb}/EviSmile1_18x21.png | Bin .../{BadUsb => BadKb}/EviSmile2_18x21.png | Bin .../{BadUsb => BadKb}/EviWaiting1_18x21.png | Bin .../{BadUsb => BadKb}/EviWaiting2_18x21.png | Bin .../icons/{BadUsb => BadKb}/Percent_10x14.png | Bin .../icons/{BadUsb => BadKb}/Smile_18x18.png | Bin .../icons/{BadUsb => BadKb}/UsbTree_48x22.png | Bin .../{BadUsb_14 => BadKb_14}/frame_01.png | Bin .../{BadUsb_14 => BadKb_14}/frame_02.png | Bin .../{BadUsb_14 => BadKb_14}/frame_03.png | Bin .../{BadUsb_14 => BadKb_14}/frame_04.png | Bin .../{BadUsb_14 => BadKb_14}/frame_05.png | Bin .../{BadUsb_14 => BadKb_14}/frame_06.png | Bin .../{BadUsb_14 => BadKb_14}/frame_07.png | Bin .../{BadUsb_14 => BadKb_14}/frame_08.png | Bin .../{BadUsb_14 => BadKb_14}/frame_09.png | Bin .../{BadUsb_14 => BadKb_14}/frame_10.png | Bin .../{BadUsb_14 => BadKb_14}/frame_11.png | Bin .../{BadUsb_14 => BadKb_14}/frame_rate | 0 assets/resources/badkb/.badkb.settings | 1 + .../Kiosk-Evasion-Bruteforce.txt | 0 .../{badusb => badkb}/Wifi-Stealer_ORG.txt | 0 .../{badusb => badkb}/demo_macos.txt | 4 +- .../{badusb => badkb}/demo_windows.txt | 4 +- .../{badusb => badkb}/layouts/ba-BA.kl | Bin .../{badusb => badkb}/layouts/cz_CS.kl | Bin .../{badusb => badkb}/layouts/da-DA.kl | Bin .../{badusb => badkb}/layouts/de-CH.kl | Bin .../{badusb => badkb}/layouts/de-DE.kl | Bin .../{badusb => badkb}/layouts/dvorak.kl | Bin .../{badusb => badkb}/layouts/en-UK.kl | Bin .../{badusb => badkb}/layouts/en-US.kl | Bin .../{badusb => badkb}/layouts/es-ES.kl | Bin .../{badusb => badkb}/layouts/fr-BE.kl | Bin .../{badusb => badkb}/layouts/fr-CH.kl | Bin .../{badusb => badkb}/layouts/fr-FR.kl | Bin .../{badusb => badkb}/layouts/hr-HR.kl | Bin .../{badusb => badkb}/layouts/hu-HU.kl | Bin .../{badusb => badkb}/layouts/it-IT.kl | Bin .../{badusb => badkb}/layouts/nb-NO.kl | Bin .../{badusb => badkb}/layouts/nl-NL.kl | Bin .../{badusb => badkb}/layouts/pt-BR.kl | Bin .../{badusb => badkb}/layouts/pt-PT.kl | Bin .../{badusb => badkb}/layouts/si-SI.kl | Bin .../{badusb => badkb}/layouts/sk-SK.kl | Bin .../{badusb => badkb}/layouts/sv-SE.kl | Bin .../{badusb => badkb}/layouts/tr-TR.kl | Bin assets/resources/badusb/.badusb.settings | 1 - 105 files changed, 1033 insertions(+), 1033 deletions(-) rename applications/debug/file_browser_test/icons/{badusb_10px.png => badkb_10px.png} (100%) rename applications/main/{bad_usb => bad_kb}/application.fam (55%) rename applications/main/{bad_usb/bad_usb_app.c => bad_kb/bad_kb_app.c} (52%) create mode 100644 applications/main/bad_kb/bad_kb_app.h create mode 100644 applications/main/bad_kb/bad_kb_app_i.h rename applications/main/{bad_usb/bad_usb_script.c => bad_kb/bad_kb_script.c} (65%) create mode 100644 applications/main/bad_kb/bad_kb_script.h create mode 100644 applications/main/bad_kb/bad_kb_settings_filename.h create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene.c rename applications/main/{bad_usb/scenes/bad_usb_scene.h => bad_kb/scenes/bad_kb_scene.h} (68%) create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config.h create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_name.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c rename applications/main/{bad_usb/scenes/bad_usb_scene_error.c => bad_kb/scenes/bad_kb_scene_error.c} (70%) create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_file_select.c create mode 100644 applications/main/bad_kb/scenes/bad_kb_scene_work.c rename applications/main/{bad_usb/views/bad_usb_view.c => bad_kb/views/bad_kb_view.c} (70%) create mode 100644 applications/main/bad_kb/views/bad_kb_view.h delete mode 100644 applications/main/bad_usb/bad_usb_app.h delete mode 100644 applications/main/bad_usb/bad_usb_app_i.h delete mode 100644 applications/main/bad_usb/bad_usb_script.h delete mode 100644 applications/main/bad_usb/bad_usb_settings_filename.h delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config.h delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_name.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_file_select.c delete mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_work.c delete mode 100644 applications/main/bad_usb/views/bad_usb_view.h rename applications/plugins/mousejacker/icons/{badusb_10px.png => badkb_10px.png} (100%) rename applications/plugins/mousejacker/images/{badusb_10px.png => badkb_10px.png} (100%) rename assets/icons/Archive/{badusb_10px.png => badkb_10px.png} (100%) rename assets/icons/{BadUsb => BadKb}/Clock_18x18.png (100%) rename assets/icons/{BadUsb => BadKb}/Error_18x18.png (100%) rename assets/icons/{BadUsb => BadKb}/EviSmile1_18x21.png (100%) rename assets/icons/{BadUsb => BadKb}/EviSmile2_18x21.png (100%) rename assets/icons/{BadUsb => BadKb}/EviWaiting1_18x21.png (100%) rename assets/icons/{BadUsb => BadKb}/EviWaiting2_18x21.png (100%) rename assets/icons/{BadUsb => BadKb}/Percent_10x14.png (100%) rename assets/icons/{BadUsb => BadKb}/Smile_18x18.png (100%) rename assets/icons/{BadUsb => BadKb}/UsbTree_48x22.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_01.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_02.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_03.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_04.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_05.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_06.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_07.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_08.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_09.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_10.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_11.png (100%) rename assets/icons/MainMenu/{BadUsb_14 => BadKb_14}/frame_rate (100%) create mode 100644 assets/resources/badkb/.badkb.settings rename assets/resources/{badusb => badkb}/Kiosk-Evasion-Bruteforce.txt (100%) rename assets/resources/{badusb => badkb}/Wifi-Stealer_ORG.txt (100%) rename assets/resources/{badusb => badkb}/demo_macos.txt (92%) rename assets/resources/{badusb => badkb}/demo_windows.txt (92%) rename assets/resources/{badusb => badkb}/layouts/ba-BA.kl (100%) rename assets/resources/{badusb => badkb}/layouts/cz_CS.kl (100%) rename assets/resources/{badusb => badkb}/layouts/da-DA.kl (100%) rename assets/resources/{badusb => badkb}/layouts/de-CH.kl (100%) rename assets/resources/{badusb => badkb}/layouts/de-DE.kl (100%) rename assets/resources/{badusb => badkb}/layouts/dvorak.kl (100%) rename assets/resources/{badusb => badkb}/layouts/en-UK.kl (100%) rename assets/resources/{badusb => badkb}/layouts/en-US.kl (100%) rename assets/resources/{badusb => badkb}/layouts/es-ES.kl (100%) rename assets/resources/{badusb => badkb}/layouts/fr-BE.kl (100%) rename assets/resources/{badusb => badkb}/layouts/fr-CH.kl (100%) rename assets/resources/{badusb => badkb}/layouts/fr-FR.kl (100%) rename assets/resources/{badusb => badkb}/layouts/hr-HR.kl (100%) rename assets/resources/{badusb => badkb}/layouts/hu-HU.kl (100%) rename assets/resources/{badusb => badkb}/layouts/it-IT.kl (100%) rename assets/resources/{badusb => badkb}/layouts/nb-NO.kl (100%) rename assets/resources/{badusb => badkb}/layouts/nl-NL.kl (100%) rename assets/resources/{badusb => badkb}/layouts/pt-BR.kl (100%) rename assets/resources/{badusb => badkb}/layouts/pt-PT.kl (100%) rename assets/resources/{badusb => badkb}/layouts/si-SI.kl (100%) rename assets/resources/{badusb => badkb}/layouts/sk-SK.kl (100%) rename assets/resources/{badusb => badkb}/layouts/sv-SE.kl (100%) rename assets/resources/{badusb => badkb}/layouts/tr-TR.kl (100%) delete mode 100644 assets/resources/badusb/.badusb.settings diff --git a/applications/ReadMe.md b/applications/ReadMe.md index 3bd2aeb06..efc9afd86 100644 --- a/applications/ReadMe.md +++ b/applications/ReadMe.md @@ -25,7 +25,7 @@ Applications for factory testing the Flipper. Applications for main Flipper menu. - `archive` - Archive and file manager -- `bad_usb` - Bad USB application +- `bad_kb` - Bad KB application - `fap_loader` - External applications loader - `gpio` - GPIO application: includes USART bridge and GPIO control - `ibutton` - iButton application, onewire keys and more diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index bf423d34e..1df4891cd 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -48,7 +48,7 @@ FileBrowserApp* file_browser_app_alloc(char* arg) { app->file_path = furi_string_alloc(); app->file_browser = file_browser_alloc(app->file_path); - file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badusb_10px, true); + file_browser_configure(app->file_browser, "*", NULL, true, false, &I_badkb_10px, true); view_dispatcher_add_view( app->view_dispatcher, FileBrowserAppViewStart, widget_get_view(app->widget)); diff --git a/applications/debug/file_browser_test/icons/badusb_10px.png b/applications/debug/file_browser_test/icons/badkb_10px.png similarity index 100% rename from applications/debug/file_browser_test/icons/badusb_10px.png rename to applications/debug/file_browser_test/icons/badkb_10px.png diff --git a/applications/debug/file_browser_test/scenes/file_browser_scene_start.c b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c index 9eb26944f..9211ff3bb 100644 --- a/applications/debug/file_browser_test/scenes/file_browser_scene_start.c +++ b/applications/debug/file_browser_test/scenes/file_browser_scene_start.c @@ -19,7 +19,7 @@ bool file_browser_scene_start_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - furi_string_set(app->file_path, ANY_PATH("badusb/demo_windows.txt")); + furi_string_set(app->file_path, ANY_PATH("badkb/demo_windows.txt")); scene_manager_next_scene(app->scene_manager, FileBrowserSceneBrowser); consumed = true; } else if(event.type == SceneManagerEventTypeTick) { diff --git a/applications/main/application.fam b/applications/main/application.fam index 9820ee3ac..eefb801b3 100644 --- a/applications/main/application.fam +++ b/applications/main/application.fam @@ -9,7 +9,7 @@ App( "lfrfid", "nfc", "subghz", - "bad_usb", + "bad_kb", "u2f", "fap_loader", "sub_playlist", @@ -30,7 +30,7 @@ App( "lfrfid", "nfc", "subghz", - # "bad_usb", + # "bad_kb", # "u2f", "fap_loader", "archive", diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 5b13e98da..ece04ad72 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -14,7 +14,7 @@ static const char* tab_default_paths[] = { [ArchiveTabSubGhz] = ANY_PATH("subghz"), [ArchiveTabLFRFID] = ANY_PATH("lfrfid"), [ArchiveTabInfrared] = ANY_PATH("infrared"), - [ArchiveTabBadUsb] = ANY_PATH("badusb"), + [ArchiveTabBadKb] = ANY_PATH("badkb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, @@ -26,7 +26,7 @@ static const char* known_ext[] = { [ArchiveFileTypeSubGhz] = ".sub", [ArchiveFileTypeLFRFID] = ".rfid", [ArchiveFileTypeInfrared] = ".ir", - [ArchiveFileTypeBadUsb] = ".txt", + [ArchiveFileTypeBadKb] = ".txt", [ArchiveFileTypeU2f] = "?", [ArchiveFileTypeApplication] = ".fap", [ArchiveFileTypeUpdateManifest] = ".fuf", @@ -41,7 +41,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabSubGhz] = ArchiveFileTypeSubGhz, [ArchiveTabLFRFID] = ArchiveFileTypeLFRFID, [ArchiveTabInfrared] = ArchiveFileTypeInfrared, - [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, + [ArchiveTabBadKb] = ArchiveFileTypeBadKb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 5c06c1bda..7e7ab1774 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -16,11 +16,11 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder for(size_t i = 0; i < COUNT_OF(known_ext); i++) { if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { - if(i == ArchiveFileTypeBadUsb) { + if(i == ArchiveFileTypeBadKb) { if(furi_string_search( - file->path, archive_get_default_path(ArchiveTabBadUsb)) == 0) { + file->path, archive_get_default_path(ArchiveTabBadKb)) == 0) { file->type = i; - return; // *.txt file is a BadUSB script only if it is in BadUSB folder + return; // *.txt file is a BadKB script only if it is in BadKB folder } } else { file->type = i; diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index db624f5b5..62d635461 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -15,7 +15,7 @@ typedef enum { ArchiveFileTypeSubGhz, ArchiveFileTypeLFRFID, ArchiveFileTypeInfrared, - ArchiveFileTypeBadUsb, + ArchiveFileTypeBadKb, ArchiveFileTypeU2f, ArchiveFileTypeApplication, ArchiveFileTypeUpdateManifest, diff --git a/applications/main/archive/scenes/archive_scene_browser.c b/applications/main/archive/scenes/archive_scene_browser.c index f88efb0c4..0696647ea 100644 --- a/applications/main/archive/scenes/archive_scene_browser.c +++ b/applications/main/archive/scenes/archive_scene_browser.c @@ -17,7 +17,7 @@ static const char* flipper_app_name[] = { [ArchiveFileTypeSubGhz] = "Sub-GHz", [ArchiveFileTypeLFRFID] = "125 kHz RFID", [ArchiveFileTypeInfrared] = "Infrared", - [ArchiveFileTypeBadUsb] = "Bad USB", + [ArchiveFileTypeBadKb] = "Bad KB", [ArchiveFileTypeU2f] = "U2F", [ArchiveFileTypeApplication] = "Applications", [ArchiveFileTypeUpdateManifest] = "UpdaterApp", diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index dce753fde..213d65512 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -16,7 +16,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabSubGhz] = "Sub-GHz", [ArchiveTabLFRFID] = "RFID LF", [ArchiveTabInfrared] = "Infrared", - [ArchiveTabBadUsb] = "Bad USB", + [ArchiveTabBadKb] = "Bad KB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", [ArchiveTabBrowser] = "Browser", @@ -28,7 +28,7 @@ static const Icon* ArchiveItemIcons[] = { [ArchiveFileTypeSubGhz] = &I_sub1_10px, [ArchiveFileTypeLFRFID] = &I_125_10px, [ArchiveFileTypeInfrared] = &I_ir_10px, - [ArchiveFileTypeBadUsb] = &I_badusb_10px, + [ArchiveFileTypeBadKb] = &I_badkb_10px, [ArchiveFileTypeU2f] = &I_u2f_10px, [ArchiveFileTypeApplication] = &I_Apps_10px, [ArchiveFileTypeUpdateManifest] = &I_update_10px, @@ -109,7 +109,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_pin, ArchiveBrowserEventFileMenuPin); - if(selected->type <= ArchiveFileTypeBadUsb) { + if(selected->type <= ArchiveFileTypeBadKb) { archive_menu_add_item( menu_array_push_raw(model->context_menu), item_show, @@ -129,7 +129,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_info, ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadUsb) { + if(selected->type <= ArchiveFileTypeBadKb) { archive_menu_add_item( menu_array_push_raw(model->context_menu), item_show, @@ -157,7 +157,7 @@ static void render_item_menu(Canvas* canvas, ArchiveBrowserViewModel* model) { menu_array_push_raw(model->context_menu), item_info, ArchiveBrowserEventFileMenuInfo); - if(selected->type <= ArchiveFileTypeBadUsb) { + if(selected->type <= ArchiveFileTypeBadKb) { archive_menu_add_item( menu_array_push_raw(model->context_menu), item_show, @@ -588,4 +588,4 @@ void browser_free(ArchiveBrowserView* browser) { view_free(browser->view); free(browser); -} \ No newline at end of file +} diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index dfe18d13b..1e6cbf036 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -25,7 +25,7 @@ typedef enum { ArchiveTabNFC, ArchiveTabInfrared, ArchiveTabIButton, - ArchiveTabBadUsb, + ArchiveTabBadKb, ArchiveTabU2f, ArchiveTabApplications, ArchiveTabBrowser, diff --git a/applications/main/bad_usb/application.fam b/applications/main/bad_kb/application.fam similarity index 55% rename from applications/main/bad_usb/application.fam rename to applications/main/bad_kb/application.fam index 2442dd3aa..09531da81 100644 --- a/applications/main/bad_usb/application.fam +++ b/applications/main/bad_kb/application.fam @@ -1,15 +1,15 @@ App( - appid="bad_usb", - name="Bad USB", + appid="bad_kb", + name="Bad KB", apptype=FlipperAppType.APP, - entry_point="bad_usb_app", - cdefines=["APP_BAD_USB"], + entry_point="bad_kb_app", + cdefines=["APP_BAD_KB"], requires=[ "gui", "dialogs", ], stack_size=2 * 1024, - icon="A_BadUsb_14", + icon="A_BadKb_14", order=70, fap_libs=["assets"], ) diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_kb/bad_kb_app.c similarity index 52% rename from applications/main/bad_usb/bad_usb_app.c rename to applications/main/bad_kb/bad_kb_app.c index 43ed76c37..c62af4733 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -1,5 +1,5 @@ -#include "bad_usb_app_i.h" -#include "bad_usb_settings_filename.h" +#include "bad_kb_app_i.h" +#include "bad_kb_settings_filename.h" #include #include #include @@ -8,29 +8,29 @@ #include #include -#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME +#define BAD_KB_SETTINGS_PATH BAD_KB_APP_BASE_FOLDER "/" BAD_KB_SETTINGS_FILE_NAME -static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { +static bool bad_kb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); - BadUsbApp* app = context; + BadKbApp* app = context; return scene_manager_handle_custom_event(app->scene_manager, event); } -static bool bad_usb_app_back_event_callback(void* context) { +static bool bad_kb_app_back_event_callback(void* context) { furi_assert(context); - BadUsbApp* app = context; + BadKbApp* app = context; return scene_manager_handle_back_event(app->scene_manager); } -static void bad_usb_app_tick_event_callback(void* context) { +static void bad_kb_app_tick_event_callback(void* context) { furi_assert(context); - BadUsbApp* app = context; + BadKbApp* app = context; scene_manager_handle_tick_event(app->scene_manager); } -static void bad_usb_load_settings(BadUsbApp* app) { +static void bad_kb_load_settings(BadKbApp* app) { File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { char chr; while((storage_file_read(settings_file, &chr, 1) == 1) && !storage_file_eof(settings_file) && !isspace(chr)) { @@ -41,9 +41,9 @@ static void bad_usb_load_settings(BadUsbApp* app) { storage_file_free(settings_file); } -static void bad_usb_save_settings(BadUsbApp* app) { +static void bad_kb_save_settings(BadKbApp* app) { File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + if(storage_file_open(settings_file, BAD_KB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { storage_file_write( settings_file, furi_string_get_cstr(app->keyboard_layout), @@ -54,21 +54,21 @@ static void bad_usb_save_settings(BadUsbApp* app) { storage_file_free(settings_file); } -void bad_usb_set_name(BadUsbApp* app, const char* fmt, ...) { +void bad_kb_set_name(BadKbApp* app, const char* fmt, ...) { furi_assert(app); va_list args; va_start(args, fmt); - vsnprintf(app->name, BAD_USB_ADV_NAME_MAX_LEN, fmt, args); + vsnprintf(app->name, BAD_KB_ADV_NAME_MAX_LEN, fmt, args); va_end(args); } -BadUsbApp* bad_usb_app_alloc(char* arg) { - BadUsbApp* app = malloc(sizeof(BadUsbApp)); +BadKbApp* bad_kb_app_alloc(char* arg) { + BadKbApp* app = malloc(sizeof(BadKbApp)); - app->bad_usb_script = NULL; + app->bad_kb_script = NULL; app->file_path = furi_string_alloc(); app->keyboard_layout = furi_string_alloc(); @@ -76,7 +76,7 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { furi_string_set(app->file_path, arg); } - bad_usb_load_settings(app); + bad_kb_load_settings(app); app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); @@ -85,97 +85,97 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { app->view_dispatcher = view_dispatcher_alloc(); view_dispatcher_enable_queue(app->view_dispatcher); - app->scene_manager = scene_manager_alloc(&bad_usb_scene_handlers, app); + app->scene_manager = scene_manager_alloc(&bad_kb_scene_handlers, app); view_dispatcher_set_event_callback_context(app->view_dispatcher, app); view_dispatcher_set_tick_event_callback( - app->view_dispatcher, bad_usb_app_tick_event_callback, 500); + app->view_dispatcher, bad_kb_app_tick_event_callback, 500); view_dispatcher_set_custom_event_callback( - app->view_dispatcher, bad_usb_app_custom_event_callback); + app->view_dispatcher, bad_kb_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, bad_usb_app_back_event_callback); + app->view_dispatcher, bad_kb_app_back_event_callback); Bt* bt = furi_record_open(RECORD_BT); app->bt = bt; const char* adv_name = bt_get_profile_adv_name(bt); - memcpy(app->name, adv_name, BAD_USB_ADV_NAME_MAX_LEN); - memcpy(app->bt_old_config.name, adv_name, BAD_USB_ADV_NAME_MAX_LEN); + memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); + memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); const uint8_t* mac_addr = bt_get_profile_mac_address(bt); - memcpy(app->mac, mac_addr, BAD_USB_MAC_ADDRESS_LEN); - memcpy(app->bt_old_config.mac, mac_addr, BAD_USB_MAC_ADDRESS_LEN); + memcpy(app->mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); + memcpy(app->bt_old_config.mac, mac_addr, BAD_KB_MAC_ADDRESS_LEN); // Custom Widget app->widget = widget_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); + app->view_dispatcher, BadKbAppViewError, widget_get_view(app->widget)); app->var_item_list_bt = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfigBt, variable_item_list_get_view(app->var_item_list_bt)); + app->view_dispatcher, BadKbAppViewConfigBt, variable_item_list_get_view(app->var_item_list_bt)); app->var_item_list_usb = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfigUsb, variable_item_list_get_view(app->var_item_list_usb)); + app->view_dispatcher, BadKbAppViewConfigUsb, variable_item_list_get_view(app->var_item_list_usb)); - app->bad_usb_view = bad_usb_alloc(); + app->bad_kb_view = bad_kb_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); + app->view_dispatcher, BadKbAppViewWork, bad_kb_get_view(app->bad_kb_view)); app->text_input = text_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfigName, text_input_get_view(app->text_input)); + app->view_dispatcher, BadKbAppViewConfigName, text_input_get_view(app->text_input)); app->byte_input = byte_input_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadUsbAppViewConfigMac, byte_input_get_view(app->byte_input)); + app->view_dispatcher, BadKbAppViewConfigMac, byte_input_get_view(app->byte_input)); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); if(furi_hal_usb_is_locked()) { - app->error = BadUsbAppErrorCloseRpc; - scene_manager_next_scene(app->scene_manager, BadUsbSceneError); + app->error = BadKbAppErrorCloseRpc; + scene_manager_next_scene(app->scene_manager, BadKbSceneError); } else { if(!furi_string_empty(app->file_path)) { - app->bad_usb_script = bad_usb_script_open(app->file_path, app->is_bt ? app->bt : NULL); - bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); - scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); + app->bad_kb_script = bad_kb_script_open(app->file_path, app->is_bt ? app->bt : NULL); + bad_kb_script_set_keyboard_layout(app->bad_kb_script, app->keyboard_layout); + scene_manager_next_scene(app->scene_manager, BadKbSceneWork); } else { - furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); - scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); + furi_string_set(app->file_path, BAD_KB_APP_BASE_FOLDER); + scene_manager_next_scene(app->scene_manager, BadKbSceneFileSelect); } } return app; } -void bad_usb_app_free(BadUsbApp* app) { +void bad_kb_app_free(BadKbApp* app) { furi_assert(app); - if(app->bad_usb_script) { - bad_usb_script_close(app->bad_usb_script); - app->bad_usb_script = NULL; + if(app->bad_kb_script) { + bad_kb_script_close(app->bad_kb_script); + app->bad_kb_script = NULL; } // Views - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); - bad_usb_free(app->bad_usb_view); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewWork); + bad_kb_free(app->bad_kb_view); // Custom Widget - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewError); widget_free(app->widget); // Variable item list - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigBt); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigBt); variable_item_list_free(app->var_item_list_bt); - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigUsb); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigUsb); variable_item_list_free(app->var_item_list_usb); // Text Input - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigName); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigName); text_input_free(app->text_input); // Byte Input - view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfigMac); + view_dispatcher_remove_view(app->view_dispatcher, BadKbAppViewConfigMac); byte_input_free(app->byte_input); // View dispatcher @@ -188,7 +188,7 @@ void bad_usb_app_free(BadUsbApp* app) { if (strcmp(app->bt_old_config.name, app->name) != 0) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); } - if (memcmp(app->bt_old_config.mac, app->mac, BAD_USB_MAC_ADDRESS_LEN) != 0) { + if (memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac); } @@ -199,7 +199,7 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close(RECORD_DIALOGS); furi_record_close(RECORD_BT); - bad_usb_save_settings(app); + bad_kb_save_settings(app); furi_string_free(app->file_path); furi_string_free(app->keyboard_layout); @@ -207,11 +207,11 @@ void bad_usb_app_free(BadUsbApp* app) { free(app); } -int32_t bad_usb_app(void* p) { - BadUsbApp* bad_usb_app = bad_usb_app_alloc((char*)p); +int32_t bad_kb_app(void* p) { + BadKbApp* bad_kb_app = bad_kb_app_alloc((char*)p); - view_dispatcher_run(bad_usb_app->view_dispatcher); + view_dispatcher_run(bad_kb_app->view_dispatcher); - bad_usb_app_free(bad_usb_app); + bad_kb_app_free(bad_kb_app); return 0; } diff --git a/applications/main/bad_kb/bad_kb_app.h b/applications/main/bad_kb/bad_kb_app.h new file mode 100644 index 000000000..e75a94651 --- /dev/null +++ b/applications/main/bad_kb/bad_kb_app.h @@ -0,0 +1,13 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct BadKbApp BadKbApp; + +void bad_kb_set_name(BadKbApp* app, const char* fmt, ...); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_kb/bad_kb_app_i.h b/applications/main/bad_kb/bad_kb_app_i.h new file mode 100644 index 000000000..913830e72 --- /dev/null +++ b/applications/main/bad_kb/bad_kb_app_i.h @@ -0,0 +1,80 @@ +#pragma once + +#include "bad_kb_app.h" +#include "scenes/bad_kb_scene.h" +#include "bad_kb_script.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "views/bad_kb_view.h" + +#define BAD_KB_APP_BASE_FOLDER ANY_PATH("badkb") +#define BAD_KB_APP_PATH_LAYOUT_FOLDER BAD_KB_APP_BASE_FOLDER "/layouts" +#define BAD_KB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_KB_APP_LAYOUT_EXTENSION ".kl" + +#define BAD_KB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro +#define BAD_KB_ADV_NAME_MAX_LEN 18 + +typedef enum { + BadKbAppErrorNoFiles, + BadKbAppErrorCloseRpc, +} BadKbAppError; + +typedef enum BadKbCustomEvent { + BadKbAppCustomEventTextEditResult, + BadKbAppCustomEventByteInputDone, + BadKbCustomEventErrorBack +} BadKbCustomEvent; + +typedef struct { + uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; + char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + + // number of bt keys before starting the app (all keys added in + // the bt keys file then will be removed) + uint16_t n_keys; +} BadKbBtConfig; + +struct BadKbApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + DialogsApp* dialogs; + Widget* widget; + VariableItemList* var_item_list_bt; + VariableItemList* var_item_list_usb; + + Bt* bt; + TextInput* text_input; + ByteInput* byte_input; + uint8_t mac[BAD_KB_MAC_ADDRESS_LEN]; + char name[BAD_KB_ADV_NAME_MAX_LEN + 1]; + BadKbBtConfig bt_old_config; + + BadKbAppError error; + FuriString* file_path; + FuriString* keyboard_layout; + BadKb* bad_kb_view; + BadKbScript* bad_kb_script; + + bool is_bt; +}; + +typedef enum { + BadKbAppViewError, + BadKbAppViewWork, + BadKbAppViewConfigBt, + BadKbAppViewConfigUsb, + BadKbAppViewConfigMac, + BadKbAppViewConfigName +} BadKbAppView; diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_kb/bad_kb_script.c similarity index 65% rename from applications/main/bad_usb/bad_usb_script.c rename to applications/main/bad_kb/bad_kb_script.c index 51ce49df9..e63a39c05 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -6,14 +6,14 @@ #include #include #include -#include "bad_usb_script.h" +#include "bad_kb_script.h" #include #include #define HID_BT_KEYS_STORAGE_PATH EXT_PATH("apps/Tools/.bt_hid.keys") -#define TAG "BadUSB" +#define TAG "BadKB" #define WORKER_TAG TAG "Worker" #define FILE_BUFFER_LEN 16 @@ -21,7 +21,7 @@ #define SCRIPT_STATE_END (-2) #define SCRIPT_STATE_NEXT_LINE (-3) -#define BADUSB_ASCII_TO_KEY(script, x) \ +#define BADKB_ASCII_TO_KEY(script, x) \ (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) typedef enum { @@ -52,9 +52,9 @@ const uint8_t bt_hid_delays[LevelRssiNum] = { 14, // LevelRssi39_0 }; -struct BadUsbScript { +struct BadKbScript { FuriHalUsbHidConfig hid_cfg; - BadUsbState st; + BadKbState st; FuriString* file_path; FuriString* keyboard_layout; uint32_t defdelay; @@ -230,8 +230,8 @@ static bool ducky_is_line_end(const char chr) { return ((chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n')); } -static void ducky_numlock_on(BadUsbScript* bad_usb) { - if (bad_usb->bt) { +static void ducky_numlock_on(BadKbScript* bad_kb) { + if (bad_kb->bt) { if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); @@ -246,12 +246,12 @@ static void ducky_numlock_on(BadUsbScript* bad_usb) { } } -static bool ducky_numpad_press(BadUsbScript* bad_usb, const char num) { +static bool ducky_numpad_press(BadKbScript* bad_kb, const char num) { if((num < '0') || (num > '9')) return false; uint16_t key = numpad_keys[num - '0']; FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); - if (bad_usb->bt) { + if (bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); @@ -264,13 +264,13 @@ static bool ducky_numpad_press(BadUsbScript* bad_usb, const char num) { return true; } -static bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) { +static bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { uint8_t i = 0; bool state = false; FURI_LOG_I(WORKER_TAG, "char %s", charcode); - if (bad_usb->bt) { + if (bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); } else { @@ -278,12 +278,12 @@ static bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) { } while(!ducky_is_line_end(charcode[i])) { - state = ducky_numpad_press(bad_usb, charcode[i]); + state = ducky_numpad_press(bad_kb, charcode[i]); if(state == false) break; i++; } - if (bad_usb->bt) { + if (bad_kb->bt) { furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); } else { furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); @@ -291,7 +291,7 @@ static bool ducky_altchar(BadUsbScript* bad_usb, const char* charcode) { return state; } -static bool ducky_altstring(BadUsbScript* bad_usb, const char* param) { +static bool ducky_altstring(BadKbScript* bad_kb, const char* param) { uint32_t i = 0; bool state = false; @@ -304,19 +304,19 @@ static bool ducky_altstring(BadUsbScript* bad_usb, const char* param) { char temp_str[4]; snprintf(temp_str, 4, "%u", param[i]); - state = ducky_altchar(bad_usb, temp_str); + state = ducky_altchar(bad_kb, temp_str); if(state == false) break; i++; } return state; } -static bool ducky_string(BadUsbScript* bad_usb, const char* param) { +static bool ducky_string(BadKbScript* bad_kb, const char* param) { uint32_t i = 0; while(param[i] != '\0') { - uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); + uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); if(keycode != HID_KEYBOARD_NONE) { - if (bad_usb->bt) { + if (bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(keycode); furi_delay_ms(bt_timeout); @@ -331,7 +331,7 @@ static bool ducky_string(BadUsbScript* bad_usb, const char* param) { return true; } -static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { +static uint16_t ducky_get_keycode(BadKbScript* bad_kb, const char* param, bool accept_chars) { for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && @@ -340,13 +340,13 @@ static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool } } if((accept_chars) && (strlen(param) > 0)) { - return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF); + return (BADKB_ASCII_TO_KEY(bad_kb, param[0]) & 0xFF); } return 0; } static int32_t - ducky_parse_line(BadUsbScript* bad_usb, FuriString* line, char* error, size_t error_len) { + ducky_parse_line(BadKbScript* bad_kb, FuriString* line, char* error, size_t error_len) { uint32_t line_len = furi_string_size(line); const char* line_tmp = furi_string_get_cstr(line); bool state = false; @@ -384,7 +384,7 @@ static int32_t (strncmp(line_tmp, ducky_cmd_defdelay_2, strlen(ducky_cmd_defdelay_2)) == 0)) { // DEFAULT_DELAY line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_usb->defdelay); + state = ducky_get_number(line_tmp, &bad_kb->defdelay); if(!state && error != NULL) { snprintf(error, error_len, "Invalid number %s", line_tmp); } @@ -392,7 +392,7 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_string(bad_usb, line_tmp); + state = ducky_string(bad_kb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid string %s", line_tmp); } @@ -400,8 +400,8 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_altchar, strlen(ducky_cmd_altchar)) == 0) { // ALTCHAR line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(bad_usb); - state = ducky_altchar(bad_usb, line_tmp); + ducky_numlock_on(bad_kb); + state = ducky_altchar(bad_kb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid altchar %s", line_tmp); } @@ -411,8 +411,8 @@ static int32_t (strncmp(line_tmp, ducky_cmd_altstr_2, strlen(ducky_cmd_altstr_2)) == 0)) { // ALTSTRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - ducky_numlock_on(bad_usb); - state = ducky_altstring(bad_usb, line_tmp); + ducky_numlock_on(bad_kb); + state = ducky_altstring(bad_kb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid altstring %s", line_tmp); } @@ -420,7 +420,7 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_repeat, strlen(ducky_cmd_repeat)) == 0) { // REPEAT line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_get_number(line_tmp, &bad_usb->repeat_cnt); + state = ducky_get_number(line_tmp, &bad_kb->repeat_cnt); if(!state && error != NULL) { snprintf(error, error_len, "Invalid number %s", line_tmp); } @@ -428,8 +428,8 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true); - if (bad_usb->bt) { + uint16_t key = ducky_get_keycode(bad_kb, line_tmp, true); + if (bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); @@ -444,7 +444,7 @@ static int32_t return (0); } else { // Special keys + modifiers - uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false); + uint16_t key = ducky_get_keycode(bad_kb, line_tmp, false); if(key == HID_KEYBOARD_NONE) { if(error != NULL) { snprintf(error, error_len, "No keycode defined for %s", line_tmp); @@ -454,9 +454,9 @@ static int32_t if((key & 0xFF00) != 0) { // It's a modifier key line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(bad_usb, line_tmp, true); + key |= ducky_get_keycode(bad_kb, line_tmp, true); } - if (bad_usb->bt) { + if (bad_kb->bt) { furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); @@ -468,231 +468,231 @@ static int32_t } } -static bool ducky_set_usb_id(BadUsbScript* bad_usb, const char* line) { - if(sscanf(line, "%lX:%lX", &bad_usb->hid_cfg.vid, &bad_usb->hid_cfg.pid) == 2) { - bad_usb->hid_cfg.manuf[0] = '\0'; - bad_usb->hid_cfg.product[0] = '\0'; +static bool ducky_set_usb_id(BadKbScript* bad_kb, const char* line) { + if(sscanf(line, "%lX:%lX", &bad_kb->hid_cfg.vid, &bad_kb->hid_cfg.pid) == 2) { + bad_kb->hid_cfg.manuf[0] = '\0'; + bad_kb->hid_cfg.product[0] = '\0'; uint8_t id_len = ducky_get_command_len(line); if(!ducky_is_line_end(line[id_len + 1])) { sscanf( &line[id_len + 1], "%31[^\r\n:]:%31[^\r\n]", - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); + bad_kb->hid_cfg.manuf, + bad_kb->hid_cfg.product); } FURI_LOG_D( WORKER_TAG, "set id: %04lX:%04lX mfr:%s product:%s", - bad_usb->hid_cfg.vid, - bad_usb->hid_cfg.pid, - bad_usb->hid_cfg.manuf, - bad_usb->hid_cfg.product); + bad_kb->hid_cfg.vid, + bad_kb->hid_cfg.pid, + bad_kb->hid_cfg.manuf, + bad_kb->hid_cfg.product); return true; } return false; } -static bool ducky_script_preload(BadUsbScript* bad_usb, File* script_file) { +static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { uint8_t ret = 0; uint32_t line_len = 0; - furi_string_reset(bad_usb->line); + furi_string_reset(bad_kb->line); do { - ret = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + ret = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); for(uint16_t i = 0; i < ret; i++) { - if(bad_usb->file_buf[i] == '\n' && line_len > 0) { - bad_usb->st.line_nb++; + if(bad_kb->file_buf[i] == '\n' && line_len > 0) { + bad_kb->st.line_nb++; line_len = 0; } else { - if(bad_usb->st.line_nb == 0) { // Save first line - furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + if(bad_kb->st.line_nb == 0) { // Save first line + furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); } line_len++; } } if(storage_file_eof(script_file)) { if(line_len > 0) { - bad_usb->st.line_nb++; + bad_kb->st.line_nb++; break; } } } while(ret > 0); - if (!bad_usb->bt) { - const char* line_tmp = furi_string_get_cstr(bad_usb->line); + if (!bad_kb->bt) { + const char* line_tmp = furi_string_get_cstr(bad_kb->line); bool id_set = false; // Looking for ID command at first line if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { - id_set = ducky_set_usb_id(bad_usb, &line_tmp[strlen(ducky_cmd_id) + 1]); + id_set = ducky_set_usb_id(bad_kb, &line_tmp[strlen(ducky_cmd_id) + 1]); } if(id_set) { - furi_check(furi_hal_usb_set_config(&usb_hid, &bad_usb->hid_cfg)); + furi_check(furi_hal_usb_set_config(&usb_hid, &bad_kb->hid_cfg)); } else { furi_check(furi_hal_usb_set_config(&usb_hid, NULL)); } } storage_file_seek(script_file, 0, true); - furi_string_reset(bad_usb->line); + furi_string_reset(bad_kb->line); return true; } -static int32_t ducky_script_execute_next(BadUsbScript* bad_usb, File* script_file) { +static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) { int32_t delay_val = 0; - if(bad_usb->repeat_cnt > 0) { - bad_usb->repeat_cnt--; + if(bad_kb->repeat_cnt > 0) { + bad_kb->repeat_cnt--; delay_val = ducky_parse_line( - bad_usb, bad_usb->line_prev, bad_usb->st.error, sizeof(bad_usb->st.error)); + bad_kb, bad_kb->line_prev, bad_kb->st.error, sizeof(bad_kb->st.error)); if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line return 0; } else if(delay_val < 0) { // Script error - bad_usb->st.error_line = bad_usb->st.line_cur - 1; - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur - 1U); + bad_kb->st.error_line = bad_kb->st.line_cur - 1; + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_kb->st.line_cur - 1U); return SCRIPT_STATE_ERROR; } else { - return (delay_val + bad_usb->defdelay); + return (delay_val + bad_kb->defdelay); } } - furi_string_set(bad_usb->line_prev, bad_usb->line); - furi_string_reset(bad_usb->line); + furi_string_set(bad_kb->line_prev, bad_kb->line); + furi_string_reset(bad_kb->line); while(1) { - if(bad_usb->buf_len == 0) { - bad_usb->buf_len = storage_file_read(script_file, bad_usb->file_buf, FILE_BUFFER_LEN); + if(bad_kb->buf_len == 0) { + bad_kb->buf_len = storage_file_read(script_file, bad_kb->file_buf, FILE_BUFFER_LEN); if(storage_file_eof(script_file)) { - if((bad_usb->buf_len < FILE_BUFFER_LEN) && (bad_usb->file_end == false)) { - bad_usb->file_buf[bad_usb->buf_len] = '\n'; - bad_usb->buf_len++; - bad_usb->file_end = true; + if((bad_kb->buf_len < FILE_BUFFER_LEN) && (bad_kb->file_end == false)) { + bad_kb->file_buf[bad_kb->buf_len] = '\n'; + bad_kb->buf_len++; + bad_kb->file_end = true; } } - bad_usb->buf_start = 0; - if(bad_usb->buf_len == 0) return SCRIPT_STATE_END; + bad_kb->buf_start = 0; + if(bad_kb->buf_len == 0) return SCRIPT_STATE_END; } - for(uint8_t i = bad_usb->buf_start; i < (bad_usb->buf_start + bad_usb->buf_len); i++) { - if(bad_usb->file_buf[i] == '\n' && furi_string_size(bad_usb->line) > 0) { - bad_usb->st.line_cur++; - bad_usb->buf_len = bad_usb->buf_len + bad_usb->buf_start - (i + 1); - bad_usb->buf_start = i + 1; - furi_string_trim(bad_usb->line); + for(uint8_t i = bad_kb->buf_start; i < (bad_kb->buf_start + bad_kb->buf_len); i++) { + if(bad_kb->file_buf[i] == '\n' && furi_string_size(bad_kb->line) > 0) { + bad_kb->st.line_cur++; + bad_kb->buf_len = bad_kb->buf_len + bad_kb->buf_start - (i + 1); + bad_kb->buf_start = i + 1; + furi_string_trim(bad_kb->line); delay_val = ducky_parse_line( - bad_usb, bad_usb->line, bad_usb->st.error, sizeof(bad_usb->st.error)); + bad_kb, bad_kb->line, bad_kb->st.error, sizeof(bad_kb->st.error)); if(delay_val == SCRIPT_STATE_NEXT_LINE) { // Empty line return 0; } else if(delay_val < 0) { - bad_usb->st.error_line = bad_usb->st.line_cur; + bad_kb->st.error_line = bad_kb->st.line_cur; if(delay_val == SCRIPT_STATE_NEXT_LINE) { snprintf( - bad_usb->st.error, sizeof(bad_usb->st.error), "Forbidden empty line"); + bad_kb->st.error, sizeof(bad_kb->st.error), "Forbidden empty line"); FURI_LOG_E( - WORKER_TAG, "Forbidden empty line at line %u", bad_usb->st.line_cur); + WORKER_TAG, "Forbidden empty line at line %u", bad_kb->st.line_cur); } else { - FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_usb->st.line_cur); + FURI_LOG_E(WORKER_TAG, "Unknown command at line %u", bad_kb->st.line_cur); } return SCRIPT_STATE_ERROR; } else { - return (delay_val + bad_usb->defdelay); + return (delay_val + bad_kb->defdelay); } } else { - furi_string_push_back(bad_usb->line, bad_usb->file_buf[i]); + furi_string_push_back(bad_kb->line, bad_kb->file_buf[i]); } } - bad_usb->buf_len = 0; - if(bad_usb->file_end) return SCRIPT_STATE_END; + bad_kb->buf_len = 0; + if(bad_kb->file_end) return SCRIPT_STATE_END; } return 0; } -static void bad_usb_bt_hid_state_callback(BtStatus status, void* context) { +static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { furi_assert(context); - BadUsbScript* bad_usb = (BadUsbScript*)context; + BadKbScript* bad_kb = (BadKbScript*)context; bool state = (status == BtStatusConnected); if(state == true) { - LevelRssiRange r = bt_remote_rssi_range(bad_usb->bt); + LevelRssiRange r = bt_remote_rssi_range(bad_kb->bt); if(r != LevelRssiError) { bt_timeout = bt_hid_delays[r]; } - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); } else - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } -static void bad_usb_usb_hid_state_callback(bool state, void* context) { +static void bad_kb_usb_hid_state_callback(bool state, void* context) { furi_assert(context); - BadUsbScript* bad_usb = context; + BadKbScript* bad_kb = context; if(state == true) - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtConnect); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); else - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtDisconnect); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } -static int32_t bad_usb_worker(void* context) { - BadUsbScript* bad_usb = context; +static int32_t bad_kb_worker(void* context) { + BadKbScript* bad_kb = context; - BadUsbWorkerState worker_state = BadUsbStateInit; + BadKbWorkerState worker_state = BadKbStateInit; int32_t delay_val = 0; FuriHalUsbInterface* usb_mode_prev = NULL; GapPairing old_pairing_method = GapPairingNone; - if (bad_usb->bt) { + if (bad_kb->bt) { bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(bad_usb->bt); + bt_disconnect(bad_kb->bt); furi_delay_ms(200); - bt_keys_storage_set_storage_path(bad_usb->bt, HID_BT_KEYS_STORAGE_PATH); - if (!bt_set_profile(bad_usb->bt, BtProfileHidKeyboard)) { + bt_keys_storage_set_storage_path(bad_kb->bt, HID_BT_KEYS_STORAGE_PATH); + if (!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); return -1; } - old_pairing_method = bt_get_profile_pairing_method(bad_usb->bt); - bt_set_profile_pairing_method(bad_usb->bt, GapPairingNone); + old_pairing_method = bt_get_profile_pairing_method(bad_kb->bt); + bt_set_profile_pairing_method(bad_kb->bt, GapPairingNone); furi_hal_bt_start_advertising(); - bt_set_status_changed_callback(bad_usb->bt, bad_usb_bt_hid_state_callback, bad_usb); + bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } else { usb_mode_prev = furi_hal_usb_get_config(); - furi_hal_hid_set_state_callback(bad_usb_usb_hid_state_callback, bad_usb); + furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); } FURI_LOG_I(WORKER_TAG, "Init"); File* script_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - bad_usb->line = furi_string_alloc(); - bad_usb->line_prev = furi_string_alloc(); + bad_kb->line = furi_string_alloc(); + bad_kb->line_prev = furi_string_alloc(); while(1) { - if(worker_state == BadUsbStateInit) { // State: initialization + if(worker_state == BadKbStateInit) { // State: initialization if(storage_file_open( script_file, - furi_string_get_cstr(bad_usb->file_path), + furi_string_get_cstr(bad_kb->file_path), FSAM_READ, FSOM_OPEN_EXISTING)) { - if((ducky_script_preload(bad_usb, script_file)) && (bad_usb->st.line_nb > 0)) { - if (bad_usb->bt) { - worker_state = BadUsbStateNotConnected; // Ready to run + if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { + if (bad_kb->bt) { + worker_state = BadKbStateNotConnected; // Ready to run } else { if(furi_hal_hid_is_connected()) { - worker_state = BadUsbStateIdle; // Ready to run + worker_state = BadKbStateIdle; // Ready to run } else { - worker_state = BadUsbStateNotConnected; // USB not connected + worker_state = BadKbStateNotConnected; // USB not connected } } } else { - worker_state = BadUsbStateScriptError; // Script preload error + worker_state = BadKbStateScriptError; // Script preload error } } else { FURI_LOG_E(WORKER_TAG, "File open error"); - worker_state = BadUsbStateFileError; // File open error + worker_state = BadKbStateFileError; // File open error } - bad_usb->st.state = worker_state; + bad_kb->st.state = worker_state; - } else if(worker_state == BadUsbStateNotConnected) { // State: USB not connected + } else if(worker_state == BadKbStateNotConnected) { // State: USB not connected uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriFlagWaitAny, @@ -701,13 +701,13 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { - worker_state = BadUsbStateIdle; // Ready to run + worker_state = BadKbStateIdle; // Ready to run } else if(flags & WorkerEvtToggle) { - worker_state = BadUsbStateWillRun; // Will run when USB is connected + worker_state = BadKbStateWillRun; // Will run when USB is connected } - bad_usb->st.state = worker_state; + bad_kb->st.state = worker_state; - } else if(worker_state == BadUsbStateIdle) { // State: ready to start + } else if(worker_state == BadKbStateIdle) { // State: ready to start uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, @@ -716,22 +716,22 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtToggle) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + DOLPHIN_DEED(DolphinDeedBadKbPlayScript); delay_val = 0; - bad_usb->buf_len = 0; - bad_usb->st.line_cur = 0; - bad_usb->defdelay = 0; - bad_usb->repeat_cnt = 0; - bad_usb->file_end = false; + bad_kb->buf_len = 0; + bad_kb->st.line_cur = 0; + bad_kb->defdelay = 0; + bad_kb->repeat_cnt = 0; + bad_kb->file_end = false; storage_file_seek(script_file, 0, true); - bad_usb_script_set_keyboard_layout(bad_usb, bad_usb->keyboard_layout); - worker_state = BadUsbStateRunning; + bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); + worker_state = BadKbStateRunning; } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected + worker_state = BadKbStateNotConnected; // USB disconnected } - bad_usb->st.state = worker_state; + bad_kb->st.state = worker_state; - } else if(worker_state == BadUsbStateWillRun) { // State: start on connection + } else if(worker_state == BadKbStateWillRun) { // State: start on connection uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriFlagWaitAny, @@ -740,28 +740,28 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtConnect) { // Start executing script - DOLPHIN_DEED(DolphinDeedBadUsbPlayScript); + DOLPHIN_DEED(DolphinDeedBadKbPlayScript); delay_val = 0; - bad_usb->buf_len = 0; - bad_usb->st.line_cur = 0; - bad_usb->defdelay = 0; - bad_usb->repeat_cnt = 0; - bad_usb->file_end = false; + bad_kb->buf_len = 0; + bad_kb->st.line_cur = 0; + bad_kb->defdelay = 0; + bad_kb->repeat_cnt = 0; + bad_kb->file_end = false; storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - if (bad_usb->bt) { - update_bt_timeout(bad_usb->bt); + if (bad_kb->bt) { + update_bt_timeout(bad_kb->bt); FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } - bad_usb_script_set_keyboard_layout(bad_usb, bad_usb->keyboard_layout); - worker_state = BadUsbStateRunning; + bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); + worker_state = BadKbStateRunning; } else if(flags & WorkerEvtToggle) { // Cancel scheduled execution - worker_state = BadUsbStateNotConnected; + worker_state = BadKbStateNotConnected; } - bad_usb->st.state = worker_state; + bad_kb->st.state = worker_state; - } else if(worker_state == BadUsbStateRunning) { // State: running + } else if(worker_state == BadKbStateRunning) { // State: running uint16_t delay_cur = (delay_val > 1000) ? (1000) : (delay_val); uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtToggle | WorkerEvtDisconnect, FuriFlagWaitAny, delay_cur); @@ -770,56 +770,56 @@ static int32_t bad_usb_worker(void* context) { if(flags & WorkerEvtEnd) { break; } else if(flags & WorkerEvtToggle) { - worker_state = BadUsbStateIdle; // Stop executing script - if (bad_usb->bt) { + worker_state = BadKbStateIdle; // Stop executing script + if (bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); } } else if(flags & WorkerEvtDisconnect) { - worker_state = BadUsbStateNotConnected; // USB disconnected - if (bad_usb->bt) { + worker_state = BadKbStateNotConnected; // USB disconnected + if (bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); } } - bad_usb->st.state = worker_state; + bad_kb->st.state = worker_state; continue; } else if( (flags == (unsigned)FuriFlagErrorTimeout) || (flags == (unsigned)FuriFlagErrorResource)) { if(delay_val > 0) { - bad_usb->st.delay_remain--; + bad_kb->st.delay_remain--; continue; } - bad_usb->st.state = BadUsbStateRunning; - delay_val = ducky_script_execute_next(bad_usb, script_file); + bad_kb->st.state = BadKbStateRunning; + delay_val = ducky_script_execute_next(bad_kb, script_file); if(delay_val == SCRIPT_STATE_ERROR) { // Script error delay_val = 0; - worker_state = BadUsbStateScriptError; - bad_usb->st.state = worker_state; + worker_state = BadKbStateScriptError; + bad_kb->st.state = worker_state; } else if(delay_val == SCRIPT_STATE_END) { // End of script delay_val = 0; - worker_state = BadUsbStateIdle; - bad_usb->st.state = BadUsbStateDone; - if (bad_usb->bt) { + worker_state = BadKbStateIdle; + bad_kb->st.state = BadKbStateDone; + if (bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); } continue; } else if(delay_val > 1000) { - bad_usb->st.state = BadUsbStateDelay; // Show long delays - bad_usb->st.delay_remain = delay_val / 1000; + bad_kb->st.state = BadKbStateDelay; // Show long delays + bad_kb->st.delay_remain = delay_val / 1000; } } else { furi_check((flags & FuriFlagError) == 0); } } else if( - (worker_state == BadUsbStateFileError) || - (worker_state == BadUsbStateScriptError)) { // State: error + (worker_state == BadKbStateFileError) || + (worker_state == BadKbStateScriptError)) { // State: error uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd, FuriFlagWaitAny, FuriWaitForever); // Waiting for exit command furi_check((flags & FuriFlagError) == 0); @@ -827,31 +827,31 @@ static int32_t bad_usb_worker(void* context) { break; } } - if (bad_usb->bt) { - update_bt_timeout(bad_usb->bt); + if (bad_kb->bt) { + update_bt_timeout(bad_kb->bt); FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } } - if (bad_usb->bt) { + if (bad_kb->bt) { // release all keys bt_hid_hold_while_keyboard_buffer_full(6, 3000); // stop ble - bt_set_status_changed_callback(bad_usb->bt, NULL, NULL); + bt_set_status_changed_callback(bad_kb->bt, NULL, NULL); - bt_disconnect(bad_usb->bt); + bt_disconnect(bad_kb->bt); // Wait 2nd core to update nvm storage furi_delay_ms(200); - bt_keys_storage_set_default_path(bad_usb->bt); + bt_keys_storage_set_default_path(bad_kb->bt); - bt_set_profile_pairing_method(bad_usb->bt, old_pairing_method); + bt_set_profile_pairing_method(bad_kb->bt, old_pairing_method); // fails if ble radio stack isn't ready when switching profile // if it happens, maybe we should increase the delay after bt_disconnect - bt_set_profile(bad_usb->bt, BtProfileSerial); + bt_set_profile(bad_kb->bt, BtProfileSerial); } else { furi_hal_hid_set_state_callback(NULL, NULL); @@ -860,82 +860,82 @@ static int32_t bad_usb_worker(void* context) { storage_file_close(script_file); storage_file_free(script_file); - furi_string_free(bad_usb->line); - furi_string_free(bad_usb->line_prev); + furi_string_free(bad_kb->line); + furi_string_free(bad_kb->line_prev); FURI_LOG_I(WORKER_TAG, "End"); return 0; } -static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - furi_string_set_str(bad_usb->keyboard_layout, ""); - memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); - memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +static void bad_kb_script_set_default_keyboard_layout(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_string_set_str(bad_kb->keyboard_layout, ""); + memset(bad_kb->layout, HID_KEYBOARD_NONE, sizeof(bad_kb->layout)); + memcpy(bad_kb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_kb->layout))); } -BadUsbScript* bad_usb_script_open(FuriString* file_path, Bt* bt) { +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt) { furi_assert(file_path); - BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); - bad_usb->file_path = furi_string_alloc(); - furi_string_set(bad_usb->file_path, file_path); - bad_usb->keyboard_layout = furi_string_alloc(); - bad_usb_script_set_default_keyboard_layout(bad_usb); + BadKbScript* bad_kb = malloc(sizeof(BadKbScript)); + bad_kb->file_path = furi_string_alloc(); + furi_string_set(bad_kb->file_path, file_path); + bad_kb->keyboard_layout = furi_string_alloc(); + bad_kb_script_set_default_keyboard_layout(bad_kb); - bad_usb->st.state = BadUsbStateInit; - bad_usb->st.error[0] = '\0'; + bad_kb->st.state = BadKbStateInit; + bad_kb->st.error[0] = '\0'; - bad_usb->bt = bt; + bad_kb->bt = bt; - bad_usb->thread = furi_thread_alloc_ex("BadUsbWorker", 2048, bad_usb_worker, bad_usb); - furi_thread_start(bad_usb->thread); - return bad_usb; + bad_kb->thread = furi_thread_alloc_ex("BadKbWorker", 2048, bad_kb_worker, bad_kb); + furi_thread_start(bad_kb->thread); + return bad_kb; } //-V773 -void bad_usb_script_close(BadUsbScript* bad_usb) { - furi_assert(bad_usb); +void bad_kb_script_close(BadKbScript* bad_kb) { + furi_assert(bad_kb); furi_record_close(RECORD_STORAGE); - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtEnd); - furi_thread_join(bad_usb->thread); - furi_thread_free(bad_usb->thread); - furi_string_free(bad_usb->file_path); - furi_string_free(bad_usb->keyboard_layout); - free(bad_usb); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtEnd); + furi_thread_join(bad_kb->thread); + furi_thread_free(bad_kb->thread); + furi_string_free(bad_kb->file_path); + furi_string_free(bad_kb->keyboard_layout); + free(bad_kb); } -void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { - furi_assert(bad_usb); +void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path) { + furi_assert(bad_kb); - if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + if((bad_kb->st.state == BadKbStateRunning) || (bad_kb->st.state == BadKbStateDelay)) { // do not update keyboard layout while a script is running return; } File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); if(!furi_string_empty(layout_path)) { - furi_string_set(bad_usb->keyboard_layout, layout_path); + furi_string_set(bad_kb->keyboard_layout, layout_path); if(storage_file_open( layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint16_t layout[128]; if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { - memcpy(bad_usb->layout, layout, sizeof(layout)); + memcpy(bad_kb->layout, layout, sizeof(layout)); } } storage_file_close(layout_file); } else { - bad_usb_script_set_default_keyboard_layout(bad_usb); + bad_kb_script_set_default_keyboard_layout(bad_kb); } storage_file_free(layout_file); } -void bad_usb_script_toggle(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); +void bad_kb_script_toggle(BadKbScript* bad_kb) { + furi_assert(bad_kb); + furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtToggle); } -BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb) { - furi_assert(bad_usb); - return &(bad_usb->st); +BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb) { + furi_assert(bad_kb); + return &(bad_kb->st); } diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h new file mode 100644 index 000000000..35c57c112 --- /dev/null +++ b/applications/main/bad_kb/bad_kb_script.h @@ -0,0 +1,49 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct BadKbScript BadKbScript; + +typedef enum { + BadKbStateInit, + BadKbStateNotConnected, + BadKbStateIdle, + BadKbStateWillRun, + BadKbStateRunning, + BadKbStateDelay, + BadKbStateDone, + BadKbStateScriptError, + BadKbStateFileError, +} BadKbWorkerState; + +typedef struct { + BadKbWorkerState state; + uint16_t line_cur; + uint16_t line_nb; + uint32_t delay_remain; + uint16_t error_line; + char error[64]; +} BadKbState; + +BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt); + +void bad_kb_script_close(BadKbScript* bad_kb); + +void bad_kb_script_set_keyboard_layout(BadKbScript* bad_kb, FuriString* layout_path); + +void bad_kb_script_start(BadKbScript* bad_kb); + +void bad_kb_script_stop(BadKbScript* bad_kb); + +void bad_kb_script_toggle(BadKbScript* bad_kb); + +BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/bad_kb/bad_kb_settings_filename.h b/applications/main/bad_kb/bad_kb_settings_filename.h new file mode 100644 index 000000000..3eb7d3c0a --- /dev/null +++ b/applications/main/bad_kb/bad_kb_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_KB_SETTINGS_FILE_NAME ".badkb.settings" diff --git a/applications/main/bad_kb/scenes/bad_kb_scene.c b/applications/main/bad_kb/scenes/bad_kb_scene.c new file mode 100644 index 000000000..f90d23a77 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene.c @@ -0,0 +1,30 @@ +#include "bad_kb_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const bad_kb_scene_on_enter_handlers[])(void*) = { +#include "bad_kb_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 bad_kb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "bad_kb_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 bad_kb_scene_on_exit_handlers[])(void* context) = { +#include "bad_kb_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers bad_kb_scene_handlers = { + .on_enter_handlers = bad_kb_scene_on_enter_handlers, + .on_event_handlers = bad_kb_scene_on_event_handlers, + .on_exit_handlers = bad_kb_scene_on_exit_handlers, + .scene_num = BadKbSceneNum, +}; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene.h b/applications/main/bad_kb/scenes/bad_kb_scene.h similarity index 68% rename from applications/main/bad_usb/scenes/bad_usb_scene.h rename to applications/main/bad_kb/scenes/bad_kb_scene.h index 68a753210..82db02873 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene.h +++ b/applications/main/bad_kb/scenes/bad_kb_scene.h @@ -3,27 +3,27 @@ #include // Generate scene id and total number -#define ADD_SCENE(prefix, name, id) BadUsbScene##id, +#define ADD_SCENE(prefix, name, id) BadKbScene##id, typedef enum { -#include "bad_usb_scene_config.h" - BadUsbSceneNum, -} BadUsbScene; +#include "bad_kb_scene_config.h" + BadKbSceneNum, +} BadKbScene; #undef ADD_SCENE -extern const SceneManagerHandlers bad_usb_scene_handlers; +extern const SceneManagerHandlers bad_kb_scene_handlers; // Generate scene on_enter handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "bad_usb_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE // Generate scene on_event handlers declaration #define ADD_SCENE(prefix, name, id) \ bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); -#include "bad_usb_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE // Generate scene on_exit handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); -#include "bad_usb_scene_config.h" +#include "bad_kb_scene_config.h" #undef ADD_SCENE diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config.h b/applications/main/bad_kb/scenes/bad_kb_scene_config.h new file mode 100644 index 000000000..794468eba --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config.h @@ -0,0 +1,8 @@ +ADD_SCENE(bad_kb, file_select, FileSelect) +ADD_SCENE(bad_kb, work, Work) +ADD_SCENE(bad_kb, error, Error) +ADD_SCENE(bad_kb, config_bt, ConfigBt) +ADD_SCENE(bad_kb, config_usb, ConfigUsb) +ADD_SCENE(bad_kb, config_layout, ConfigLayout) +ADD_SCENE(bad_kb, config_name, ConfigName) +ADD_SCENE(bad_kb, config_mac, ConfigMac) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c new file mode 100644 index 000000000..fc1d19e76 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -0,0 +1,84 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum VarItemListIndex { + VarItemListIndexConnection, + VarItemListIndexKeyboardLayout, + VarItemListIndexAdvertisementName, + VarItemListIndexMacAddress, +}; + +void bad_kb_scene_config_bt_connection_callback(VariableItem* item) { + BadKbApp* bad_kb = variable_item_get_context(item); + bad_kb->is_bt = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); +} + +void bad_kb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) { + BadKbApp* bad_kb = context; + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index); +} + +void bad_kb_scene_config_bt_on_enter(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list_bt; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_kb_scene_config_bt_connection_callback, bad_kb); + variable_item_set_current_value_index(item, bad_kb->is_bt); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + + item = variable_item_list_add( + var_item_list, "Keyboard layout", 0, NULL, bad_kb); + + item = variable_item_list_add( + var_item_list, "Change adv name", 0, NULL, bad_kb); + + item = variable_item_list_add( + var_item_list, "Change MAC address", 0, NULL, bad_kb); + + variable_item_list_set_enter_callback(var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigBt); +} + +bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfigBt, event.event); + consumed = true; + if(event.event == VarItemListIndexKeyboardLayout) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); + } else if(event.event == VarItemListIndexConnection) { + bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + scene_manager_previous_scene(bad_kb->scene_manager); + if (bad_kb->is_bt) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); + } else { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); + } + } else if(event.event == VarItemListIndexAdvertisementName) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); + } else if(event.event == VarItemListIndexMacAddress) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); + // } else { + // furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_kb_scene_config_bt_on_exit(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list_bt; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c new file mode 100644 index 000000000..006ad31bd --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -0,0 +1,48 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_kb_layout_select(BadKbApp* bad_kb) { + furi_assert(bad_kb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_kb->keyboard_layout)) { + furi_string_set(predefined_path, bad_kb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_KB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_kb->dialogs, bad_kb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_kb_scene_config_layout_on_enter(void* context) { + BadKbApp* bad_kb = context; + + if(bad_kb_layout_select(bad_kb)) { + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + } + scene_manager_previous_scene(bad_kb->scene_manager); +} + +bool bad_kb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadKbApp* bad_kb = context; + return false; +} + +void bad_kb_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadKbApp* bad_kb = context; +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c new file mode 100644 index 000000000..0dc4be10a --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_mac.c @@ -0,0 +1,57 @@ +#include "../bad_kb_app_i.h" + +#define TAG "BadKbConfigMac" + +static uint8_t* reverse_mac_addr(uint8_t* mac) { + uint8_t tmp; + for(int i = 0; i < 3; i++) { + tmp = mac[i]; + mac[i] = mac[5 - i]; + mac[5 - i] = tmp; + } + return mac; +} + +void bad_kb_scene_config_mac_byte_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventByteInputDone); +} + +void bad_kb_scene_config_mac_on_enter(void* context) { + BadKbApp* bad_kb = context; + + // Setup view + ByteInput* byte_input = bad_kb->byte_input; + byte_input_set_header_text(byte_input, "Enter new MAC address"); + byte_input_set_result_callback( + byte_input, + bad_kb_scene_config_mac_byte_input_callback, + NULL, + bad_kb, + reverse_mac_addr(bad_kb->mac), + GAP_MAC_ADDR_SIZE); + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigMac); +} + +bool bad_kb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == BadKbAppCustomEventByteInputDone) { + bt_set_profile_mac_address(bad_kb->bt, reverse_mac_addr(bad_kb->mac)); + scene_manager_previous_scene(bad_kb->scene_manager); + consumed = true; + } + } + return consumed; +} + +void bad_kb_scene_config_mac_on_exit(void* context) { + BadKbApp* bad_kb = context; + + // Clear view + byte_input_set_result_callback(bad_kb->byte_input, NULL, NULL, NULL, NULL, 0); + byte_input_set_header_text(bad_kb->byte_input, ""); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c new file mode 100644 index 000000000..d3d7628b1 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -0,0 +1,46 @@ +#include "../bad_kb_app_i.h" + +static void bad_kb_scene_config_name_text_input_callback(void* context) { + BadKbApp* bad_kb = context; + + view_dispatcher_send_custom_event( + bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult); +} + +void bad_kb_scene_config_name_on_enter(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_set_header_text(text_input, "Set BLE adv name"); + + text_input_set_result_callback( + text_input, + bad_kb_scene_config_name_text_input_callback, + bad_kb, + bad_kb->name, + BAD_KB_ADV_NAME_MAX_LEN, + true); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigName); +} + +bool bad_kb_scene_config_name_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + consumed = true; + if(event.event == BadKbAppCustomEventTextEditResult) { + bt_set_profile_adv_name(bad_kb->bt, bad_kb->name); + } + scene_manager_previous_scene(bad_kb->scene_manager); + } + return consumed; +} + +void bad_kb_scene_config_name_on_exit(void* context) { + BadKbApp* bad_kb = context; + TextInput* text_input = bad_kb->text_input; + + text_input_reset(text_input); +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c new file mode 100644 index 000000000..35c5ab8d2 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c @@ -0,0 +1,72 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum VarItemListIndex { + VarItemListIndexConnection, + VarItemListIndexKeyboardLayout, +}; + +void bad_kb_scene_config_usb_connection_callback(VariableItem* item) { + BadKbApp* bad_kb = variable_item_get_context(item); + bad_kb->is_bt = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); +} + +void bad_kb_scene_config_usb_var_item_list_callback(void* context, uint32_t index) { + BadKbApp* bad_kb = context; + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, index); +} + +void bad_kb_scene_config_usb_on_enter(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list_usb; + VariableItem* item; + + item = variable_item_list_add( + var_item_list, "Connection", 2, bad_kb_scene_config_usb_connection_callback, bad_kb); + variable_item_set_current_value_index(item, bad_kb->is_bt); + variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); + + item = variable_item_list_add( + var_item_list, "Keyboard layout", 0, NULL, bad_kb); + + variable_item_list_set_enter_callback(var_item_list, bad_kb_scene_config_usb_var_item_list_callback, bad_kb); + + view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigUsb); +} + +bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { + BadKbApp* bad_kb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_kb->scene_manager, BadKbSceneConfigUsb, event.event); + consumed = true; + if(event.event == VarItemListIndexKeyboardLayout) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); + } else if(event.event == VarItemListIndexConnection) { + bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + scene_manager_previous_scene(bad_kb->scene_manager); + if (bad_kb->is_bt) { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); + } else { + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); + } + // } else { + // furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_kb_scene_config_usb_on_exit(void* context) { + BadKbApp* bad_kb = context; + VariableItemList* var_item_list = bad_kb->var_item_list_usb; + + variable_item_list_reset(var_item_list); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_kb/scenes/bad_kb_scene_error.c similarity index 70% rename from applications/main/bad_usb/scenes/bad_usb_scene_error.c rename to applications/main/bad_kb/scenes/bad_kb_scene_error.c index d1290e0e0..bd1796d7a 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_error.c @@ -1,20 +1,20 @@ -#include "../bad_usb_app_i.h" +#include "../bad_kb_app_i.h" #include "../../../settings/xtreme_settings/xtreme_settings.h" static void - bad_usb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { + bad_kb_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); - BadUsbApp* app = context; + BadKbApp* app = context; if((result == GuiButtonTypeLeft) && (type == InputTypeShort)) { - view_dispatcher_send_custom_event(app->view_dispatcher, BadUsbCustomEventErrorBack); + view_dispatcher_send_custom_event(app->view_dispatcher, BadKbCustomEventErrorBack); } } -void bad_usb_scene_error_on_enter(void* context) { - BadUsbApp* app = context; +void bad_kb_scene_error_on_enter(void* context) { + BadKbApp* app = context; - if(app->error == BadUsbAppErrorNoFiles) { + if(app->error == BadKbAppErrorNoFiles) { widget_add_icon_element(app->widget, 0, 0, &I_SDQuestion_35x43); widget_add_string_multiline_element( app->widget, @@ -25,8 +25,8 @@ void bad_usb_scene_error_on_enter(void* context) { FontSecondary, "No SD card or\napp data found.\nThis app will not\nwork without\nrequired files."); widget_add_button_element( - app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); - } else if(app->error == BadUsbAppErrorCloseRpc) { + app->widget, GuiButtonTypeLeft, "Back", bad_kb_scene_error_event_callback, app); + } else if(app->error == BadKbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); if(XTREME_SETTINGS()->nsfw_mode) { widget_add_string_multiline_element( @@ -53,15 +53,15 @@ void bad_usb_scene_error_on_enter(void* context) { } } - view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewError); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewError); } -bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* app = context; +bool bad_kb_scene_error_on_event(void* context, SceneManagerEvent event) { + BadKbApp* app = context; bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadUsbCustomEventErrorBack) { + if(event.event == BadKbCustomEventErrorBack) { view_dispatcher_stop(app->view_dispatcher); consumed = true; } @@ -69,7 +69,7 @@ bool bad_usb_scene_error_on_event(void* context, SceneManagerEvent event) { return consumed; } -void bad_usb_scene_error_on_exit(void* context) { - BadUsbApp* app = context; +void bad_kb_scene_error_on_exit(void* context) { + BadKbApp* app = context; widget_reset(app->widget); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c new file mode 100644 index 000000000..44012f68d --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -0,0 +1,52 @@ +#include "../bad_kb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_kb_file_select(BadKbApp* bad_kb) { + furi_assert(bad_kb); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_KB_APP_SCRIPT_EXTENSION, &I_badkb_10px); + browser_options.base_path = BAD_KB_APP_BASE_FOLDER; + browser_options.skip_assets = true; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_kb->dialogs, bad_kb->file_path, bad_kb->file_path, &browser_options); + + return res; +} + +void bad_kb_scene_file_select_on_enter(void* context) { + BadKbApp* bad_kb = context; + + furi_hal_usb_disable(); + if(bad_kb->bad_kb_script) { + bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb->bad_kb_script = NULL; + } + + if(bad_kb_file_select(bad_kb)) { + bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); + + scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); + } else { + furi_hal_usb_enable(); + view_dispatcher_stop(bad_kb->view_dispatcher); + } +} + +bool bad_kb_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadKbApp* bad_kb = context; + return false; +} + +void bad_kb_scene_file_select_on_exit(void* context) { + UNUSED(context); + // BadKbApp* bad_kb = context; +} diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c new file mode 100644 index 000000000..a7eac1786 --- /dev/null +++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c @@ -0,0 +1,58 @@ +#include "../bad_kb_script.h" +#include "../bad_kb_app_i.h" +#include "../views/bad_kb_view.h" +#include "furi_hal.h" +#include "toolbox/path.h" + +void bad_kb_scene_work_button_callback(InputKey key, void* context) { + furi_assert(context); + BadKbApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, key); +} + +bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) { + BadKbApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == InputKeyLeft) { + if (app->is_bt) { + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt); + } else { + scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb); + } + consumed = true; + } else if(event.event == InputKeyOk) { + bad_kb_script_toggle(app->bad_kb_script); + consumed = true; + } + } else if(event.type == SceneManagerEventTypeTick) { + bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); + } + return consumed; +} + +void bad_kb_scene_work_on_enter(void* context) { + BadKbApp* app = context; + + FuriString* file_name; + file_name = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + bad_kb_set_file_name(app->bad_kb_view, furi_string_get_cstr(file_name)); + furi_string_free(file_name); + + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_kb_set_layout(app->bad_kb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + + bad_kb_set_state(app->bad_kb_view, bad_kb_script_get_state(app->bad_kb_script)); + + bad_kb_set_button_callback(app->bad_kb_view, bad_kb_scene_work_button_callback, app); + view_dispatcher_switch_to_view(app->view_dispatcher, BadKbAppViewWork); +} + +void bad_kb_scene_work_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_kb/views/bad_kb_view.c similarity index 70% rename from applications/main/bad_usb/views/bad_usb_view.c rename to applications/main/bad_kb/views/bad_kb_view.c index ad889cd1c..a0fb39f88 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -1,5 +1,5 @@ -#include "bad_usb_view.h" -#include "../bad_usb_script.h" +#include "bad_kb_view.h" +#include "../bad_kb_script.h" #include #include #include @@ -7,21 +7,21 @@ #define MAX_NAME_LEN 64 -struct BadUsb { +struct BadKb { View* view; - BadUsbButtonCallback callback; + BadKbButtonCallback callback; void* context; }; typedef struct { char file_name[MAX_NAME_LEN]; char layout[MAX_NAME_LEN]; - BadUsbState state; + BadKbState state; uint8_t anim_frame; -} BadUsbModel; +} BadKbModel; -static void bad_usb_draw_callback(Canvas* canvas, void* _model) { - BadUsbModel* model = _model; +static void bad_kb_draw_callback(Canvas* canvas, void* _model) { + BadKbModel* model = _model; FuriString* disp_str; disp_str = furi_string_alloc_set(model->file_name); @@ -47,25 +47,25 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); - if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || - (model->state.state == BadUsbStateNotConnected)) { + if((model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone) || + (model->state.state == BadKbStateNotConnected)) { if(xtreme_settings->nsfw_mode) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); } - } else if((model->state.state == BadUsbStateRunning) || (model->state.state == BadUsbStateDelay)) { + } else if((model->state.state == BadKbStateRunning) || (model->state.state == BadKbStateDelay)) { elements_button_center(canvas, "Stop"); - } else if(model->state.state == BadUsbStateWillRun) { + } else if(model->state.state == BadKbStateWillRun) { elements_button_center(canvas, "Cancel"); } - if((model->state.state == BadUsbStateNotConnected) || - (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + if((model->state.state == BadKbStateNotConnected) || + (model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone)) { elements_button_left(canvas, "Config"); } - if(model->state.state == BadUsbStateNotConnected) { + if(model->state.state == BadKbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); if(xtreme_settings->nsfw_mode) { @@ -75,7 +75,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect to"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "a device"); } - } else if(model->state.state == BadUsbStateWillRun) { + } else if(model->state.state == BadKbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); if(xtreme_settings->nsfw_mode) { @@ -84,12 +84,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); } canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); - } else if(model->state.state == BadUsbStateFileError) { + } else if(model->state.state == BadKbStateFileError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); - } else if(model->state.state == BadUsbStateScriptError) { + } else if(model->state.state == BadKbStateScriptError) { canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); @@ -99,12 +99,12 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); - } else if(model->state.state == BadUsbStateIdle) { + } else if(model->state.state == BadKbStateIdle) { canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateRunning) { + } else if(model->state.state == BadKbStateRunning) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { @@ -117,13 +117,13 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDone) { + } else if(model->state.state == BadKbStateDone) { canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); - } else if(model->state.state == BadUsbStateDelay) { + } else if(model->state.state == BadKbStateDelay) { if(model->anim_frame == 0) { canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { @@ -148,84 +148,84 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { furi_string_free(disp_str); } -static bool bad_usb_input_callback(InputEvent* event, void* context) { +static bool bad_kb_input_callback(InputEvent* event, void* context) { furi_assert(context); - BadUsb* bad_usb = context; + BadKb* bad_kb = context; bool consumed = false; if(event->type == InputTypeShort) { if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { consumed = true; - furi_assert(bad_usb->callback); - bad_usb->callback(event->key, bad_usb->context); + furi_assert(bad_kb->callback); + bad_kb->callback(event->key, bad_kb->context); } } return consumed; } -BadUsb* bad_usb_alloc() { - BadUsb* bad_usb = malloc(sizeof(BadUsb)); +BadKb* bad_kb_alloc() { + BadKb* bad_kb = malloc(sizeof(BadKb)); - bad_usb->view = view_alloc(); - view_allocate_model(bad_usb->view, ViewModelTypeLocking, sizeof(BadUsbModel)); - view_set_context(bad_usb->view, bad_usb); - view_set_draw_callback(bad_usb->view, bad_usb_draw_callback); - view_set_input_callback(bad_usb->view, bad_usb_input_callback); + bad_kb->view = view_alloc(); + view_allocate_model(bad_kb->view, ViewModelTypeLocking, sizeof(BadKbModel)); + view_set_context(bad_kb->view, bad_kb); + view_set_draw_callback(bad_kb->view, bad_kb_draw_callback); + view_set_input_callback(bad_kb->view, bad_kb_input_callback); - return bad_usb; + return bad_kb; } -void bad_usb_free(BadUsb* bad_usb) { - furi_assert(bad_usb); - view_free(bad_usb->view); - free(bad_usb); +void bad_kb_free(BadKb* bad_kb) { + furi_assert(bad_kb); + view_free(bad_kb->view); + free(bad_kb); } -View* bad_usb_get_view(BadUsb* bad_usb) { - furi_assert(bad_usb); - return bad_usb->view; +View* bad_kb_get_view(BadKb* bad_kb) { + furi_assert(bad_kb); + return bad_kb->view; } -void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) { - furi_assert(bad_usb); +void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context) { + furi_assert(bad_kb); furi_assert(callback); with_view_model( - bad_usb->view, - BadUsbModel * model, + bad_kb->view, + BadKbModel * model, { UNUSED(model); - bad_usb->callback = callback; - bad_usb->context = context; + bad_kb->callback = callback; + bad_kb->context = context; }, true); } -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { +void bad_kb_set_file_name(BadKb* bad_kb, const char* name) { furi_assert(name); with_view_model( - bad_usb->view, - BadUsbModel * model, + bad_kb->view, + BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); } -void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { +void bad_kb_set_layout(BadKb* bad_kb, const char* layout) { furi_assert(layout); with_view_model( - bad_usb->view, - BadUsbModel * model, + bad_kb->view, + BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); } -void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { +void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) { furi_assert(st); with_view_model( - bad_usb->view, - BadUsbModel * model, + bad_kb->view, + BadKbModel * model, { - memcpy(&(model->state), st, sizeof(BadUsbState)); + memcpy(&(model->state), st, sizeof(BadKbState)); model->anim_frame ^= 1; }, true); diff --git a/applications/main/bad_kb/views/bad_kb_view.h b/applications/main/bad_kb/views/bad_kb_view.h new file mode 100644 index 000000000..24fdf4792 --- /dev/null +++ b/applications/main/bad_kb/views/bad_kb_view.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "../bad_kb_script.h" + +typedef struct BadKb BadKb; +typedef void (*BadKbButtonCallback)(InputKey key, void* context); + +BadKb* bad_kb_alloc(); + +void bad_kb_free(BadKb* bad_kb); + +View* bad_kb_get_view(BadKb* bad_kb); + +void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, void* context); + +void bad_kb_set_file_name(BadKb* bad_kb, const char* name); + +void bad_kb_set_layout(BadKb* bad_kb, const char* layout); + +void bad_kb_set_state(BadKb* bad_kb, BadKbState* st); diff --git a/applications/main/bad_usb/bad_usb_app.h b/applications/main/bad_usb/bad_usb_app.h deleted file mode 100644 index 4192a59c9..000000000 --- a/applications/main/bad_usb/bad_usb_app.h +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct BadUsbApp BadUsbApp; - -void bad_usb_set_name(BadUsbApp* app, const char* fmt, ...); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h deleted file mode 100644 index abd252bb4..000000000 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include "bad_usb_app.h" -#include "scenes/bad_usb_scene.h" -#include "bad_usb_script.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "views/bad_usb_view.h" - -#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") -#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/layouts" -#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" -#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" - -#define BAD_USB_MAC_ADDRESS_LEN 6 // need replace with MAC size maccro -#define BAD_USB_ADV_NAME_MAX_LEN 18 - -typedef enum { - BadUsbAppErrorNoFiles, - BadUsbAppErrorCloseRpc, -} BadUsbAppError; - -typedef enum BadUsbCustomEvent { - BadUsbAppCustomEventTextEditResult, - BadUsbAppCustomEventByteInputDone, - BadUsbCustomEventErrorBack -} BadUsbCustomEvent; - -typedef struct { - uint8_t mac[BAD_USB_MAC_ADDRESS_LEN]; - char name[BAD_USB_ADV_NAME_MAX_LEN + 1]; - - // number of bt keys before starting the app (all keys added in - // the bt keys file then will be removed) - uint16_t n_keys; -} BadUsbBtConfig; - -struct BadUsbApp { - Gui* gui; - ViewDispatcher* view_dispatcher; - SceneManager* scene_manager; - NotificationApp* notifications; - DialogsApp* dialogs; - Widget* widget; - VariableItemList* var_item_list_bt; - VariableItemList* var_item_list_usb; - - Bt* bt; - TextInput* text_input; - ByteInput* byte_input; - uint8_t mac[BAD_USB_MAC_ADDRESS_LEN]; - char name[BAD_USB_ADV_NAME_MAX_LEN + 1]; - BadUsbBtConfig bt_old_config; - - BadUsbAppError error; - FuriString* file_path; - FuriString* keyboard_layout; - BadUsb* bad_usb_view; - BadUsbScript* bad_usb_script; - - bool is_bt; -}; - -typedef enum { - BadUsbAppViewError, - BadUsbAppViewWork, - BadUsbAppViewConfigBt, - BadUsbAppViewConfigUsb, - BadUsbAppViewConfigMac, - BadUsbAppViewConfigName -} BadUsbAppView; diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h deleted file mode 100644 index 5c37bcec2..000000000 --- a/applications/main/bad_usb/bad_usb_script.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -typedef struct BadUsbScript BadUsbScript; - -typedef enum { - BadUsbStateInit, - BadUsbStateNotConnected, - BadUsbStateIdle, - BadUsbStateWillRun, - BadUsbStateRunning, - BadUsbStateDelay, - BadUsbStateDone, - BadUsbStateScriptError, - BadUsbStateFileError, -} BadUsbWorkerState; - -typedef struct { - BadUsbWorkerState state; - uint16_t line_cur; - uint16_t line_nb; - uint32_t delay_remain; - uint16_t error_line; - char error[64]; -} BadUsbState; - -BadUsbScript* bad_usb_script_open(FuriString* file_path, Bt* bt); - -void bad_usb_script_close(BadUsbScript* bad_usb); - -void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); - -void bad_usb_script_start(BadUsbScript* bad_usb); - -void bad_usb_script_stop(BadUsbScript* bad_usb); - -void bad_usb_script_toggle(BadUsbScript* bad_usb); - -BadUsbState* bad_usb_script_get_state(BadUsbScript* bad_usb); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h deleted file mode 100644 index 12ba8f31c..000000000 --- a/applications/main/bad_usb/bad_usb_settings_filename.h +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings" diff --git a/applications/main/bad_usb/scenes/bad_usb_scene.c b/applications/main/bad_usb/scenes/bad_usb_scene.c deleted file mode 100644 index 03c7c4471..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "bad_usb_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const bad_usb_scene_on_enter_handlers[])(void*) = { -#include "bad_usb_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 bad_usb_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "bad_usb_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 bad_usb_scene_on_exit_handlers[])(void* context) = { -#include "bad_usb_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers bad_usb_scene_handlers = { - .on_enter_handlers = bad_usb_scene_on_enter_handlers, - .on_event_handlers = bad_usb_scene_on_event_handlers, - .on_exit_handlers = bad_usb_scene_on_exit_handlers, - .scene_num = BadUsbSceneNum, -}; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h deleted file mode 100644 index 2524464f1..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ /dev/null @@ -1,8 +0,0 @@ -ADD_SCENE(bad_usb, file_select, FileSelect) -ADD_SCENE(bad_usb, work, Work) -ADD_SCENE(bad_usb, error, Error) -ADD_SCENE(bad_usb, config_bt, ConfigBt) -ADD_SCENE(bad_usb, config_usb, ConfigUsb) -ADD_SCENE(bad_usb, config_layout, ConfigLayout) -ADD_SCENE(bad_usb, config_name, ConfigName) -ADD_SCENE(bad_usb, config_mac, ConfigMac) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c deleted file mode 100644 index 7071e748e..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_bt.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" - -enum VarItemListIndex { - VarItemListIndexConnection, - VarItemListIndexKeyboardLayout, - VarItemListIndexAdvertisementName, - VarItemListIndexMacAddress, -}; - -void bad_usb_scene_config_bt_connection_callback(VariableItem* item) { - BadUsbApp* bad_usb = variable_item_get_context(item); - bad_usb->is_bt = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); -} - -void bad_usb_scene_config_bt_var_item_list_callback(void* context, uint32_t index) { - BadUsbApp* bad_usb = context; - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); -} - -void bad_usb_scene_config_bt_on_enter(void* context) { - BadUsbApp* bad_usb = context; - VariableItemList* var_item_list = bad_usb->var_item_list_bt; - VariableItem* item; - - item = variable_item_list_add( - var_item_list, "Connection", 2, bad_usb_scene_config_bt_connection_callback, bad_usb); - variable_item_set_current_value_index(item, bad_usb->is_bt); - variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); - - item = variable_item_list_add( - var_item_list, "Keyboard layout", 0, NULL, bad_usb); - - item = variable_item_list_add( - var_item_list, "Change adv name", 0, NULL, bad_usb); - - item = variable_item_list_add( - var_item_list, "Change MAC address", 0, NULL, bad_usb); - - variable_item_list_set_enter_callback(var_item_list, bad_usb_scene_config_bt_var_item_list_callback, bad_usb); - - view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigBt); -} - -bool bad_usb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* bad_usb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfigBt, event.event); - consumed = true; - if(event.event == VarItemListIndexKeyboardLayout) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); - } else if(event.event == VarItemListIndexConnection) { - bad_usb_script_close(bad_usb->bad_usb_script); - bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); - bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); - scene_manager_previous_scene(bad_usb->scene_manager); - if (bad_usb->is_bt) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); - } else { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); - } - } else if(event.event == VarItemListIndexAdvertisementName) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigName); - } else if(event.event == VarItemListIndexMacAddress) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigMac); - // } else { - // furi_crash("Unknown key type"); - } - } - - return consumed; -} - -void bad_usb_scene_config_bt_on_exit(void* context) { - BadUsbApp* bad_usb = context; - VariableItemList* var_item_list = bad_usb->var_item_list_bt; - - variable_item_list_reset(var_item_list); -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c deleted file mode 100644 index 44dcd55af..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" -#include - -static bool bad_usb_layout_select(BadUsbApp* bad_usb) { - furi_assert(bad_usb); - - FuriString* predefined_path; - predefined_path = furi_string_alloc(); - if(!furi_string_empty(bad_usb->keyboard_layout)) { - furi_string_set(predefined_path, bad_usb->keyboard_layout); - } else { - furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); - } - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); - - furi_string_free(predefined_path); - return res; -} - -void bad_usb_scene_config_layout_on_enter(void* context) { - BadUsbApp* bad_usb = context; - - if(bad_usb_layout_select(bad_usb)) { - bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); - } - scene_manager_previous_scene(bad_usb->scene_manager); -} - -bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadUsbApp* bad_usb = context; - return false; -} - -void bad_usb_scene_config_layout_on_exit(void* context) { - UNUSED(context); - // BadUsbApp* bad_usb = context; -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c deleted file mode 100644 index 597cbcbd7..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_mac.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bad_usb_app_i.h" - -#define TAG "BadUsbConfigMac" - -static uint8_t* reverse_mac_addr(uint8_t* mac) { - uint8_t tmp; - for(int i = 0; i < 3; i++) { - tmp = mac[i]; - mac[i] = mac[5 - i]; - mac[5 - i] = tmp; - } - return mac; -} - -void bad_usb_scene_config_mac_byte_input_callback(void* context) { - BadUsbApp* bad_usb = context; - - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, BadUsbAppCustomEventByteInputDone); -} - -void bad_usb_scene_config_mac_on_enter(void* context) { - BadUsbApp* bad_usb = context; - - // Setup view - ByteInput* byte_input = bad_usb->byte_input; - byte_input_set_header_text(byte_input, "Enter new MAC address"); - byte_input_set_result_callback( - byte_input, - bad_usb_scene_config_mac_byte_input_callback, - NULL, - bad_usb, - reverse_mac_addr(bad_usb->mac), - GAP_MAC_ADDR_SIZE); - view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigMac); -} - -bool bad_usb_scene_config_mac_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* bad_usb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == BadUsbAppCustomEventByteInputDone) { - bt_set_profile_mac_address(bad_usb->bt, reverse_mac_addr(bad_usb->mac)); - scene_manager_previous_scene(bad_usb->scene_manager); - consumed = true; - } - } - return consumed; -} - -void bad_usb_scene_config_mac_on_exit(void* context) { - BadUsbApp* bad_usb = context; - - // Clear view - byte_input_set_result_callback(bad_usb->byte_input, NULL, NULL, NULL, NULL, 0); - byte_input_set_header_text(bad_usb->byte_input, ""); -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c deleted file mode 100644 index c22558d58..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_name.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../bad_usb_app_i.h" - -static void bad_usb_scene_config_name_text_input_callback(void* context) { - BadUsbApp* bad_usb = context; - - view_dispatcher_send_custom_event( - bad_usb->view_dispatcher, BadUsbAppCustomEventTextEditResult); -} - -void bad_usb_scene_config_name_on_enter(void* context) { - BadUsbApp* bad_usb = context; - TextInput* text_input = bad_usb->text_input; - - text_input_set_header_text(text_input, "Set BLE adv name"); - - text_input_set_result_callback( - text_input, - bad_usb_scene_config_name_text_input_callback, - bad_usb, - bad_usb->name, - BAD_USB_ADV_NAME_MAX_LEN, - true); - - view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigName); -} - -bool bad_usb_scene_config_name_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* bad_usb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - consumed = true; - if(event.event == BadUsbAppCustomEventTextEditResult) { - bt_set_profile_adv_name(bad_usb->bt, bad_usb->name); - } - scene_manager_previous_scene(bad_usb->scene_manager); - } - return consumed; -} - -void bad_usb_scene_config_name_on_exit(void* context) { - BadUsbApp* bad_usb = context; - TextInput* text_input = bad_usb->text_input; - - text_input_reset(text_input); -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c deleted file mode 100644 index af7abd570..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config_usb.c +++ /dev/null @@ -1,72 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" - -enum VarItemListIndex { - VarItemListIndexConnection, - VarItemListIndexKeyboardLayout, -}; - -void bad_usb_scene_config_usb_connection_callback(VariableItem* item) { - BadUsbApp* bad_usb = variable_item_get_context(item); - bad_usb->is_bt = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, VarItemListIndexConnection); -} - -void bad_usb_scene_config_usb_var_item_list_callback(void* context, uint32_t index) { - BadUsbApp* bad_usb = context; - view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); -} - -void bad_usb_scene_config_usb_on_enter(void* context) { - BadUsbApp* bad_usb = context; - VariableItemList* var_item_list = bad_usb->var_item_list_usb; - VariableItem* item; - - item = variable_item_list_add( - var_item_list, "Connection", 2, bad_usb_scene_config_usb_connection_callback, bad_usb); - variable_item_set_current_value_index(item, bad_usb->is_bt); - variable_item_set_current_value_text(item, bad_usb->is_bt ? "BT" : "USB"); - - item = variable_item_list_add( - var_item_list, "Keyboard layout", 0, NULL, bad_usb); - - variable_item_list_set_enter_callback(var_item_list, bad_usb_scene_config_usb_var_item_list_callback, bad_usb); - - view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfigUsb); -} - -bool bad_usb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* bad_usb = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfigUsb, event.event); - consumed = true; - if(event.event == VarItemListIndexKeyboardLayout) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); - } else if(event.event == VarItemListIndexConnection) { - bad_usb_script_close(bad_usb->bad_usb_script); - bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); - bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); - scene_manager_previous_scene(bad_usb->scene_manager); - if (bad_usb->is_bt) { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigBt); - } else { - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigUsb); - } - // } else { - // furi_crash("Unknown key type"); - } - } - - return consumed; -} - -void bad_usb_scene_config_usb_on_exit(void* context) { - BadUsbApp* bad_usb = context; - VariableItemList* var_item_list = bad_usb->var_item_list_usb; - - variable_item_list_reset(var_item_list); -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c deleted file mode 100644 index eb72f1765..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" -#include - -static bool bad_usb_file_select(BadUsbApp* bad_usb) { - furi_assert(bad_usb); - - DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options( - &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); - browser_options.base_path = BAD_USB_APP_BASE_FOLDER; - browser_options.skip_assets = true; - - // Input events and views are managed by file_browser - bool res = dialog_file_browser_show( - bad_usb->dialogs, bad_usb->file_path, bad_usb->file_path, &browser_options); - - return res; -} - -void bad_usb_scene_file_select_on_enter(void* context) { - BadUsbApp* bad_usb = context; - - furi_hal_usb_disable(); - if(bad_usb->bad_usb_script) { - bad_usb_script_close(bad_usb->bad_usb_script); - bad_usb->bad_usb_script = NULL; - } - - if(bad_usb_file_select(bad_usb)) { - bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path, bad_usb->is_bt ? bad_usb->bt : NULL); - bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); - - scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); - } else { - furi_hal_usb_enable(); - view_dispatcher_stop(bad_usb->view_dispatcher); - } -} - -bool bad_usb_scene_file_select_on_event(void* context, SceneManagerEvent event) { - UNUSED(context); - UNUSED(event); - // BadUsbApp* bad_usb = context; - return false; -} - -void bad_usb_scene_file_select_on_exit(void* context) { - UNUSED(context); - // BadUsbApp* bad_usb = context; -} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c deleted file mode 100644 index 03a8114e0..000000000 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "../bad_usb_script.h" -#include "../bad_usb_app_i.h" -#include "../views/bad_usb_view.h" -#include "furi_hal.h" -#include "toolbox/path.h" - -void bad_usb_scene_work_button_callback(InputKey key, void* context) { - furi_assert(context); - BadUsbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, key); -} - -bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { - BadUsbApp* app = context; - bool consumed = false; - - if(event.type == SceneManagerEventTypeCustom) { - if(event.event == InputKeyLeft) { - if (app->is_bt) { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigBt); - } else { - scene_manager_next_scene(app->scene_manager, BadUsbSceneConfigUsb); - } - consumed = true; - } else if(event.event == InputKeyOk) { - bad_usb_script_toggle(app->bad_usb_script); - consumed = true; - } - } else if(event.type == SceneManagerEventTypeTick) { - bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - } - return consumed; -} - -void bad_usb_scene_work_on_enter(void* context) { - BadUsbApp* app = context; - - FuriString* file_name; - file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); - bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); - furi_string_free(file_name); - - FuriString* layout; - layout = furi_string_alloc(); - path_extract_filename(app->keyboard_layout, layout, true); - bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); - furi_string_free(layout); - - bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - - bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); - view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); -} - -void bad_usb_scene_work_on_exit(void* context) { - UNUSED(context); -} diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h deleted file mode 100644 index 8447fb055..000000000 --- a/applications/main/bad_usb/views/bad_usb_view.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include -#include "../bad_usb_script.h" - -typedef struct BadUsb BadUsb; -typedef void (*BadUsbButtonCallback)(InputKey key, void* context); - -BadUsb* bad_usb_alloc(); - -void bad_usb_free(BadUsb* bad_usb); - -View* bad_usb_get_view(BadUsb* bad_usb); - -void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context); - -void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); - -void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); - -void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/applications/plugins/dolphinbackup/storage_DolphinBackup.c b/applications/plugins/dolphinbackup/storage_DolphinBackup.c index 02baddc0f..7e7bd8f36 100644 --- a/applications/plugins/dolphinbackup/storage_DolphinBackup.c +++ b/applications/plugins/dolphinbackup/storage_DolphinBackup.c @@ -16,7 +16,7 @@ static const char* app_dirsDolphinBackup[] = { "nfc", "infrared", "ibutton", - "badusb", + "badkb", ".bt.settings", ".desktop.settings", ".dolphin.state", diff --git a/applications/plugins/mousejacker/icons/badusb_10px.png b/applications/plugins/mousejacker/icons/badkb_10px.png similarity index 100% rename from applications/plugins/mousejacker/icons/badusb_10px.png rename to applications/plugins/mousejacker/icons/badkb_10px.png diff --git a/applications/plugins/mousejacker/images/badusb_10px.png b/applications/plugins/mousejacker/images/badkb_10px.png similarity index 100% rename from applications/plugins/mousejacker/images/badusb_10px.png rename to applications/plugins/mousejacker/images/badkb_10px.png diff --git a/applications/plugins/mousejacker/mousejacker.c b/applications/plugins/mousejacker/mousejacker.c index c45b0e502..3bd772889 100644 --- a/applications/plugins/mousejacker/mousejacker.c +++ b/applications/plugins/mousejacker/mousejacker.c @@ -112,7 +112,7 @@ static bool open_ducky_script(Stream* stream, PluginState* plugin_state) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( - &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badusb_10px); + &browser_options, MOUSEJACKER_APP_PATH_EXTENSION, &I_badkb_10px); browser_options.hide_ext = false; bool ret = dialog_file_browser_show(dialogs, path, path, &browser_options); @@ -396,4 +396,4 @@ int32_t mousejacker_app(void* p) { free(plugin_state); return 0; -} \ No newline at end of file +} diff --git a/applications/plugins/totp/services/config/config.c b/applications/plugins/totp/services/config/config.c index cd19d19d4..c5193ac2e 100644 --- a/applications/plugins/totp/services/config/config.c +++ b/applications/plugins/totp/services/config/config.c @@ -132,7 +132,7 @@ static TotpConfigFileOpenResult totp_open_config_file(Storage* storage, FlipperF flipper_format_write_comment_cstr(fff_data_file, " "); flipper_format_write_comment_cstr( fff_data_file, - "How to notify user when new token is generated or badusb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); + "How to notify user when new token is generated or badkb mode is activated (possible values: 0 - do not notify, 1 - sound, 2 - vibro, 3 sound and vibro)"); flipper_format_write_uint32( fff_data_file, TOTP_CONFIG_KEY_NOTIFICATION_METHOD, &tmp_uint32, 1); @@ -769,4 +769,4 @@ void totp_config_file_reset() { Storage* storage = totp_open_storage(); storage_simply_remove(storage, CONFIG_FILE_PATH); totp_close_storage(); -} \ No newline at end of file +} diff --git a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c index 41c52b435..18d1791f4 100644 --- a/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c +++ b/applications/plugins/totp/ui/scenes/generate_token/totp_scene_generate_token.c @@ -24,7 +24,7 @@ typedef struct { uint32_t last_token_gen_time; TotpTypeCodeWorkerContext* type_code_worker_context; NotificationMessage const** notification_sequence_new_token; - NotificationMessage const** notification_sequence_badusb; + NotificationMessage const** notification_sequence_badkb; } SceneState; static const NotificationSequence* @@ -69,8 +69,8 @@ static const NotificationSequence* } static const NotificationSequence* - get_notification_sequence_badusb(const PluginState* plugin_state, SceneState* scene_state) { - if(scene_state->notification_sequence_badusb == NULL) { + get_notification_sequence_badkb(const PluginState* plugin_state, SceneState* scene_state) { + if(scene_state->notification_sequence_badkb == NULL) { uint8_t i = 0; uint8_t length = 3; if(plugin_state->notification_method & NotificationMethodVibro) { @@ -81,36 +81,36 @@ static const NotificationSequence* length += 6; } - scene_state->notification_sequence_badusb = malloc(sizeof(void*) * length); - furi_check(scene_state->notification_sequence_badusb != NULL); + scene_state->notification_sequence_badkb = malloc(sizeof(void*) * length); + furi_check(scene_state->notification_sequence_badkb != NULL); - scene_state->notification_sequence_badusb[i++] = &message_blue_255; + scene_state->notification_sequence_badkb[i++] = &message_blue_255; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_on; + scene_state->notification_sequence_badkb[i++] = &message_vibro_on; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_note_d5; //-V525 - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_e4; - scene_state->notification_sequence_badusb[i++] = &message_delay_50; - scene_state->notification_sequence_badusb[i++] = &message_note_f3; + scene_state->notification_sequence_badkb[i++] = &message_note_d5; //-V525 + scene_state->notification_sequence_badkb[i++] = &message_delay_50; + scene_state->notification_sequence_badkb[i++] = &message_note_e4; + scene_state->notification_sequence_badkb[i++] = &message_delay_50; + scene_state->notification_sequence_badkb[i++] = &message_note_f3; } - scene_state->notification_sequence_badusb[i++] = &message_delay_50; + scene_state->notification_sequence_badkb[i++] = &message_delay_50; if(plugin_state->notification_method & NotificationMethodVibro) { - scene_state->notification_sequence_badusb[i++] = &message_vibro_off; + scene_state->notification_sequence_badkb[i++] = &message_vibro_off; } if(plugin_state->notification_method & NotificationMethodSound) { - scene_state->notification_sequence_badusb[i++] = &message_sound_off; + scene_state->notification_sequence_badkb[i++] = &message_sound_off; } - scene_state->notification_sequence_badusb[i++] = NULL; + scene_state->notification_sequence_badkb[i++] = NULL; } - return (NotificationSequence*)scene_state->notification_sequence_badusb; + return (NotificationSequence*)scene_state->notification_sequence_badkb; } static void int_token_to_str(uint32_t i_token_code, char* str, TokenDigitsCount len) { @@ -340,7 +340,7 @@ bool totp_scene_generate_token_handle_event( scene_state->type_code_worker_context, TotpTypeCodeWorkerEventType); notification_message( plugin_state->notification_app, - get_notification_sequence_badusb(plugin_state, scene_state)); + get_notification_sequence_badkb(plugin_state, scene_state)); return true; } @@ -399,8 +399,8 @@ void totp_scene_generate_token_deactivate(PluginState* plugin_state) { free(scene_state->notification_sequence_new_token); } - if(scene_state->notification_sequence_badusb != NULL) { - free(scene_state->notification_sequence_badusb); + if(scene_state->notification_sequence_badkb != NULL) { + free(scene_state->notification_sequence_badkb); } free(scene_state); diff --git a/applications/services/dolphin/helpers/dolphin_deed.c b/applications/services/dolphin/helpers/dolphin_deed.c index 51db56fdf..4b9f11599 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.c +++ b/applications/services/dolphin/helpers/dolphin_deed.c @@ -34,7 +34,7 @@ static const DolphinDeedWeight dolphin_deed_weights[] = { {2, DolphinAppIbutton}, // DolphinDeedIbuttonEmulate {2, DolphinAppIbutton}, // DolphinDeedIbuttonAdd - {3, DolphinAppBadusb}, // DolphinDeedBadUsbPlayScript + {3, DolphinAppBadKb}, // DolphinDeedBadKbPlayScript {3, DolphinAppPlugin}, // DolphinDeedU2fAuthorized {1, DolphinAppPlugin}, // DolphinDeedGpioUartBridge @@ -50,7 +50,7 @@ static uint8_t dolphin_deed_limits[] = { 20, // DolphinAppNfc 20, // DolphinAppIr 20, // DolphinAppIbutton - 20, // DolphinAppBadusb + 20, // DolphinAppBadKb 20, // DolphinAppPlugin }; diff --git a/applications/services/dolphin/helpers/dolphin_deed.h b/applications/services/dolphin/helpers/dolphin_deed.h index c9cd18f31..51adf6b20 100644 --- a/applications/services/dolphin/helpers/dolphin_deed.h +++ b/applications/services/dolphin/helpers/dolphin_deed.h @@ -12,7 +12,7 @@ typedef enum { DolphinAppNfc, DolphinAppIr, DolphinAppIbutton, - DolphinAppBadusb, + DolphinAppBadKb, DolphinAppPlugin, DolphinAppMAX, } DolphinApp; @@ -50,7 +50,7 @@ typedef enum { DolphinDeedIbuttonEmulate, DolphinDeedIbuttonAdd, - DolphinDeedBadUsbPlayScript, + DolphinDeedBadKbPlayScript, DolphinDeedU2fAuthorized, DolphinDeedGpioUartBridge, diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4386fdfd0..ce67e789c 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -12,7 +12,7 @@ #define TAG "BrowserWorker" #define ASSETS_DIR "assets" -#define BADUSB_LAYOUTS_DIR "layouts" +#define BADKB_LAYOUTS_DIR "layouts" #define SUBGHZ_TEMP_DIR "tmp_history" #define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX #define FILE_NAME_LEN_MAX 256 @@ -90,7 +90,7 @@ static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, boo // Skip assets folders (if enabled) if(browser->skip_assets) { return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)) && - ((furi_string_cmp_str(name, BADUSB_LAYOUTS_DIR) == 0) ? (false) : (true)) && + ((furi_string_cmp_str(name, BADKB_LAYOUTS_DIR) == 0) ? (false) : (true)) && ((furi_string_cmp_str(name, SUBGHZ_TEMP_DIR) == 0) ? (false) : (true)); } else { return true; diff --git a/assets/icons/Archive/badusb_10px.png b/assets/icons/Archive/badkb_10px.png similarity index 100% rename from assets/icons/Archive/badusb_10px.png rename to assets/icons/Archive/badkb_10px.png diff --git a/assets/icons/BadUsb/Clock_18x18.png b/assets/icons/BadKb/Clock_18x18.png similarity index 100% rename from assets/icons/BadUsb/Clock_18x18.png rename to assets/icons/BadKb/Clock_18x18.png diff --git a/assets/icons/BadUsb/Error_18x18.png b/assets/icons/BadKb/Error_18x18.png similarity index 100% rename from assets/icons/BadUsb/Error_18x18.png rename to assets/icons/BadKb/Error_18x18.png diff --git a/assets/icons/BadUsb/EviSmile1_18x21.png b/assets/icons/BadKb/EviSmile1_18x21.png similarity index 100% rename from assets/icons/BadUsb/EviSmile1_18x21.png rename to assets/icons/BadKb/EviSmile1_18x21.png diff --git a/assets/icons/BadUsb/EviSmile2_18x21.png b/assets/icons/BadKb/EviSmile2_18x21.png similarity index 100% rename from assets/icons/BadUsb/EviSmile2_18x21.png rename to assets/icons/BadKb/EviSmile2_18x21.png diff --git a/assets/icons/BadUsb/EviWaiting1_18x21.png b/assets/icons/BadKb/EviWaiting1_18x21.png similarity index 100% rename from assets/icons/BadUsb/EviWaiting1_18x21.png rename to assets/icons/BadKb/EviWaiting1_18x21.png diff --git a/assets/icons/BadUsb/EviWaiting2_18x21.png b/assets/icons/BadKb/EviWaiting2_18x21.png similarity index 100% rename from assets/icons/BadUsb/EviWaiting2_18x21.png rename to assets/icons/BadKb/EviWaiting2_18x21.png diff --git a/assets/icons/BadUsb/Percent_10x14.png b/assets/icons/BadKb/Percent_10x14.png similarity index 100% rename from assets/icons/BadUsb/Percent_10x14.png rename to assets/icons/BadKb/Percent_10x14.png diff --git a/assets/icons/BadUsb/Smile_18x18.png b/assets/icons/BadKb/Smile_18x18.png similarity index 100% rename from assets/icons/BadUsb/Smile_18x18.png rename to assets/icons/BadKb/Smile_18x18.png diff --git a/assets/icons/BadUsb/UsbTree_48x22.png b/assets/icons/BadKb/UsbTree_48x22.png similarity index 100% rename from assets/icons/BadUsb/UsbTree_48x22.png rename to assets/icons/BadKb/UsbTree_48x22.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_01.png b/assets/icons/MainMenu/BadKb_14/frame_01.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_01.png rename to assets/icons/MainMenu/BadKb_14/frame_01.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_02.png b/assets/icons/MainMenu/BadKb_14/frame_02.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_02.png rename to assets/icons/MainMenu/BadKb_14/frame_02.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_03.png b/assets/icons/MainMenu/BadKb_14/frame_03.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_03.png rename to assets/icons/MainMenu/BadKb_14/frame_03.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_04.png b/assets/icons/MainMenu/BadKb_14/frame_04.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_04.png rename to assets/icons/MainMenu/BadKb_14/frame_04.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_05.png b/assets/icons/MainMenu/BadKb_14/frame_05.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_05.png rename to assets/icons/MainMenu/BadKb_14/frame_05.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_06.png b/assets/icons/MainMenu/BadKb_14/frame_06.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_06.png rename to assets/icons/MainMenu/BadKb_14/frame_06.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_07.png b/assets/icons/MainMenu/BadKb_14/frame_07.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_07.png rename to assets/icons/MainMenu/BadKb_14/frame_07.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_08.png b/assets/icons/MainMenu/BadKb_14/frame_08.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_08.png rename to assets/icons/MainMenu/BadKb_14/frame_08.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_09.png b/assets/icons/MainMenu/BadKb_14/frame_09.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_09.png rename to assets/icons/MainMenu/BadKb_14/frame_09.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_10.png b/assets/icons/MainMenu/BadKb_14/frame_10.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_10.png rename to assets/icons/MainMenu/BadKb_14/frame_10.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_11.png b/assets/icons/MainMenu/BadKb_14/frame_11.png similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_11.png rename to assets/icons/MainMenu/BadKb_14/frame_11.png diff --git a/assets/icons/MainMenu/BadUsb_14/frame_rate b/assets/icons/MainMenu/BadKb_14/frame_rate similarity index 100% rename from assets/icons/MainMenu/BadUsb_14/frame_rate rename to assets/icons/MainMenu/BadKb_14/frame_rate diff --git a/assets/resources/badkb/.badkb.settings b/assets/resources/badkb/.badkb.settings new file mode 100644 index 000000000..4841c2ded --- /dev/null +++ b/assets/resources/badkb/.badkb.settings @@ -0,0 +1 @@ +/any/badkb/layouts/en-US.kl diff --git a/assets/resources/badusb/Kiosk-Evasion-Bruteforce.txt b/assets/resources/badkb/Kiosk-Evasion-Bruteforce.txt similarity index 100% rename from assets/resources/badusb/Kiosk-Evasion-Bruteforce.txt rename to assets/resources/badkb/Kiosk-Evasion-Bruteforce.txt diff --git a/assets/resources/badusb/Wifi-Stealer_ORG.txt b/assets/resources/badkb/Wifi-Stealer_ORG.txt similarity index 100% rename from assets/resources/badusb/Wifi-Stealer_ORG.txt rename to assets/resources/badkb/Wifi-Stealer_ORG.txt diff --git a/assets/resources/badusb/demo_macos.txt b/assets/resources/badkb/demo_macos.txt similarity index 92% rename from assets/resources/badusb/demo_macos.txt rename to assets/resources/badkb/demo_macos.txt index 82543b28f..1fd4544b3 100644 --- a/assets/resources/badusb/demo_macos.txt +++ b/assets/resources/badkb/demo_macos.txt @@ -2,7 +2,7 @@ ID 1234:5678 Apple:Keyboard REM You can change these values to VID/PID of original Apple keyboard REM to bypass Keyboard Setup Assistant -REM This is BadUSB demo script for macOS +REM This is BadKB demo script for macOS REM Open terminal window DELAY 1000 @@ -75,7 +75,7 @@ ENTER HOME ENTER -STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format +STRING Flipper Zero BadKB feature is compatible with USB Rubber Ducky script format ENTER STRING More information about script syntax can be found here: ENTER diff --git a/assets/resources/badusb/demo_windows.txt b/assets/resources/badkb/demo_windows.txt similarity index 92% rename from assets/resources/badusb/demo_windows.txt rename to assets/resources/badkb/demo_windows.txt index 2ed33b3c0..f3b1309d1 100644 --- a/assets/resources/badusb/demo_windows.txt +++ b/assets/resources/badkb/demo_windows.txt @@ -1,4 +1,4 @@ -REM This is BadUSB demo script for windows +REM This is BadKB demo script for windows REM Open windows notepad DELAY 1000 @@ -76,7 +76,7 @@ ENTER HOME ENTER -STRING Flipper Zero BadUSB feature is compatible with USB Rubber Ducky script format +STRING Flipper Zero BadKB feature is compatible with USB Rubber Ducky script format ENTER STRING More information about script syntax can be found here: ENTER diff --git a/assets/resources/badusb/layouts/ba-BA.kl b/assets/resources/badkb/layouts/ba-BA.kl similarity index 100% rename from assets/resources/badusb/layouts/ba-BA.kl rename to assets/resources/badkb/layouts/ba-BA.kl diff --git a/assets/resources/badusb/layouts/cz_CS.kl b/assets/resources/badkb/layouts/cz_CS.kl similarity index 100% rename from assets/resources/badusb/layouts/cz_CS.kl rename to assets/resources/badkb/layouts/cz_CS.kl diff --git a/assets/resources/badusb/layouts/da-DA.kl b/assets/resources/badkb/layouts/da-DA.kl similarity index 100% rename from assets/resources/badusb/layouts/da-DA.kl rename to assets/resources/badkb/layouts/da-DA.kl diff --git a/assets/resources/badusb/layouts/de-CH.kl b/assets/resources/badkb/layouts/de-CH.kl similarity index 100% rename from assets/resources/badusb/layouts/de-CH.kl rename to assets/resources/badkb/layouts/de-CH.kl diff --git a/assets/resources/badusb/layouts/de-DE.kl b/assets/resources/badkb/layouts/de-DE.kl similarity index 100% rename from assets/resources/badusb/layouts/de-DE.kl rename to assets/resources/badkb/layouts/de-DE.kl diff --git a/assets/resources/badusb/layouts/dvorak.kl b/assets/resources/badkb/layouts/dvorak.kl similarity index 100% rename from assets/resources/badusb/layouts/dvorak.kl rename to assets/resources/badkb/layouts/dvorak.kl diff --git a/assets/resources/badusb/layouts/en-UK.kl b/assets/resources/badkb/layouts/en-UK.kl similarity index 100% rename from assets/resources/badusb/layouts/en-UK.kl rename to assets/resources/badkb/layouts/en-UK.kl diff --git a/assets/resources/badusb/layouts/en-US.kl b/assets/resources/badkb/layouts/en-US.kl similarity index 100% rename from assets/resources/badusb/layouts/en-US.kl rename to assets/resources/badkb/layouts/en-US.kl diff --git a/assets/resources/badusb/layouts/es-ES.kl b/assets/resources/badkb/layouts/es-ES.kl similarity index 100% rename from assets/resources/badusb/layouts/es-ES.kl rename to assets/resources/badkb/layouts/es-ES.kl diff --git a/assets/resources/badusb/layouts/fr-BE.kl b/assets/resources/badkb/layouts/fr-BE.kl similarity index 100% rename from assets/resources/badusb/layouts/fr-BE.kl rename to assets/resources/badkb/layouts/fr-BE.kl diff --git a/assets/resources/badusb/layouts/fr-CH.kl b/assets/resources/badkb/layouts/fr-CH.kl similarity index 100% rename from assets/resources/badusb/layouts/fr-CH.kl rename to assets/resources/badkb/layouts/fr-CH.kl diff --git a/assets/resources/badusb/layouts/fr-FR.kl b/assets/resources/badkb/layouts/fr-FR.kl similarity index 100% rename from assets/resources/badusb/layouts/fr-FR.kl rename to assets/resources/badkb/layouts/fr-FR.kl diff --git a/assets/resources/badusb/layouts/hr-HR.kl b/assets/resources/badkb/layouts/hr-HR.kl similarity index 100% rename from assets/resources/badusb/layouts/hr-HR.kl rename to assets/resources/badkb/layouts/hr-HR.kl diff --git a/assets/resources/badusb/layouts/hu-HU.kl b/assets/resources/badkb/layouts/hu-HU.kl similarity index 100% rename from assets/resources/badusb/layouts/hu-HU.kl rename to assets/resources/badkb/layouts/hu-HU.kl diff --git a/assets/resources/badusb/layouts/it-IT.kl b/assets/resources/badkb/layouts/it-IT.kl similarity index 100% rename from assets/resources/badusb/layouts/it-IT.kl rename to assets/resources/badkb/layouts/it-IT.kl diff --git a/assets/resources/badusb/layouts/nb-NO.kl b/assets/resources/badkb/layouts/nb-NO.kl similarity index 100% rename from assets/resources/badusb/layouts/nb-NO.kl rename to assets/resources/badkb/layouts/nb-NO.kl diff --git a/assets/resources/badusb/layouts/nl-NL.kl b/assets/resources/badkb/layouts/nl-NL.kl similarity index 100% rename from assets/resources/badusb/layouts/nl-NL.kl rename to assets/resources/badkb/layouts/nl-NL.kl diff --git a/assets/resources/badusb/layouts/pt-BR.kl b/assets/resources/badkb/layouts/pt-BR.kl similarity index 100% rename from assets/resources/badusb/layouts/pt-BR.kl rename to assets/resources/badkb/layouts/pt-BR.kl diff --git a/assets/resources/badusb/layouts/pt-PT.kl b/assets/resources/badkb/layouts/pt-PT.kl similarity index 100% rename from assets/resources/badusb/layouts/pt-PT.kl rename to assets/resources/badkb/layouts/pt-PT.kl diff --git a/assets/resources/badusb/layouts/si-SI.kl b/assets/resources/badkb/layouts/si-SI.kl similarity index 100% rename from assets/resources/badusb/layouts/si-SI.kl rename to assets/resources/badkb/layouts/si-SI.kl diff --git a/assets/resources/badusb/layouts/sk-SK.kl b/assets/resources/badkb/layouts/sk-SK.kl similarity index 100% rename from assets/resources/badusb/layouts/sk-SK.kl rename to assets/resources/badkb/layouts/sk-SK.kl diff --git a/assets/resources/badusb/layouts/sv-SE.kl b/assets/resources/badkb/layouts/sv-SE.kl similarity index 100% rename from assets/resources/badusb/layouts/sv-SE.kl rename to assets/resources/badkb/layouts/sv-SE.kl diff --git a/assets/resources/badusb/layouts/tr-TR.kl b/assets/resources/badkb/layouts/tr-TR.kl similarity index 100% rename from assets/resources/badusb/layouts/tr-TR.kl rename to assets/resources/badkb/layouts/tr-TR.kl diff --git a/assets/resources/badusb/.badusb.settings b/assets/resources/badusb/.badusb.settings deleted file mode 100644 index 34330a3c0..000000000 --- a/assets/resources/badusb/.badusb.settings +++ /dev/null @@ -1 +0,0 @@ -/any/badusb/layouts/en-US.kl From 8a3bd796d4721f6ee91567e92a7e095dfdd21cc1 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 17:44:22 +0000 Subject: [PATCH 072/231] Generic bad kb comments --- applications/main/bad_kb/bad_kb_script.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index e63a39c05..80d203db6 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -680,7 +680,7 @@ static int32_t bad_kb_worker(void* context) { if(furi_hal_hid_is_connected()) { worker_state = BadKbStateIdle; // Ready to run } else { - worker_state = BadKbStateNotConnected; // USB not connected + worker_state = BadKbStateNotConnected; // Not connected } } } else { @@ -692,7 +692,7 @@ static int32_t bad_kb_worker(void* context) { } bad_kb->st.state = worker_state; - } else if(worker_state == BadKbStateNotConnected) { // State: USB not connected + } else if(worker_state == BadKbStateNotConnected) { // State: Not connected uint32_t flags = furi_thread_flags_wait( WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, FuriFlagWaitAny, @@ -703,7 +703,7 @@ static int32_t bad_kb_worker(void* context) { } else if(flags & WorkerEvtConnect) { worker_state = BadKbStateIdle; // Ready to run } else if(flags & WorkerEvtToggle) { - worker_state = BadKbStateWillRun; // Will run when USB is connected + worker_state = BadKbStateWillRun; // Will run when connected } bad_kb->st.state = worker_state; @@ -727,7 +727,7 @@ static int32_t bad_kb_worker(void* context) { bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); worker_state = BadKbStateRunning; } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // USB disconnected + worker_state = BadKbStateNotConnected; // Disconnected } bad_kb->st.state = worker_state; @@ -777,7 +777,7 @@ static int32_t bad_kb_worker(void* context) { furi_hal_hid_kb_release_all(); } } else if(flags & WorkerEvtDisconnect) { - worker_state = BadKbStateNotConnected; // USB disconnected + worker_state = BadKbStateNotConnected; // Disconnected if (bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { From 72935579e6c4863a4eb95072836dc1a6d60bb3ed Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 17:46:32 +0000 Subject: [PATCH 073/231] Format --- .../main/archive/helpers/archive_files.c | 4 +-- applications/main/bad_kb/bad_kb_app.c | 15 ++++---- applications/main/bad_kb/bad_kb_script.c | 36 +++++++++---------- .../bad_kb/scenes/bad_kb_scene_config_bt.c | 21 ++++++----- .../bad_kb/scenes/bad_kb_scene_config_name.c | 3 +- .../bad_kb/scenes/bad_kb_scene_config_usb.c | 15 ++++---- .../bad_kb/scenes/bad_kb_scene_file_select.c | 3 +- .../main/bad_kb/scenes/bad_kb_scene_work.c | 2 +- applications/main/bad_kb/views/bad_kb_view.c | 14 +++----- applications/services/bt/bt_service/bt.c | 7 ++-- firmware/targets/f7/ble_glue/gap.c | 4 +-- 11 files changed, 59 insertions(+), 65 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.c b/applications/main/archive/helpers/archive_files.c index 7e7ab1774..83eb2a845 100644 --- a/applications/main/archive/helpers/archive_files.c +++ b/applications/main/archive/helpers/archive_files.c @@ -17,8 +17,8 @@ void archive_set_file_type(ArchiveFile_t* file, const char* path, bool is_folder if((known_ext[i][0] == '?') || (known_ext[i][0] == '*')) continue; if(furi_string_search(file->path, known_ext[i], 0) != FURI_STRING_FAILURE) { if(i == ArchiveFileTypeBadKb) { - if(furi_string_search( - file->path, archive_get_default_path(ArchiveTabBadKb)) == 0) { + if(furi_string_search(file->path, archive_get_default_path(ArchiveTabBadKb)) == + 0) { file->type = i; return; // *.txt file is a BadKB script only if it is in BadKB folder } diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index c62af4733..4181b08d4 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -112,10 +112,14 @@ BadKbApp* bad_kb_app_alloc(char* arg) { app->var_item_list_bt = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigBt, variable_item_list_get_view(app->var_item_list_bt)); + app->view_dispatcher, + BadKbAppViewConfigBt, + variable_item_list_get_view(app->var_item_list_bt)); app->var_item_list_usb = variable_item_list_alloc(); view_dispatcher_add_view( - app->view_dispatcher, BadKbAppViewConfigUsb, variable_item_list_get_view(app->var_item_list_usb)); + app->view_dispatcher, + BadKbAppViewConfigUsb, + variable_item_list_get_view(app->var_item_list_usb)); app->bad_kb_view = bad_kb_alloc(); view_dispatcher_add_view( @@ -184,15 +188,14 @@ void bad_kb_app_free(BadKbApp* app) { // restores bt config // BtProfile have already been switched to the previous one - // so we directly modify the right profile - if (strcmp(app->bt_old_config.name, app->name) != 0) { + // so we directly modify the right profile + if(strcmp(app->bt_old_config.name, app->name) != 0) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); } - if (memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) { + if(memcmp(app->bt_old_config.mac, app->mac, BAD_KB_MAC_ADDRESS_LEN) != 0) { furi_hal_bt_set_profile_mac_addr(FuriHalBtProfileHidKeyboard, app->bt_old_config.mac); } - // Close records furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index 80d203db6..18aabe860 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -231,8 +231,8 @@ static bool ducky_is_line_end(const char chr) { } static void ducky_numlock_on(BadKbScript* bad_kb) { - if (bad_kb->bt) { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME + if(bad_kb->bt) { + if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); furi_delay_ms(bt_timeout); @@ -251,7 +251,7 @@ static bool ducky_numpad_press(BadKbScript* bad_kb, const char num) { uint16_t key = numpad_keys[num - '0']; FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); - if (bad_kb->bt) { + if(bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); @@ -270,7 +270,7 @@ static bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { FURI_LOG_I(WORKER_TAG, "char %s", charcode); - if (bad_kb->bt) { + if(bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); } else { @@ -283,7 +283,7 @@ static bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { i++; } - if (bad_kb->bt) { + if(bad_kb->bt) { furi_hal_bt_hid_kb_release(KEY_MOD_LEFT_ALT); } else { furi_hal_hid_kb_release(KEY_MOD_LEFT_ALT); @@ -316,7 +316,7 @@ static bool ducky_string(BadKbScript* bad_kb, const char* param) { while(param[i] != '\0') { uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); if(keycode != HID_KEYBOARD_NONE) { - if (bad_kb->bt) { + if(bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(keycode); furi_delay_ms(bt_timeout); @@ -429,7 +429,7 @@ static int32_t // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; uint16_t key = ducky_get_keycode(bad_kb, line_tmp, true); - if (bad_kb->bt) { + if(bad_kb->bt) { bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); @@ -456,7 +456,7 @@ static int32_t line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; key |= ducky_get_keycode(bad_kb, line_tmp, true); } - if (bad_kb->bt) { + if(bad_kb->bt) { furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); @@ -520,7 +520,7 @@ static bool ducky_script_preload(BadKbScript* bad_kb, File* script_file) { } } while(ret > 0); - if (!bad_kb->bt) { + if(!bad_kb->bt) { const char* line_tmp = furi_string_get_cstr(bad_kb->line); bool id_set = false; // Looking for ID command at first line if(strncmp(line_tmp, ducky_cmd_id, strlen(ducky_cmd_id)) == 0) { @@ -643,12 +643,12 @@ static int32_t bad_kb_worker(void* context) { FuriHalUsbInterface* usb_mode_prev = NULL; GapPairing old_pairing_method = GapPairingNone; - if (bad_kb->bt) { + if(bad_kb->bt) { bt_timeout = bt_hid_delays[LevelRssi39_0]; bt_disconnect(bad_kb->bt); furi_delay_ms(200); bt_keys_storage_set_storage_path(bad_kb->bt, HID_BT_KEYS_STORAGE_PATH); - if (!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { + if(!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { FURI_LOG_E(TAG, "Failed to switch to HID profile"); return -1; } @@ -674,7 +674,7 @@ static int32_t bad_kb_worker(void* context) { FSAM_READ, FSOM_OPEN_EXISTING)) { if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { - if (bad_kb->bt) { + if(bad_kb->bt) { worker_state = BadKbStateNotConnected; // Ready to run } else { if(furi_hal_hid_is_connected()) { @@ -750,7 +750,7 @@ static int32_t bad_kb_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - if (bad_kb->bt) { + if(bad_kb->bt) { update_bt_timeout(bad_kb->bt); FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } @@ -771,14 +771,14 @@ static int32_t bad_kb_worker(void* context) { break; } else if(flags & WorkerEvtToggle) { worker_state = BadKbStateIdle; // Stop executing script - if (bad_kb->bt) { + if(bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); } } else if(flags & WorkerEvtDisconnect) { worker_state = BadKbStateNotConnected; // Disconnected - if (bad_kb->bt) { + if(bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); @@ -803,7 +803,7 @@ static int32_t bad_kb_worker(void* context) { delay_val = 0; worker_state = BadKbStateIdle; bad_kb->st.state = BadKbStateDone; - if (bad_kb->bt) { + if(bad_kb->bt) { furi_hal_bt_hid_kb_release_all(); } else { furi_hal_hid_kb_release_all(); @@ -827,13 +827,13 @@ static int32_t bad_kb_worker(void* context) { break; } } - if (bad_kb->bt) { + if(bad_kb->bt) { update_bt_timeout(bad_kb->bt); FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } } - if (bad_kb->bt) { + if(bad_kb->bt) { // release all keys bt_hid_hold_while_keyboard_buffer_full(6, 3000); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c index fc1d19e76..6d01f1df1 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -31,16 +31,14 @@ void bad_kb_scene_config_bt_on_enter(void* context) { variable_item_set_current_value_index(item, bad_kb->is_bt); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - item = variable_item_list_add( - var_item_list, "Keyboard layout", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); - item = variable_item_list_add( - var_item_list, "Change adv name", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "Change adv name", 0, NULL, bad_kb); - item = variable_item_list_add( - var_item_list, "Change MAC address", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "Change MAC address", 0, NULL, bad_kb); - variable_item_list_set_enter_callback(var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb); + variable_item_list_set_enter_callback( + var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigBt); } @@ -56,10 +54,11 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { bad_kb_script_close(bad_kb->bad_kb_script); - bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb->bad_kb_script = + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); scene_manager_previous_scene(bad_kb->scene_manager); - if (bad_kb->is_bt) { + if(bad_kb->is_bt) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); } else { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); @@ -68,8 +67,8 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigName); } else if(event.event == VarItemListIndexMacAddress) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigMac); - // } else { - // furi_crash("Unknown key type"); + // } else { + // furi_crash("Unknown key type"); } } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c index d3d7628b1..82dd7a850 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_name.c @@ -3,8 +3,7 @@ static void bad_kb_scene_config_name_text_input_callback(void* context) { BadKbApp* bad_kb = context; - view_dispatcher_send_custom_event( - bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult); + view_dispatcher_send_custom_event(bad_kb->view_dispatcher, BadKbAppCustomEventTextEditResult); } void bad_kb_scene_config_name_on_enter(void* context) { diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c index 35c5ab8d2..27457e5f2 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c @@ -29,10 +29,10 @@ void bad_kb_scene_config_usb_on_enter(void* context) { variable_item_set_current_value_index(item, bad_kb->is_bt); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); - item = variable_item_list_add( - var_item_list, "Keyboard layout", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); - variable_item_list_set_enter_callback(var_item_list, bad_kb_scene_config_usb_var_item_list_callback, bad_kb); + variable_item_list_set_enter_callback( + var_item_list, bad_kb_scene_config_usb_var_item_list_callback, bad_kb); view_dispatcher_switch_to_view(bad_kb->view_dispatcher, BadKbAppViewConfigUsb); } @@ -48,16 +48,17 @@ bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { bad_kb_script_close(bad_kb->bad_kb_script); - bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb->bad_kb_script = + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); scene_manager_previous_scene(bad_kb->scene_manager); - if (bad_kb->is_bt) { + if(bad_kb->is_bt) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigBt); } else { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigUsb); } - // } else { - // furi_crash("Unknown key type"); + // } else { + // furi_crash("Unknown key type"); } } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c index 44012f68d..df1c93feb 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_file_select.c @@ -29,7 +29,8 @@ void bad_kb_scene_file_select_on_enter(void* context) { } if(bad_kb_file_select(bad_kb)) { - bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); + bad_kb->bad_kb_script = + bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneWork); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_work.c b/applications/main/bad_kb/scenes/bad_kb_scene_work.c index a7eac1786..2f6ae6057 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_work.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_work.c @@ -16,7 +16,7 @@ bool bad_kb_scene_work_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == InputKeyLeft) { - if (app->is_bt) { + if(app->is_bt) { scene_manager_next_scene(app->scene_manager, BadKbSceneConfigBt); } else { scene_manager_next_scene(app->scene_manager, BadKbSceneConfigUsb); diff --git a/applications/main/bad_kb/views/bad_kb_view.c b/applications/main/bad_kb/views/bad_kb_view.c index a0fb39f88..581a703b9 100644 --- a/applications/main/bad_kb/views/bad_kb_view.c +++ b/applications/main/bad_kb/views/bad_kb_view.c @@ -60,8 +60,8 @@ static void bad_kb_draw_callback(Canvas* canvas, void* _model) { elements_button_center(canvas, "Cancel"); } - if((model->state.state == BadKbStateNotConnected) || - (model->state.state == BadKbStateIdle) || (model->state.state == BadKbStateDone)) { + if((model->state.state == BadKbStateNotConnected) || (model->state.state == BadKbStateIdle) || + (model->state.state == BadKbStateDone)) { elements_button_left(canvas, "Config"); } @@ -204,19 +204,13 @@ void bad_kb_set_button_callback(BadKb* bad_kb, BadKbButtonCallback callback, voi void bad_kb_set_file_name(BadKb* bad_kb, const char* name) { furi_assert(name); with_view_model( - bad_kb->view, - BadKbModel * model, - { strlcpy(model->file_name, name, MAX_NAME_LEN); }, - true); + bad_kb->view, BadKbModel * model, { strlcpy(model->file_name, name, MAX_NAME_LEN); }, true); } void bad_kb_set_layout(BadKb* bad_kb, const char* layout) { furi_assert(layout); with_view_model( - bad_kb->view, - BadKbModel * model, - { strlcpy(model->layout, layout, MAX_NAME_LEN); }, - true); + bad_kb->view, BadKbModel * model, { strlcpy(model->layout, layout, MAX_NAME_LEN); }, true); } void bad_kb_set_state(BadKb* bad_kb, BadKbState* st) { diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 6108a7790..6c2fb9307 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -76,10 +76,9 @@ static void bt_pin_code_hide(Bt* bt) { static bool bt_pin_code_verify_event_handler(Bt* bt, uint32_t pin) { furi_assert(bt); - - if (bt_get_profile_pairing_method(bt) == GapPairingNone) - return true; - + + if(bt_get_profile_pairing_method(bt) == GapPairingNone) return true; + notification_message(bt->notification, &sequence_display_backlight_on); FuriString* pin_str; dialog_message_set_icon(bt->dialog_message, XTREME_ASSETS()->I_BLE_Pairing_128x64, 0, 0); diff --git a/firmware/targets/f7/ble_glue/gap.c b/firmware/targets/f7/ble_glue/gap.c index 892fbd2a9..83944b4b5 100644 --- a/firmware/targets/f7/ble_glue/gap.c +++ b/firmware/targets/f7/ble_glue/gap.c @@ -378,7 +378,7 @@ static void gap_init_svc(Gap* gap) { } else if(gap->config->pairing_method == GapPairingPinCodeVerifyYesNo) { aci_gap_set_io_capability(IO_CAP_DISPLAY_YES_NO); keypress_supported = true; - } else if (gap->config->pairing_method == GapPairingNone) { + } else if(gap->config->pairing_method == GapPairingNone) { // Just works pairing method (IOS accept it, it seems android and linux doesn't) conf_mitm = 0; conf_used_fixed_pin = 0; @@ -387,8 +387,6 @@ static void gap_init_svc(Gap* gap) { keypress_supported = true; } - - // Setup authentication aci_gap_set_authentication_requirement( gap->config->bonding_mode, From 52680fd14e025056bedc538d53e2c9e54b4da5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Sat, 4 Feb 2023 01:09:20 +0700 Subject: [PATCH 074/231] FreeRTOS: update to 10.5.1 (#2353) --- firmware/targets/f7/api_symbols.csv | 12 +++++++----- lib/FreeRTOS-Kernel | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 7b247daed..02f476cc0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.9,, +Version,+,11.10,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2108,6 +2108,7 @@ Function,-,putchar_unlocked,int,int Function,-,putenv,int,char* Function,-,puts,int,const char* Function,-,putw,int,"int, FILE*" +Function,-,pvPortCalloc,void*,"size_t, size_t" Function,-,pvPortMalloc,void*,size_t Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, @@ -2797,11 +2798,11 @@ Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" Function,-,vTaskSetTimeOutState,void,TimeOut_t* Function,-,vTaskStartScheduler,void, -Function,-,vTaskStepTick,void,const TickType_t +Function,-,vTaskStepTick,void,TickType_t Function,-,vTaskSuspend,void,TaskHandle_t Function,-,vTaskSuspendAll,void, Function,-,vTaskSwitchContext,void, -Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const UBaseType_t" +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" @@ -2942,12 +2943,13 @@ Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* Function,-,xTaskResumeAll,BaseType_t, Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t -Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t" -Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" Function,-,xTimerCreateTimerTask,BaseType_t, Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t +Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel index 4c4089b15..def7d2df2 160000 --- a/lib/FreeRTOS-Kernel +++ b/lib/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit 4c4089b1544b590ed3b72491a15365ec020c921f +Subproject commit def7d2df2b0506d3d249334974f51e427c17a41c From 6603bc119c434537438577f730ea4aca1f9e04cc Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 3 Feb 2023 19:39:30 +0100 Subject: [PATCH 075/231] Removes storing of bt peer key in internal flash for bad usb BLE --- applications/main/bad_usb/bad_usb_script.c | 6 +++++- applications/services/bt/bt_service/bt.c | 9 +++++++++ applications/services/bt/bt_service/bt.h | 11 +++++++++++ firmware/targets/f7/api_symbols.csv | 4 +++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 51ce49df9..49e5329f0 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -655,6 +655,8 @@ static int32_t bad_usb_worker(void* context) { old_pairing_method = bt_get_profile_pairing_method(bad_usb->bt); bt_set_profile_pairing_method(bad_usb->bt, GapPairingNone); furi_hal_bt_start_advertising(); + // disable peer key adding to bt SRAM storage + bt_disable_peer_key_update(bad_usb->bt); bt_set_status_changed_callback(bad_usb->bt, bad_usb_bt_hid_state_callback, bad_usb); } else { usb_mode_prev = furi_hal_usb_get_config(); @@ -752,7 +754,6 @@ static int32_t bad_usb_worker(void* context) { furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); if (bad_usb->bt) { update_bt_timeout(bad_usb->bt); - FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } bad_usb_script_set_keyboard_layout(bad_usb, bad_usb->keyboard_layout); worker_state = BadUsbStateRunning; @@ -852,6 +853,9 @@ static int32_t bad_usb_worker(void* context) { // fails if ble radio stack isn't ready when switching profile // if it happens, maybe we should increase the delay after bt_disconnect bt_set_profile(bad_usb->bt, BtProfileSerial); + + // starts saving peer keys (bounded devices) + bt_enable_peer_key_update(bad_usb->bt); } else { furi_hal_hid_set_state_callback(NULL, NULL); diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 6108a7790..b002254f0 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -446,6 +446,15 @@ GapPairing bt_get_profile_pairing_method(Bt* bt) { return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); } +void bt_disable_peer_key_update(Bt *bt) { + UNUSED(bt); + furi_hal_bt_set_key_storage_change_callback(NULL, NULL); +} + +void bt_enable_peer_key_update(Bt *bt) { + furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); +} + int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index 046887a2c..ddf9382e8 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -52,6 +52,17 @@ bool bt_remote_rssi(Bt* bt, BtRssi* rssi); void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method); GapPairing bt_get_profile_pairing_method(Bt* bt); +/** Stop saving new peer key to flash (in .bt.keys file) + * +*/ +void bt_disable_peer_key_update(Bt *bt); + +/** Enable saving peer key to internal flash (enable by default) + * + * @note This function should be called if bt_disable_peer_key_update was called before +*/ +void bt_enable_peer_key_update(Bt *bt); + /** Disconnect from Central * * @param bt Bt instance diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2129d0530..8cea0cdd3 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.2,, +Version,v,13.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -569,7 +569,9 @@ Function,+,ble_glue_start,_Bool, Function,+,ble_glue_thread_stop,void, Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disable_peer_key_update,void,Bt* Function,+,bt_disconnect,void,Bt* +Function,+,bt_enable_peer_key_update,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* Function,+,bt_get_profile_adv_name,const char*,Bt* Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* From 935aee90ef91ca3dcc7c3adb2cd97f7d0463877b Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 3 Feb 2023 21:06:55 +0100 Subject: [PATCH 076/231] Bad-KB: allows to go back to script selection and stay connected to the client when using BLE connection mode --- applications/main/bad_kb/bad_kb_app.c | 5 + applications/main/bad_kb/bad_kb_script.c | 108 ++++++++++-------- applications/main/bad_kb/bad_kb_script.h | 1 + firmware/targets/f7/api_symbols.csv | 3 +- firmware/targets/f7/furi_hal/furi_hal_bt.c | 4 + .../targets/furi_hal_include/furi_hal_bt.h | 2 + 6 files changed, 77 insertions(+), 46 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 4181b08d4..a449edf89 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -186,6 +186,11 @@ void bad_kb_app_free(BadKbApp* app) { view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); + // disconnect victim and reset bt config + if (furi_hal_bt_is_connected()) { + bad_kb_script_bt_disconnect_and_reset_config(app->bt, GapPairingPinCodeVerifyYesNo); + } + // restores bt config // BtProfile have already been switched to the previous one // so we directly modify the right profile diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index eb671724d..a34b7e4c3 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -625,6 +625,22 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } +static inline BadKbWorkerState bad_kb_hid_get_initial_state(BadKbScript* bad_kb) { + if(bad_kb->bt) { + if(furi_hal_usb_is_connected(&usb_hid)) { + return BadKbStateIdle; + } else { + return BadKbStateNotConnected; + } + } else { + if(bt_get_status(bad_kb->bt) == BtStatusConnected) { + return BadKbStateIdle; + } else { + return BadKbStateNotConnected; + } + } +} + static void bad_kb_usb_hid_state_callback(bool state, void* context) { furi_assert(context); BadKbScript* bad_kb = context; @@ -635,6 +651,32 @@ static void bad_kb_usb_hid_state_callback(bool state, void* context) { furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); } +void bad_kb_script_bt_disconnect_and_reset_config(Bt* bt, GapPairing old_pairing_method) { + if(bt) { + // release all keys + bt_hid_hold_while_keyboard_buffer_full(6, 3000); + + // stop ble + bt_set_status_changed_callback(bt, NULL, NULL); + + bt_disconnect(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(bt); + + bt_set_profile_pairing_method(bt, old_pairing_method); + + // starts saving peer keys (bounded devices) + bt_enable_peer_key_update(bt); + + // fails if ble radio stack isn't ready when switching profile + // if it happens, maybe we should increase the delay after bt_disconnect + bt_set_profile(bt, BtProfileSerial); + } +} + static int32_t bad_kb_worker(void* context) { BadKbScript* bad_kb = context; @@ -642,22 +684,24 @@ static int32_t bad_kb_worker(void* context) { int32_t delay_val = 0; FuriHalUsbInterface* usb_mode_prev = NULL; - GapPairing old_pairing_method = GapPairingNone; + GapPairing old_pairing_method = GapPairingPinCodeVerifyYesNo; if(bad_kb->bt) { bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(bad_kb->bt); - furi_delay_ms(200); - bt_keys_storage_set_storage_path(bad_kb->bt, HID_BT_KEYS_STORAGE_PATH); - if(!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - return -1; + if (!furi_hal_bt_is_connected()) { + bt_disconnect(bad_kb->bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(bad_kb->bt, HID_BT_KEYS_STORAGE_PATH); + if(!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { + FURI_LOG_E(TAG, "Failed to switch to HID profile"); + return -1; + } + old_pairing_method = bt_get_profile_pairing_method(bad_kb->bt); + bt_set_profile_pairing_method(bad_kb->bt, GapPairingNone); + furi_hal_bt_start_advertising(); + // disable peer key adding to bt SRAM storage + bt_disable_peer_key_update(bad_kb->bt); + bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } - old_pairing_method = bt_get_profile_pairing_method(bad_kb->bt); - bt_set_profile_pairing_method(bad_kb->bt, GapPairingNone); - furi_hal_bt_start_advertising(); - // disable peer key adding to bt SRAM storage - bt_disable_peer_key_update(bad_usb->bt); - bt_set_status_changed_callback(bad_usb->bt, bad_usb_bt_hid_state_callback, bad_usb); } else { usb_mode_prev = furi_hal_usb_get_config(); furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); @@ -676,15 +720,7 @@ static int32_t bad_kb_worker(void* context) { FSAM_READ, FSOM_OPEN_EXISTING)) { if((ducky_script_preload(bad_kb, script_file)) && (bad_kb->st.line_nb > 0)) { - if(bad_kb->bt) { - worker_state = BadKbStateNotConnected; // Ready to run - } else { - if(furi_hal_hid_is_connected()) { - worker_state = BadKbStateIdle; // Ready to run - } else { - worker_state = BadKbStateNotConnected; // Not connected - } - } + worker_state = bad_kb_hid_get_initial_state(bad_kb); } else { worker_state = BadKbStateScriptError; // Script preload error } @@ -752,8 +788,8 @@ static int32_t bad_kb_worker(void* context) { storage_file_seek(script_file, 0, true); // extra time for PC to recognize Flipper as keyboard furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); - if(bad_usb->bt) { - update_bt_timeout(bad_usb->bt); + if(bad_kb->bt) { + update_bt_timeout(bad_kb->bt); } bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); worker_state = BadKbStateRunning; @@ -835,27 +871,9 @@ static int32_t bad_kb_worker(void* context) { } if(bad_kb->bt) { - // release all keys - bt_hid_hold_while_keyboard_buffer_full(6, 3000); - - // stop ble - bt_set_status_changed_callback(bad_kb->bt, NULL, NULL); - - bt_disconnect(bad_kb->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - bt_keys_storage_set_default_path(bad_kb->bt); - - bt_set_profile_pairing_method(bad_kb->bt, old_pairing_method); - - // fails if ble radio stack isn't ready when switching profile - // if it happens, maybe we should increase the delay after bt_disconnect - bt_set_profile(bad_usb->bt, BtProfileSerial); - - // starts saving peer keys (bounded devices) - bt_enable_peer_key_update(bad_usb->bt); + if (!furi_hal_bt_is_connected()) { + bad_kb_script_bt_disconnect_and_reset_config(bad_kb->bt, old_pairing_method); + } } else { furi_hal_hid_set_state_callback(NULL, NULL); diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h index 35c57c112..aa18dbe9e 100644 --- a/applications/main/bad_kb/bad_kb_script.h +++ b/applications/main/bad_kb/bad_kb_script.h @@ -44,6 +44,7 @@ void bad_kb_script_toggle(BadKbScript* bad_kb); BadKbState* bad_kb_script_get_state(BadKbScript* bad_kb); +void bad_kb_script_bt_disconnect_and_reset_config(Bt *bt, GapPairing old_pairing_method); #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8cea0cdd3..32ec5f4ed 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,13.4,, +Version,v,13.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1030,6 +1030,7 @@ Function,-,furi_hal_bt_init,void, Function,+,furi_hal_bt_is_active,_Bool, Function,+,furi_hal_bt_is_alive,_Bool, Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_connected,_Bool, Function,+,furi_hal_bt_is_testing_supported,_Bool, Function,+,furi_hal_bt_lock_core2,void, Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index f33c92c62..e01669eb8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -287,6 +287,10 @@ bool furi_hal_bt_is_active() { return gap_get_state() > GapStateIdle; } +bool furi_hal_bt_is_connected() { + return gap_get_state() == GapStateConnected; +} + void furi_hal_bt_start_advertising() { if(gap_get_state() == GapStateIdle) { gap_start_advertising(); diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 3e554bb4f..03b9eb863 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -250,6 +250,8 @@ void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile); +bool furi_hal_bt_is_connected(void); + #ifdef __cplusplus } #endif From dc38d57d6e31a9acc02cead314e6b704459ae8ed Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 3 Feb 2023 21:20:44 +0100 Subject: [PATCH 077/231] Fix: vs-code and copilot mistakes (oups) --- applications/main/bad_kb/bad_kb_script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index a34b7e4c3..6d5c7478c 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -627,13 +627,13 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { static inline BadKbWorkerState bad_kb_hid_get_initial_state(BadKbScript* bad_kb) { if(bad_kb->bt) { - if(furi_hal_usb_is_connected(&usb_hid)) { + if(furi_hal_bt_is_connected()) { return BadKbStateIdle; } else { return BadKbStateNotConnected; } } else { - if(bt_get_status(bad_kb->bt) == BtStatusConnected) { + if(furi_hal_hid_is_connected()) { return BadKbStateIdle; } else { return BadKbStateNotConnected; From a67af49f548c761ac8e207c64ea87b9bd77be5d7 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 3 Feb 2023 22:42:37 +0100 Subject: [PATCH 078/231] Update FreeRTOS-Kernel --- lib/FreeRTOS-Kernel | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/FreeRTOS-Kernel b/lib/FreeRTOS-Kernel index def7d2df2..4c4089b15 160000 --- a/lib/FreeRTOS-Kernel +++ b/lib/FreeRTOS-Kernel @@ -1 +1 @@ -Subproject commit def7d2df2b0506d3d249334974f51e427c17a41c +Subproject commit 4c4089b1544b590ed3b72491a15365ec020c921f From 82f4a287cbce1c48d6fe10827bb8cabfb6a2a447 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 22:19:04 +0000 Subject: [PATCH 079/231] Stay connected in bad kb --- applications/main/bad_kb/bad_kb_app.c | 1 + applications/main/bad_kb/bad_kb_script.c | 148 ++++++++++++------ applications/main/bad_kb/bad_kb_script.h | 4 + .../bad_kb/scenes/bad_kb_scene_config_bt.c | 1 + .../bad_kb/scenes/bad_kb_scene_config_usb.c | 1 + 5 files changed, 109 insertions(+), 46 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 4181b08d4..09b7d41a4 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -155,6 +155,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { void bad_kb_app_free(BadKbApp* app) { furi_assert(app); + bad_kb_connection_deinit(app->bt); if(app->bad_kb_script) { bad_kb_script_close(app->bad_kb_script); app->bad_kb_script = NULL; diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index 18aabe860..3bfbb158c 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -41,6 +41,12 @@ typedef enum { LevelRssiError = 0xFF, } LevelRssiRange; +typedef enum { + BadKbConnectionModeNone, + BadKbConnectionModeUsb, + BadKbConnectionModeBt, +} BadKbConnectionMode; + /** * Delays for waiting between HID key press and key release */ @@ -164,6 +170,11 @@ static const uint8_t numpad_keys[10] = { HID_KEYPAD_9, }; +BadKbConnectionMode connection_mode = BadKbConnectionModeNone; +FuriHalUsbInterface* usb_mode_prev = NULL; +GapPairing bt_mode_prev = GapPairingNone; +bool bt_connected = false; +bool usb_connected = false; uint8_t bt_timeout = 0; static LevelRssiRange bt_remote_rssi_range(Bt* bt) { @@ -620,19 +631,91 @@ static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { if(r != LevelRssiError) { bt_timeout = bt_hid_delays[r]; } + bt_connected = true; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); - } else + } else { + bt_connected = false; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); + } } static void bad_kb_usb_hid_state_callback(bool state, void* context) { furi_assert(context); BadKbScript* bad_kb = context; - if(state == true) + if(state == true) { + usb_connected = true; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtConnect); - else + } else { + usb_connected = false; furi_thread_flags_set(furi_thread_get_id(bad_kb->thread), WorkerEvtDisconnect); + } +} + +void bad_kb_bt_init(Bt* bt) { + bt_timeout = bt_hid_delays[LevelRssi39_0]; + bt_disconnect(bt); + furi_delay_ms(200); + bt_keys_storage_set_storage_path(bt, HID_BT_KEYS_STORAGE_PATH); + furi_assert(bt_set_profile(bt, BtProfileHidKeyboard)); + bt_mode_prev = bt_get_profile_pairing_method(bt); + bt_set_profile_pairing_method(bt, GapPairingNone); + furi_hal_bt_start_advertising(); + + connection_mode = BadKbConnectionModeBt; +} + +void bad_kb_bt_deinit(Bt* bt) { + // release all keys + bt_hid_hold_while_keyboard_buffer_full(6, 3000); + + // stop ble + bt_disconnect(bt); + + // Wait 2nd core to update nvm storage + furi_delay_ms(200); + + bt_keys_storage_set_default_path(bt); + + bt_set_profile_pairing_method(bt, bt_mode_prev); + + // fails if ble radio stack isn't ready when switching profile + // if it happens, maybe we should increase the delay after bt_disconnect + bt_set_profile(bt, BtProfileSerial); + + connection_mode = BadKbConnectionModeNone; +} + +void bad_kb_usb_init() { + usb_mode_prev = furi_hal_usb_get_config(); + + connection_mode = BadKbConnectionModeUsb; +} + +void bad_kb_usb_deinit() { + furi_hal_usb_set_config(usb_mode_prev, NULL); + + connection_mode = BadKbConnectionModeNone; +} + +void bad_kb_connection_init(Bt* bt) { + if(connection_mode != BadKbConnectionModeNone) return; + + if(bt) { + bad_kb_bt_init(bt); + } else { + bad_kb_usb_init(); + } +} + +void bad_kb_connection_deinit(Bt* bt) { + if(connection_mode == BadKbConnectionModeNone) return; + + if(connection_mode == BadKbConnectionModeBt) { + bad_kb_bt_deinit(bt); + } else { + bad_kb_usb_deinit(); + } } static int32_t bad_kb_worker(void* context) { @@ -641,23 +724,11 @@ static int32_t bad_kb_worker(void* context) { BadKbWorkerState worker_state = BadKbStateInit; int32_t delay_val = 0; - FuriHalUsbInterface* usb_mode_prev = NULL; - GapPairing old_pairing_method = GapPairingNone; + bad_kb_connection_init(bad_kb->bt); + if(bad_kb->bt) { - bt_timeout = bt_hid_delays[LevelRssi39_0]; - bt_disconnect(bad_kb->bt); - furi_delay_ms(200); - bt_keys_storage_set_storage_path(bad_kb->bt, HID_BT_KEYS_STORAGE_PATH); - if(!bt_set_profile(bad_kb->bt, BtProfileHidKeyboard)) { - FURI_LOG_E(TAG, "Failed to switch to HID profile"); - return -1; - } - old_pairing_method = bt_get_profile_pairing_method(bad_kb->bt); - bt_set_profile_pairing_method(bad_kb->bt, GapPairingNone); - furi_hal_bt_start_advertising(); bt_set_status_changed_callback(bad_kb->bt, bad_kb_bt_hid_state_callback, bad_kb); } else { - usb_mode_prev = furi_hal_usb_get_config(); furi_hal_hid_set_state_callback(bad_kb_usb_hid_state_callback, bad_kb); } @@ -693,17 +764,21 @@ static int32_t bad_kb_worker(void* context) { bad_kb->st.state = worker_state; } else if(worker_state == BadKbStateNotConnected) { // State: Not connected - uint32_t flags = furi_thread_flags_wait( - WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, - FuriFlagWaitAny, - FuriWaitForever); - furi_check((flags & FuriFlagError) == 0); - if(flags & WorkerEvtEnd) { - break; - } else if(flags & WorkerEvtConnect) { + if((bad_kb->bt && bt_connected) || (!bad_kb->bt && usb_connected)) { worker_state = BadKbStateIdle; // Ready to run - } else if(flags & WorkerEvtToggle) { - worker_state = BadKbStateWillRun; // Will run when connected + } else { + uint32_t flags = furi_thread_flags_wait( + WorkerEvtEnd | WorkerEvtConnect | WorkerEvtToggle, + FuriFlagWaitAny, + FuriWaitForever); + furi_check((flags & FuriFlagError) == 0); + if(flags & WorkerEvtEnd) { + break; + } else if(flags & WorkerEvtConnect) { + worker_state = BadKbStateIdle; // Ready to run + } else if(flags & WorkerEvtToggle) { + worker_state = BadKbStateWillRun; // Will run when connected + } } bad_kb->st.state = worker_state; @@ -834,28 +909,9 @@ static int32_t bad_kb_worker(void* context) { } if(bad_kb->bt) { - // release all keys - bt_hid_hold_while_keyboard_buffer_full(6, 3000); - - // stop ble bt_set_status_changed_callback(bad_kb->bt, NULL, NULL); - - bt_disconnect(bad_kb->bt); - - // Wait 2nd core to update nvm storage - furi_delay_ms(200); - - bt_keys_storage_set_default_path(bad_kb->bt); - - bt_set_profile_pairing_method(bad_kb->bt, old_pairing_method); - - // fails if ble radio stack isn't ready when switching profile - // if it happens, maybe we should increase the delay after bt_disconnect - bt_set_profile(bad_kb->bt, BtProfileSerial); } else { furi_hal_hid_set_state_callback(NULL, NULL); - - furi_hal_usb_set_config(usb_mode_prev, NULL); } storage_file_close(script_file); diff --git a/applications/main/bad_kb/bad_kb_script.h b/applications/main/bad_kb/bad_kb_script.h index 35c57c112..0ea701eb8 100644 --- a/applications/main/bad_kb/bad_kb_script.h +++ b/applications/main/bad_kb/bad_kb_script.h @@ -30,6 +30,10 @@ typedef struct { char error[64]; } BadKbState; +void bad_kb_connection_init(Bt* bt); + +void bad_kb_connection_deinit(Bt* bt); + BadKbScript* bad_kb_script_open(FuriString* file_path, Bt* bt); void bad_kb_script_close(BadKbScript* bad_kb); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c index 6d01f1df1..9b4d8041e 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -54,6 +54,7 @@ bool bad_kb_scene_config_bt_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb_connection_deinit(bad_kb->bt); bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c index 27457e5f2..fc265b64e 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c @@ -48,6 +48,7 @@ bool bad_kb_scene_config_usb_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(bad_kb->scene_manager, BadKbSceneConfigLayout); } else if(event.event == VarItemListIndexConnection) { bad_kb_script_close(bad_kb->bad_kb_script); + bad_kb_connection_deinit(bad_kb->bt); bad_kb->bad_kb_script = bad_kb_script_open(bad_kb->file_path, bad_kb->is_bt ? bad_kb->bt : NULL); bad_kb_script_set_keyboard_layout(bad_kb->bad_kb_script, bad_kb->keyboard_layout); From d6a1dc2e1f986265cfc6c8b30c35fbdfb01abb40 Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 3 Feb 2023 19:39:30 +0100 Subject: [PATCH 080/231] Removes storing of bt peer key in internal flash for bad usb BLE Co-authored-by: yocvito --- applications/main/bad_kb/bad_kb_script.c | 6 +++++- applications/services/bt/bt_service/bt.c | 9 +++++++++ applications/services/bt/bt_service/bt.h | 11 +++++++++++ firmware/targets/f7/api_symbols.csv | 4 +++- 4 files changed, 28 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index 3bfbb158c..cc548ae27 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -661,6 +661,8 @@ void bad_kb_bt_init(Bt* bt) { bt_mode_prev = bt_get_profile_pairing_method(bt); bt_set_profile_pairing_method(bt, GapPairingNone); furi_hal_bt_start_advertising(); + // disable peer key adding to bt SRAM storage + bt_disable_peer_key_update(bad_usb->bt); connection_mode = BadKbConnectionModeBt; } @@ -683,6 +685,9 @@ void bad_kb_bt_deinit(Bt* bt) { // if it happens, maybe we should increase the delay after bt_disconnect bt_set_profile(bt, BtProfileSerial); + // starts saving peer keys (bounded devices) + bt_enable_peer_key_update(bad_usb->bt); + connection_mode = BadKbConnectionModeNone; } @@ -827,7 +832,6 @@ static int32_t bad_kb_worker(void* context) { furi_thread_flags_wait(0, FuriFlagWaitAny, 1500); if(bad_kb->bt) { update_bt_timeout(bad_kb->bt); - FURI_LOG_I(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } bad_kb_script_set_keyboard_layout(bad_kb, bad_kb->keyboard_layout); worker_state = BadKbStateRunning; diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 6c2fb9307..cc5e8f33e 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -445,6 +445,15 @@ GapPairing bt_get_profile_pairing_method(Bt* bt) { return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); } +void bt_disable_peer_key_update(Bt *bt) { + UNUSED(bt); + furi_hal_bt_set_key_storage_change_callback(NULL, NULL); +} + +void bt_enable_peer_key_update(Bt *bt) { + furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); +} + int32_t bt_srv(void* p) { UNUSED(p); Bt* bt = bt_alloc(); diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index 046887a2c..ddf9382e8 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -52,6 +52,17 @@ bool bt_remote_rssi(Bt* bt, BtRssi* rssi); void bt_set_profile_pairing_method(Bt* bt, GapPairing pairing_method); GapPairing bt_get_profile_pairing_method(Bt* bt); +/** Stop saving new peer key to flash (in .bt.keys file) + * +*/ +void bt_disable_peer_key_update(Bt *bt); + +/** Enable saving peer key to internal flash (enable by default) + * + * @note This function should be called if bt_disable_peer_key_update was called before +*/ +void bt_enable_peer_key_update(Bt *bt); + /** Disconnect from Central * * @param bt Bt instance diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 2129d0530..8cea0cdd3 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.2,, +Version,v,13.4,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -569,7 +569,9 @@ Function,+,ble_glue_start,_Bool, Function,+,ble_glue_thread_stop,void, Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disable_peer_key_update,void,Bt* Function,+,bt_disconnect,void,Bt* +Function,+,bt_enable_peer_key_update,void,Bt* Function,+,bt_forget_bonded_devices,void,Bt* Function,+,bt_get_profile_adv_name,const char*,Bt* Function,+,bt_get_profile_mac_address,const uint8_t*,Bt* From 08475ff579d633d94fffda4494f12b7c872075b0 Mon Sep 17 00:00:00 2001 From: yocvito Date: Fri, 3 Feb 2023 21:06:55 +0100 Subject: [PATCH 081/231] Add bt is connected check --- firmware/targets/f7/api_symbols.csv | 3 ++- firmware/targets/f7/furi_hal/furi_hal_bt.c | 4 ++++ firmware/targets/furi_hal_include/furi_hal_bt.h | 2 ++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 8cea0cdd3..95bcce708 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,13.4,, +Version,+,13.5,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1030,6 +1030,7 @@ Function,-,furi_hal_bt_init,void, Function,+,furi_hal_bt_is_active,_Bool, Function,+,furi_hal_bt_is_alive,_Bool, Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_connected,_Bool, Function,+,furi_hal_bt_is_testing_supported,_Bool, Function,+,furi_hal_bt_lock_core2,void, Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index f33c92c62..e01669eb8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -287,6 +287,10 @@ bool furi_hal_bt_is_active() { return gap_get_state() > GapStateIdle; } +bool furi_hal_bt_is_connected() { + return gap_get_state() == GapStateConnected; +} + void furi_hal_bt_start_advertising() { if(gap_get_state() == GapStateIdle) { gap_start_advertising(); diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 3e554bb4f..03b9eb863 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -250,6 +250,8 @@ void furi_hal_bt_set_profile_pairing_method(FuriHalBtProfile profile, GapPairing GapPairing furi_hal_bt_get_profile_pairing_method(FuriHalBtProfile profile); +bool furi_hal_bt_is_connected(void); + #ifdef __cplusplus } #endif From 92c83cfb798f6b95e7deaa4cec5f88b29f774203 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 22:40:29 +0000 Subject: [PATCH 082/231] Fix typo --- applications/main/bad_kb/bad_kb_script.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index cc548ae27..f27e1a268 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -662,7 +662,7 @@ void bad_kb_bt_init(Bt* bt) { bt_set_profile_pairing_method(bt, GapPairingNone); furi_hal_bt_start_advertising(); // disable peer key adding to bt SRAM storage - bt_disable_peer_key_update(bad_usb->bt); + bt_disable_peer_key_update(bt); connection_mode = BadKbConnectionModeBt; } @@ -686,7 +686,7 @@ void bad_kb_bt_deinit(Bt* bt) { bt_set_profile(bt, BtProfileSerial); // starts saving peer keys (bounded devices) - bt_enable_peer_key_update(bad_usb->bt); + bt_enable_peer_key_update(bt); connection_mode = BadKbConnectionModeNone; } From e97659819d64c0b5c833bde3d127b07e277f4664 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 23:10:50 +0000 Subject: [PATCH 083/231] Change bad bt config options names --- applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c index 9b4d8041e..394783828 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -33,9 +33,9 @@ void bad_kb_scene_config_bt_on_enter(void* context) { item = variable_item_list_add(var_item_list, "Keyboard layout", 0, NULL, bad_kb); - item = variable_item_list_add(var_item_list, "Change adv name", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT device name", 0, NULL, bad_kb); - item = variable_item_list_add(var_item_list, "Change MAC address", 0, NULL, bad_kb); + item = variable_item_list_add(var_item_list, "BT MAC address", 0, NULL, bad_kb); variable_item_list_set_enter_callback( var_item_list, bad_kb_scene_config_bt_var_item_list_callback, bad_kb); From 9a02a87e829421443b12918b34d7f8f2fb49ca51 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 3 Feb 2023 23:37:44 +0000 Subject: [PATCH 084/231] Fix worker interface bug on bad kb quit --- applications/main/bad_kb/bad_kb_app.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 09b7d41a4..0a47b7492 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -155,7 +155,6 @@ BadKbApp* bad_kb_app_alloc(char* arg) { void bad_kb_app_free(BadKbApp* app) { furi_assert(app); - bad_kb_connection_deinit(app->bt); if(app->bad_kb_script) { bad_kb_script_close(app->bad_kb_script); app->bad_kb_script = NULL; @@ -190,6 +189,7 @@ void bad_kb_app_free(BadKbApp* app) { // restores bt config // BtProfile have already been switched to the previous one // so we directly modify the right profile + bad_kb_connection_deinit(app->bt); if(strcmp(app->bt_old_config.name, app->name) != 0) { furi_hal_bt_set_profile_adv_name(FuriHalBtProfileHidKeyboard, app->bt_old_config.name); } From 1e6e7b8669d725b1cfb2b6053e0d8f5c78d517ca Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 13:56:53 +0000 Subject: [PATCH 085/231] Update symbols --- firmware/targets/f7/api_symbols.csv | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index b154c5c76..e71a86827 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.6,, +Version,+,12.7,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2188,7 +2188,6 @@ Function,-,putchar_unlocked,int,int Function,-,putenv,int,char* Function,-,puts,int,const char* Function,-,putw,int,"int, FILE*" -Function,-,pvPortCalloc,void*,"size_t, size_t" Function,-,pvPortMalloc,void*,size_t Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, @@ -4401,11 +4400,11 @@ Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" Function,-,vTaskSetTimeOutState,void,TimeOut_t* Function,-,vTaskStartScheduler,void, -Function,-,vTaskStepTick,void,TickType_t +Function,-,vTaskStepTick,void,const TickType_t Function,-,vTaskSuspend,void,TaskHandle_t Function,-,vTaskSuspendAll,void, Function,-,vTaskSwitchContext,void, -Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const UBaseType_t" Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" @@ -4563,13 +4562,12 @@ Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* Function,-,xTaskResumeAll,BaseType_t, Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t -Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" -Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const UBaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" Function,-,xTimerCreateTimerTask,BaseType_t, Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t -Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" From 9f279ac87268365e39a1571f60c42be94cd7d3f8 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Mon, 6 Feb 2023 17:03:29 +0300 Subject: [PATCH 086/231] [FL-2744] SPI Mem Manager C port (#1860) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .gitignore | 1 + .../plugins/spi_mem_manager/application.fam | 17 + .../images/ChipLooking_64x64/frame_01.png | Bin 0 -> 7231 bytes .../images/ChipLooking_64x64/frame_02.png | Bin 0 -> 6909 bytes .../images/ChipLooking_64x64/frame_03.png | Bin 0 -> 7308 bytes .../images/ChipLooking_64x64/frame_rate | 1 + .../spi_mem_manager/images/Dip8_10px.png | Bin 0 -> 195 bytes .../spi_mem_manager/images/Dip8_32x36.png | Bin 0 -> 977 bytes .../images/DolphinMafia_115x62.png | Bin 0 -> 2504 bytes .../images/DolphinNice_96x59.png | Bin 0 -> 2459 bytes .../images/SDQuestion_35x43.png | Bin 0 -> 1950 bytes .../images/Wiring_SPI_128x64.png | Bin 0 -> 4446 bytes .../spi_mem_manager/lib/spi/spi_mem_chip.c | 105 ++ .../spi_mem_manager/lib/spi/spi_mem_chip.h | 34 + .../lib/spi/spi_mem_chip_arr.c | 1399 +++++++++++++++++ .../spi_mem_manager/lib/spi/spi_mem_chip_i.h | 85 + .../spi_mem_manager/lib/spi/spi_mem_tools.c | 152 ++ .../spi_mem_manager/lib/spi/spi_mem_tools.h | 14 + .../spi_mem_manager/lib/spi/spi_mem_worker.c | 129 ++ .../spi_mem_manager/lib/spi/spi_mem_worker.h | 54 + .../lib/spi/spi_mem_worker_i.h | 24 + .../lib/spi/spi_mem_worker_modes.c | 214 +++ .../spi_mem_manager/scenes/spi_mem_scene.c | 30 + .../spi_mem_manager/scenes/spi_mem_scene.h | 29 + .../scenes/spi_mem_scene_about.c | 42 + .../scenes/spi_mem_scene_chip_detect.c | 37 + .../scenes/spi_mem_scene_chip_detect_fail.c | 57 + .../scenes/spi_mem_scene_chip_detected.c | 94 ++ .../scenes/spi_mem_scene_chip_error.c | 52 + .../scenes/spi_mem_scene_config.h | 21 + .../scenes/spi_mem_scene_delete_confirm.c | 62 + .../scenes/spi_mem_scene_erase.c | 65 + .../scenes/spi_mem_scene_file_info.c | 29 + .../scenes/spi_mem_scene_read.c | 57 + .../scenes/spi_mem_scene_read_filename.c | 46 + .../scenes/spi_mem_scene_saved_file_menu.c | 76 + .../scenes/spi_mem_scene_select_file.c | 22 + .../scenes/spi_mem_scene_select_model.c | 45 + .../scenes/spi_mem_scene_select_vendor.c | 70 + .../scenes/spi_mem_scene_start.c | 84 + .../scenes/spi_mem_scene_storage_error.c | 56 + .../scenes/spi_mem_scene_success.c | 40 + .../scenes/spi_mem_scene_verify.c | 59 + .../scenes/spi_mem_scene_verify_error.c | 43 + .../scenes/spi_mem_scene_wiring.c | 18 + .../scenes/spi_mem_scene_write.c | 58 + .../plugins/spi_mem_manager/spi_mem_app.c | 112 ++ .../plugins/spi_mem_manager/spi_mem_app.h | 3 + .../plugins/spi_mem_manager/spi_mem_app_i.h | 79 + .../plugins/spi_mem_manager/spi_mem_files.c | 74 + .../plugins/spi_mem_manager/spi_mem_files.h | 14 + .../plugins/spi_mem_manager/tools/README.md | 7 + .../spi_mem_manager/tools/chiplist/LICENSE | 22 + .../tools/chiplist/chiplist.xml | 984 ++++++++++++ .../spi_mem_manager/tools/chiplist_convert.py | 109 ++ .../views/spi_mem_view_detect.c | 64 + .../views/spi_mem_view_detect.h | 9 + .../views/spi_mem_view_progress.c | 230 +++ .../views/spi_mem_view_progress.h | 26 + 59 files changed, 5154 insertions(+) create mode 100644 applications/plugins/spi_mem_manager/application.fam create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_02.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png create mode 100644 applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate create mode 100644 applications/plugins/spi_mem_manager/images/Dip8_10px.png create mode 100644 applications/plugins/spi_mem_manager/images/Dip8_32x36.png create mode 100644 applications/plugins/spi_mem_manager/images/DolphinMafia_115x62.png create mode 100644 applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png create mode 100644 applications/plugins/spi_mem_manager/images/SDQuestion_35x43.png create mode 100644 applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h create mode 100644 applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c create mode 100644 applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app.h create mode 100644 applications/plugins/spi_mem_manager/spi_mem_app_i.h create mode 100644 applications/plugins/spi_mem_manager/spi_mem_files.c create mode 100644 applications/plugins/spi_mem_manager/spi_mem_files.h create mode 100644 applications/plugins/spi_mem_manager/tools/README.md create mode 100644 applications/plugins/spi_mem_manager/tools/chiplist/LICENSE create mode 100644 applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml create mode 100755 applications/plugins/spi_mem_manager/tools/chiplist_convert.py create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c create mode 100644 applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h diff --git a/.gitignore b/.gitignore index d54ed4a19..542652eb0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.swp +*.swo *.gdb_history diff --git a/applications/plugins/spi_mem_manager/application.fam b/applications/plugins/spi_mem_manager/application.fam new file mode 100644 index 000000000..09d801876 --- /dev/null +++ b/applications/plugins/spi_mem_manager/application.fam @@ -0,0 +1,17 @@ +App( + appid="spi_mem_manager", + name="SPI Mem Manager", + apptype=FlipperAppType.EXTERNAL, + entry_point="spi_mem_app", + requires=["gui"], + stack_size=1 * 2048, + order=30, + fap_icon="images/Dip8_10px.png", + fap_category="Tools", + fap_icon_assets="images", + fap_private_libs=[ + Lib( + name="spi", + ), + ], +) diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..4ff2e3042e540d8b499118e9edf4ecd180b39511 GIT binary patch literal 7231 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 NY%KPgSDX4C`yZHc9FG71 literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_03.png new file mode 100644 index 0000000000000000000000000000000000000000..1342dc7bf95265db5d0ab2924e1ef03fe4df9943 GIT binary patch literal 7308 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate new file mode 100644 index 000000000..d8263ee98 --- /dev/null +++ b/applications/plugins/spi_mem_manager/images/ChipLooking_64x64/frame_rate @@ -0,0 +1 @@ +2 \ No newline at end of file diff --git a/applications/plugins/spi_mem_manager/images/Dip8_10px.png b/applications/plugins/spi_mem_manager/images/Dip8_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..9de9364d13c5cbd7fe43a6df59789193d9133057 GIT binary patch literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V6Od#Ih{XE z)7O>#85cXB6sN>NS&&A_64!_l=ltB<)VvY~=c3falGGH1^30M91$R&1fbd2>aiAhw zPZ!4!iOaqHj$8}|9EV^1`~UonL4lUvGtG!wL5x*034X4|#-7<_>P#}{{_l#8SgPZ+ js&kpz9*+3KCj7Uj_`9EfRa>$PXb^*^tDnm{r-UW|gHJf_ literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/Dip8_32x36.png b/applications/plugins/spi_mem_manager/images/Dip8_32x36.png new file mode 100644 index 0000000000000000000000000000000000000000..8f01af2760363b31029cbc425d9a9f2e2becf96b GIT binary patch literal 977 zcmeAS@N?(olHy`uVBq!ia0vp^3P7yF!3-qto1VD9z`)E9;1l8s2$&H|6fVg?3oVGw3ym^DWNC|K_4;uvDl`*xDGP=kVibG*#E`lk^cO$zev z+}xJ~R3@KooBBeTVTU7E1RI;jy{)QdNiPgUSiTrM5ocMrlj*@Cjo*LH^-f?g|F=lv zl)Fs&L8nKli4M~yCl?8HDNptH;hVjxE12WemhD};j?@R{i;C*6a`dzQvVm!LtxQw_ S>l}Mfba=Y@xvXZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png b/applications/plugins/spi_mem_manager/images/DolphinNice_96x59.png new file mode 100644 index 0000000000000000000000000000000000000000..a299d3630239b4486e249cc501872bed5996df3b GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!Q8gyJRa{_+4UIP z$!=1i2t*O^Q!1)9DGybMBGF3a6SW|L6h5LVfJD{LegT4thW>zPQHvPws{yrXept!t zv3=&;bMHMf^XAC#V8_NS8##{a$PeX4*}aQh-5b`i|CfLH7_-|w{?PLw$KCsIeBHqv zeQy)T-TjJN7>~xyod%|r1hT0`619rY&>XjYN6klgf<(MUimsOtE`R=|z`J%v*qt1RpF9hwQq*vxPN&rD$57IyUT+iM0RsE`QpwMy9wjao*i^BQa%zm^2P4v8i*LT?<9 zA2&z%EDZ>+C4h(lkolCJfSRgm;7MKvGLS%0g0cuT1E>Z}@y(yWq6M~NjOGTKvDi~a zC`FNPNK&<0O;nWx4T=)fbzK6oB+DX0h~cysp_=H0T`h(j331^1kxM;3W<(a9j4}dK z+DM_|w`skwSteF6sfK(BCP1803uv0FLo1awI*j_KSd^yTn-YhGX`e`=B&3r8CjC>y zi@I9D{1T05SfaPk*8co2g*I*n^e2OIy*xISNSRa^cgV1?uFp5J0YMQB3Y3;xjT&i1 zyC$M##e?pUVhLRKj&_L)9?Wld>u%HCq;SStTN}Y*kccThRe>U`i)-U2J}i z;>oxY@%)BuZHgI3yPAgMs43vsNJP47iHfQ!qLpHl$)u9T2onYB=@#3rz-223l~=OH zs_a-*O3@VL0MW4s7KyDoOqHyNDzSA4a2n~Dsk#w2OUpDcsm-dZtbCu(W=8_*xMlVs z93AZA^Zi*3>Y66X2`KP3HXIsM5Hp%vK}90@UNN>klflv*azobR>E=QjBQG^aWtXqJ z(?B?06d3`>ZXmYMeC^(>%xg-hL0c^mM!Jei8nBQ$Q56NGx5!#@TNg^V5+9y5Z&x22##B&{B?u64ye+M3KZ=XlsY71%@jTp=Dy zHDISkcY5&@J8>5Bx!%I~{^i3@-@m}$cNaW+{nKk_j+x(U>F+k}c?6!^@X+fADi3lO z_rLKG{`yum;ZU>ayk!Z+q>VzZOn^bqQ>?UO0Drz@_TNg$rjt zNcQEpt?wS&gMaKe^8V2SorGL{ZtT%R?yPd~`n9FG(}zAOOPl5My;t&Z3(*F9I?g}- zvp)ag@#QCe{o%9hrGrn+&dpu9`rFcD;MqUV>^-?JG4|Gpeamy-PL6)jzu5T`{Cd7~ fwr}T&J8Rt1;5!ej|9##1_yo=O59dzx?S1thQe1#H literal 0 HcmV?d00001 diff --git a/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png b/applications/plugins/spi_mem_manager/images/Wiring_SPI_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c3ce363f801cff5080d10599ca258d07783fa1 GIT binary patch literal 4446 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 != SPIMemChipVendorUnknown && vendor->vendor_enum != vendor_enum) + vendor++; + return vendor->vendor_name; +} + +bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips) { + const SPIMemChip* chip_info_arr; + found_chips_reset(found_chips); + for(chip_info_arr = SPIMemChips; chip_info_arr->model_name != NULL; chip_info_arr++) { + if(chip_info->vendor_id != chip_info_arr->vendor_id) continue; + if(chip_info->type_id != chip_info_arr->type_id) continue; + if(chip_info->capacity_id != chip_info_arr->capacity_id) continue; + found_chips_push_back(found_chips, chip_info_arr); + } + if(found_chips_size(found_chips)) return true; + return false; +} + +void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src) { + memcpy(dest, src, sizeof(SPIMemChip)); +} + +size_t spi_mem_chip_get_size(SPIMemChip* chip) { + return (chip->size); +} + +const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip) { + return (spi_mem_chip_search_vendor_name(chip->vendor_enum)); +} + +const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum) { + return (spi_mem_chip_search_vendor_name(vendor_enum)); +} + +const char* spi_mem_chip_get_model_name(const SPIMemChip* chip) { + return (chip->model_name); +} + +uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip) { + return (chip->vendor_id); +} + +uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip) { + return (chip->type_id); +} + +uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip) { + return (chip->capacity_id); +} + +SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip) { + return (chip->write_mode); +} + +size_t spi_mem_chip_get_page_size(SPIMemChip* chip) { + return (chip->page_size); +} + +uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip) { + return ((uint32_t)chip->vendor_enum); +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h new file mode 100644 index 000000000..683937df4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +typedef struct SPIMemChip SPIMemChip; + +ARRAY_DEF(found_chips, const SPIMemChip*, M_POD_OPLIST) + +typedef enum { + SPIMemChipStatusBusy, + SPIMemChipStatusIdle, + SPIMemChipStatusError +} SPIMemChipStatus; + +typedef enum { + SPIMemChipWriteModeUnknown = 0, + SPIMemChipWriteModePage = (0x01 << 0), + SPIMemChipWriteModeAAIByte = (0x01 << 1), + SPIMemChipWriteModeAAIWord = (0x01 << 2), +} SPIMemChipWriteMode; + +const char* spi_mem_chip_get_vendor_name(const SPIMemChip* chip); +const char* spi_mem_chip_get_model_name(const SPIMemChip* chip); +size_t spi_mem_chip_get_size(SPIMemChip* chip); +uint8_t spi_mem_chip_get_vendor_id(SPIMemChip* chip); +uint8_t spi_mem_chip_get_type_id(SPIMemChip* chip); +uint8_t spi_mem_chip_get_capacity_id(SPIMemChip* chip); +SPIMemChipWriteMode spi_mem_chip_get_write_mode(SPIMemChip* chip); +size_t spi_mem_chip_get_page_size(SPIMemChip* chip); +bool spi_mem_chip_find_all(SPIMemChip* chip_info, found_chips_t found_chips); +void spi_mem_chip_copy_chip_info(SPIMemChip* dest, const SPIMemChip* src); +uint32_t spi_mem_chip_get_vendor_enum(const SPIMemChip* chip); +const char* spi_mem_chip_get_vendor_name_by_enum(uint32_t vendor_enum); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c new file mode 100644 index 000000000..25b237d68 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_arr.c @@ -0,0 +1,1399 @@ +#include "spi_mem_chip_i.h" +const SPIMemChip SPIMemChips[] = { + {0x1F, 0x40, 0x00, "AT25DN256", 32768, 256, SPIMemChipVendorADESTO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x20, "A25L05PT", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "A25L05PU", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x21, "A25L10PT", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x11, "A25L10PU", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x22, "A25L20PT", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x12, "A25L20PU", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x23, "A25L40PT", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x13, "A25L40PU", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x24, "A25L80PT", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x14, "A25L80PU", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x25, "A25L16PT", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "A25L16PU", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "A25L512", 65536, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "A25L010", 131072, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "A25L020", 262144, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "A25L040", 524288, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "A25L080", 1048576, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "A25L016", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "A25L032", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x15, "A25LQ16", 2097152, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x37, 0x40, 0x16, "A25LQ32A", 4194304, 256, SPIMemChipVendorAMIC, SPIMemChipWriteModePage}, + {0x68, 0x40, 0x14, "BY25D80", 1048576, 256, SPIMemChipVendorBoya, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25B05T", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25B10T", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25B20T", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25B40T", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25B80T", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25B16T", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25B32T", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25B64T", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25F05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25F10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25F20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25F40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x14, "EN25F80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x15, "EN25F16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x16, "EN25F32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x10, "EN25LF05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x11, "EN25LF10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x12, "EN25LF20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x13, "EN25LF40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "EN25P05", 65536, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x11, "EN25P10", 131072, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x12, "EN25P20", 262144, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x13, "EN25P40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x14, "EN25P80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x15, "EN25P16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x16, "EN25P32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x17, "EN25P64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x13, "EN25Q40", 524288, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x14, "EN25Q80A", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x15, "EN25Q16A", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25Q32A", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x16, "EN25Q32B", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x17, "EN25Q64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x30, 0x18, "EN25Q128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x15, "EN25QH16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x16, "EN25QH32", 4194304, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x17, "EN25QH64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x18, "EN25QH128", 16777216, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x70, 0x19, "EN25QH256", 33554432, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x14, "EN25T80", 1048576, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x51, 0x15, "EN25T16", 2097152, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x1C, 0x31, 0x17, "EN25F64", 8388608, 256, SPIMemChipVendorEON, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7C, "Pm25LV010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x21, "Pm25LD010", 131072, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x22, "Pm25LV020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7D, "Pm25W020", 262144, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x7F, 0x9D, 0x7E, "Pm25LV040", 524288, 256, SPIMemChipVendorPFLASH, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ZP25L16P", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "TS25L16PE", 2097152, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "TS25L80PE", 1048576, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "TS25L032A", 4194304, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "TS25L40P", 524288, 256, SPIMemChipVendorTERRA, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x10, + "GPR25L005E", + 65536, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "GPR25L161B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x12, + "GPR25L020B", + 262144, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "GPR25L3203F", + 4194304, + 256, + SPIMemChipVendorGeneralplus, + SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "AC25LV512", 65536, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "AC25LV010", 131072, 256, SPIMemChipVendorDEUTRON, SPIMemChipWriteModePage}, + {0x9D, 0x7B, 0x00, "EM25LV512", 65536, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x9D, 0x7C, 0x00, "EM25LV010", 131072, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L004A", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L008A", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L016A", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x8C, 0x8C, "F25L04UA", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x13, "F25L04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x30, 0x13, "F25S04P", 524288, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x14, "F25L08P", 1048576, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x15, "F25L16P", 2097152, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x20, 0x16, "F25L32P", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x8C, 0x40, 0x16, "F25L32Q", 4194304, 256, SPIMemChipVendorEFST, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x11, "ES25P10", 131072, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x12, "ES25P20", 262144, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x13, "ES25P40", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x14, "ES25P80", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x15, "ES25P16", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x20, 0x16, "ES25P32", 4194304, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x13, "ES25M40A", 524288, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x14, "ES25M80A", 1048576, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0x4A, 0x32, 0x15, "ES25M16A", 2097152, 256, SPIMemChipVendorEXCELSEMI, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x14, "FM25Q08A", 1048576, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16A", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x15, "FM25Q16B", 2097152, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x16, "FM25Q32A", 4194304, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xF8, 0x32, 0x17, "FM25Q64A", 8388608, 256, SPIMemChipVendorFIDELIX, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x13, "GD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x30, 0x14, "GD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x13, "GD25F40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x20, 0x14, "GD25F80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x10, "GD25Q512", 65536, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x11, "GD25Q10", 131072, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x12, "GD25Q20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x12, + "GD25LQ20C_1.8V", + 262144, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x13, "GD25Q40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x14, "GD25Q80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80B", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x14, + "GD25Q80C", + 1048576, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x15, "GD25Q16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x15, + "GD25Q16B", + 2097152, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x16, "GD25Q32", 4194304, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x16, + "GD25Q32B", + 4194304, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x40, 0x17, "GD25Q64", 8388608, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25Q64B", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x17, + "GD25B64C", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128B", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x40, + 0x18, + "GD25Q128C", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x17, + "GD25LQ064C_1.8V", + 8388608, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x18, + "GD25LQ128C_1.8V", + 16777216, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, + 0x60, + 0x19, + "GD25LQ256C_1.8V", + 33554432, + 256, + SPIMemChipVendorGIGADEVICE, + SPIMemChipWriteModePage}, + {0xC8, 0x31, 0x14, "MD25T80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x12, "MD25D20", 262144, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x13, "MD25D40", 524288, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x14, "MD25D80", 1048576, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x51, 0x40, 0x15, "MD25D16", 2097152, 256, SPIMemChipVendorGIGADEVICE, SPIMemChipWriteModePage}, + {0x1C, 0x20, 0x10, "ICE25P05", 65536, 128, SPIMemChipVendorICE, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QB25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QB25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x13, "QB25F640S33B", 8388608, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F016S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x11, "QH25F160S33B", 2097152, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0x89, 0x89, 0x12, "QH25F320S33B", 4194304, 256, SPIMemChipVendorINTEL, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "KH25L1005A", 131072, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "KH25L2005", 262144, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "KH25L4005A", 524288, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "KH25L512A", 65536, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "KH25L8005", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x26, 0x15, "KH25L8036D", 1048576, 256, SPIMemChipVendorKHIC, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005A", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1005C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x11, "MX25L1021E", 131072, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1025C", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25L1026E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12805D", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12835F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12836E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12839F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845G", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12845F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865E", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12865F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12873F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x18, + "MX25L12875F", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25635E", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x15, "MX25L1605", 2097152, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605A", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1605D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x15, + "MX25L1606E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1633E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1635D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1635E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1636D", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x15, + "MX25L1636E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1673E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x24, + 0x15, + "MX25L1675E", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2005C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026C", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25L2026E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x16, "MX25L3205", 4194304, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205A", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3205D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3206E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3208E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3225D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3233F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3235D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3235E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3236D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x5E, + 0x16, + "MX25L3237D", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25L3239E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3273F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x16, + "MX25L3275E", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005A", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4005C", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25L4026E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512A", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25L512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x22, 0x10, "MX25L5121E", 65536, 32, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x17, "MX25L6405", 8388608, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6405D", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6406E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6408E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6433F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6435E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6436F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25L6439E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6445E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6465E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6473F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x17, + "MX25L6475E", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25L8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8008E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8035E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8036E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8073E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25L8075E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x19, + "MX25L25673G", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x10, "MX25R512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x11, "MX25R1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x15, + "MX25R1635F", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x12, "MX25R2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x16, + "MX25R3235F", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x28, 0x13, "MX25R4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x17, + "MX25R6435F", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x28, + 0x14, + "MX25R8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x31, + "MX25U1001E_1.8V", + 131072, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x18, + "MX25U12835F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25673G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x39, + "MX25U25645G_1.8V", + 33554432, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635E_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x35, + "MX25U1635F_1.8V", + 2097152, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2032E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x32, + "MX25U2033E_1.8V", + 262144, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235E_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x36, + "MX25U3235F_1.8V", + 4194304, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4032E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4033E_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x33, + "MX25U4035_1.8V", + 524288, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x30, + "MX25U5121E_1.8V", + 65536, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6435F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x37, + "MX25U6473F_1.8V", + 8388608, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8032E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8033E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x34, + "MX25U8035E_1.8V", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x38, + "MX25U12873F_1.8V", + 16777216, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x11, "MX25V1006E", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x11, "MX25V1035F", 131072, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x12, "MX25V2006E", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x12, "MX25V2035F", 262144, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512C", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x10, "MX25V512E", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x10, "MX25V512F", 65536, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4005", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x13, "MX25V4006E", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x53, "MX25V4035", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x23, 0x13, "MX25V4035F", 524288, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, 0x20, 0x14, "MX25V8005", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x20, + 0x14, + "MX25V8006E", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, 0x25, 0x54, "MX25V8035", 1048576, 256, SPIMemChipVendorMACRONIX, SPIMemChipWriteModePage}, + {0xC2, + 0x23, + 0x14, + "MX25V8035F", + 1048576, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3A, + "MX66U51235F_1.8V", + 67108864, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0xC2, + 0x25, + 0x3B, + "MX66U1G45G_1.8V", + 134217728, + 256, + SPIMemChipVendorMACRONIX, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x16, "N25Q032A", 4194304, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x17, "N25Q064A", 8388608, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "N25Q256A13", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "N25Q512A83", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x2C, 0xCB, 0x19, "N25W256A11", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x18, + "MT25QL128AB", + 16777216, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x19, "MT25QL256A", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, 0xBA, 0x20, "MT25QL512A", 67108864, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x22, + "MT25QL02GC", + 268435456, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x20, 0xBB, 0x19, "MT25QU256", 33554432, 256, SPIMemChipVendorMICRON, SPIMemChipWriteModePage}, + {0x20, + 0xBA, + 0x21, + "N25Q00AA13G", + 134217728, + 256, + SPIMemChipVendorMICRON, + SPIMemChipWriteModePage}, + {0x37, 0x30, 0x10, "MS25X512", 65536, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "MS25X10", 131072, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "MS25X20", 262144, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x13, "MS25X40", 524288, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x14, "MS25X80", 1048576, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x15, "MS25X16", 2097152, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x16, "MS25X32", 4194304, 256, SPIMemChipVendorMSHINE, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x11, "N25S10", 131072, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x12, "N25S20", 262144, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x13, "N25S40", 524288, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x15, "N25S16", 2097152, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x16, "N25S32", 4194304, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0xD5, 0x30, 0x14, "N25S80", 1048576, 256, SPIMemChipVendorNANTRONICS, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7C, "NX25P10", 131072, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "NX25P16", 2097152, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7D, "NX25P20", 262144, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "NX25P32", 4194304, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x7E, "NX25P40", 524288, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x9D, 0x7F, 0x13, "NX25P80", 1048576, 256, SPIMemChipVendorNEXFLASH, SPIMemChipWriteModePage}, + {0x20, 0x40, 0x15, "M45PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05", 65536, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "M25P05A", 65536, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10", 131072, 128, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "M25P10A", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "M25P20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "M25P40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "M25P80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "M25P16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "M25P32", 4194304, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "M25P64", 8388608, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, + 0x20, + 0x18, + "M25P128_ST25P28V6G", + 16777216, + 256, + SPIMemChipVendorNUMONYX, + SPIMemChipWriteModePage}, + {0x20, 0x80, 0x11, "M25PE10", 131072, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x15, "M25PE16", 2097152, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x12, "M25PE20", 262144, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x13, "M25PE40", 524288, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0x20, 0x80, 0x14, "M25PE80", 1048576, 256, SPIMemChipVendorNUMONYX, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25LF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x49, 0x00, "PCT25VF010A", 131072, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "PCT25VF016B", 2097152, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x43, 0x00, "PCT25VF020A", 262144, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x4A, "PCT25VF032B", 4194304, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x44, 0x00, "PCT25VF040A", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "PCT25VF040B", 524288, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8E, "PCT25VF080B", 1048576, 256, SPIMemChipVendorPCT, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x10, "S25FL001D", 131072, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x11, "S25FL002D", 262144, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL004D", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "S25FL004K", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008A", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x13, "S25FL008D", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "S25FL008K", 1048576, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x14, "S25FL016A", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "S25FL016K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032A", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "S25FL032K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x15, "S25FL032P", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x12, "S25FL040A", 524288, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x26, + "S25FL040A_BOT", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x25, + "S25FL040A_TOP", + 524288, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064A", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "S25FL064K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x02, 0x16, "S25FL064P", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x15, "S25FL116K", 2097152, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0xEF, + 0x40, + 0x18, + "S25FL128K", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128P", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, + 0x20, + 0x18, + "S25FL128S", + 16777216, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0x01, 0x40, 0x16, "S25FL132K", 4194304, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, 0x40, 0x17, "S25FL164K", 8388608, 256, SPIMemChipVendorSPANSION, SPIMemChipWriteModePage}, + {0x01, + 0x02, + 0x19, + "S25FL256S", + 33554432, + 256, + SPIMemChipVendorSPANSION, + SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x41, "SST25VF016B", 2097152, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8C, "SST25VF020B", 262144, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4A, "SST25VF032B", 4194304, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x4B, "SST25VF064C", 8388608, 256, SPIMemChipVendorSST, SPIMemChipWriteModePage}, + {0xBF, 0x25, 0x8D, "SST25VF040B", 524288, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0xBF, 0x25, 0x8E, "SST25VF080B", 1048576, 1, SPIMemChipVendorSST, SPIMemChipWriteModeAAIWord}, + {0x20, 0x71, 0x15, "M25PX16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x16, "M25PX32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x17, "M25PX64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x71, 0x14, "M25PX80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05", 65536, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x10, "ST25P05A", 65536, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10", 131072, 128, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x11, "ST25P10A", 131072, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "ST25P16", 2097152, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x12, "ST25P20", 262144, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x16, "ST25P32", 4194304, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x13, "ST25P40", 524288, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x17, "ST25P64", 8388608, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x14, "ST25P80", 1048576, 256, SPIMemChipVendorST, SPIMemChipWriteModePage}, + {0xEF, 0x10, 0x00, "W25P10", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x15, "W25P16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x11, 0x00, "W25P20", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x16, "W25P32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x12, 0x00, "W25P40", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x17, "W25P64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x20, 0x14, "W25P80", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x11, + "W25Q10EW_1.8V", + 131072, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128BV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x18, "W25Q128FV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x18, "W25Q128JV", 16777216, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256FV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x70, 0x19, "W25Q256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x18, + "W25Q128FW_1.8V", + 16777216, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16CV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16DV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x15, + "W25Q16FW_1.8V", + 2097152, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x15, "W25Q16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x12, "W25Q20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x12, + "W25Q20EW_1.8V", + 262144, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32FV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x16, + "W25Q32FW_1.8V", + 4194304, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x16, "W25Q32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x13, "W25Q40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x13, + "W25Q40EW_1.8V", + 524288, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64CV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64FV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x17, "W25Q64JV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x17, + "W25Q64FW_1.8V", + 8388608, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x50, + 0x14, + "W25Q80BW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x14, "W25Q80DV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, + 0x60, + 0x14, + "W25Q80EW_1.8V", + 1048576, + 256, + SPIMemChipVendorWINBOND, + SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x10, "W25X05CL", 65536, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10AV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10BV", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10CL", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10L", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x11, "W25X10V", 131072, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AL", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16AV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16BV", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x15, "W25X16V", 2097152, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20AV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20BV", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20CL", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20L", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x12, "W25X20V", 262144, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32AV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32BV", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x16, "W25X32V", 4194304, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40AV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40BV", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40CL", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40L", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x13, "W25X40V", 524288, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64BV", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x17, "W25X64V", 8388608, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AL", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80AV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80BV", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80L", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x30, 0x14, "W25X80V", 1048576, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x71, 0x19, "W25M512JV", 67108864, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0xEF, 0x40, 0x19, "W25R256JV", 33554432, 256, SPIMemChipVendorWINBOND, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x10, "TS25L512A", 65536, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x11, "TS25L010A", 131072, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x30, 0x12, "TS25L020A", 262144, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16AP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x20, 0x20, 0x15, "TS25L16BP", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x37, 0x20, 0x15, "TS25L16P", 2097152, 256, SPIMemChipVendorZEMPRO, SPIMemChipWriteModePage}, + {0x5E, 0x40, 0x15, "ZB25D16", 2097152, 256, SPIMemChipVendorZbit, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "BG25Q40A", 524288, 256, SPIMemChipVendorBerg_Micro, SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x14, + "BG25Q80A", + 1048576, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x15, + "BG25Q16A", + 2097152, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0xE0, + 0x40, + 0x16, + "BG25Q32A", + 4194304, + 256, + SPIMemChipVendorBerg_Micro, + SPIMemChipWriteModePage}, + {0x1F, 0x23, 0x00, "AT45DB021D", 270336, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x24, 0x00, "AT45DB041D", 540672, 264, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x26, 0x00, "AT45DB161D", 2162688, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x27, 0x01, "AT45DB321D", 4325376, 528, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x43, 0x00, "AT25DF021", 262144, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x44, 0x00, "AT25DF041A", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x84, 0x00, "AT25SF041", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT25DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT25DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT25DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x48, 0x00, "AT25DF641", 8388608, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x65, 0x00, "AT25F512B", 65536, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x45, 0x00, "AT26DF081A", 1048576, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x46, 0x00, "AT26DF161A", 2097152, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x47, 0x00, "AT26DF321A", 4194304, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0x1F, 0x04, 0x00, "AT26F004", 524288, 256, SPIMemChipVendorATMEL, SPIMemChipWriteModePage}, + {0xE0, + 0x60, + 0x18, + "ACE25A128G_1.8V", + 16777216, + 256, + SPIMemChipVendorACE, + SPIMemChipWriteModePage}, + {0x9B, 0x32, 0x16, "ATO25Q32", 4194304, 256, SPIMemChipVendorATO, SPIMemChipWriteModePage}, + {0x54, 0x40, 0x17, "DQ25Q64A", 8388608, 256, SPIMemChipVendorDOUQI, SPIMemChipWriteModePage}, + {0x0E, 0x40, 0x15, "FT25H16", 2097152, 256, SPIMemChipVendorFremont, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x13, "FM25Q04A", 524288, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xA1, 0x40, 0x16, "FM25Q32", 4194304, 256, SPIMemChipVendorFudan, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x14, "GT25Q80A", 1048576, 256, SPIMemChipVendorGenitop, SPIMemChipWriteModePage}, + {0xE0, 0x40, 0x13, "PN25F04A", 524288, 256, SPIMemChipVendorParagon, SPIMemChipWriteModePage}}; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h new file mode 100644 index 000000000..30d607094 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_chip_i.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include "spi_mem_chip.h" + +typedef enum { + SPIMemChipVendorUnknown, + SPIMemChipVendorADESTO, + SPIMemChipVendorAMIC, + SPIMemChipVendorBoya, + SPIMemChipVendorEON, + SPIMemChipVendorPFLASH, + SPIMemChipVendorTERRA, + SPIMemChipVendorGeneralplus, + SPIMemChipVendorDEUTRON, + SPIMemChipVendorEFST, + SPIMemChipVendorEXCELSEMI, + SPIMemChipVendorFIDELIX, + SPIMemChipVendorGIGADEVICE, + SPIMemChipVendorICE, + SPIMemChipVendorINTEL, + SPIMemChipVendorKHIC, + SPIMemChipVendorMACRONIX, + SPIMemChipVendorMICRON, + SPIMemChipVendorMSHINE, + SPIMemChipVendorNANTRONICS, + SPIMemChipVendorNEXFLASH, + SPIMemChipVendorNUMONYX, + SPIMemChipVendorPCT, + SPIMemChipVendorSPANSION, + SPIMemChipVendorSST, + SPIMemChipVendorST, + SPIMemChipVendorWINBOND, + SPIMemChipVendorZEMPRO, + SPIMemChipVendorZbit, + SPIMemChipVendorBerg_Micro, + SPIMemChipVendorATMEL, + SPIMemChipVendorACE, + SPIMemChipVendorATO, + SPIMemChipVendorDOUQI, + SPIMemChipVendorFremont, + SPIMemChipVendorFudan, + SPIMemChipVendorGenitop, + SPIMemChipVendorParagon +} SPIMemChipVendor; + +typedef enum { + SPIMemChipCMDReadJEDECChipID = 0x9F, + SPIMemChipCMDReadData = 0x03, + SPIMemChipCMDChipErase = 0xC7, + SPIMemChipCMDWriteEnable = 0x06, + SPIMemChipCMDWriteDisable = 0x04, + SPIMemChipCMDReadStatus = 0x05, + SPIMemChipCMDWriteData = 0x02, + SPIMemChipCMDReleasePowerDown = 0xAB +} SPIMemChipCMD; + +enum SPIMemChipStatusBit { + SPIMemChipStatusBitBusy = (0x01 << 0), + SPIMemChipStatusBitWriteEnabled = (0x01 << 1), + SPIMemChipStatusBitBitProtection1 = (0x01 << 2), + SPIMemChipStatusBitBitProtection2 = (0x01 << 3), + SPIMemChipStatusBitBitProtection3 = (0x01 << 4), + SPIMemChipStatusBitTopBottomProtection = (0x01 << 5), + SPIMemChipStatusBitSectorProtect = (0x01 << 6), + SPIMemChipStatusBitRegisterProtect = (0x01 << 7) +}; + +typedef struct { + const char* vendor_name; + SPIMemChipVendor vendor_enum; +} SPIMemChipVendorName; + +struct SPIMemChip { + uint8_t vendor_id; + uint8_t type_id; + uint8_t capacity_id; + const char* model_name; + size_t size; + size_t page_size; + SPIMemChipVendor vendor_enum; + SPIMemChipWriteMode write_mode; +}; + +extern const SPIMemChip SPIMemChips[]; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c new file mode 100644 index 000000000..3518ca25c --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c @@ -0,0 +1,152 @@ +#include +#include +#include "spi_mem_chip_i.h" +#include "spi_mem_tools.h" + +static uint8_t spi_mem_tools_addr_to_byte_arr(uint32_t addr, uint8_t* cmd) { + uint8_t len = 3; // TODO(add support of 4 bytes address mode) + for(uint8_t i = 0; i < len; i++) { + cmd[i] = (addr >> ((len - (i + 1)) * 8)) & 0xFF; + } + return len; +} + +static bool spi_mem_tools_trx( + SPIMemChipCMD cmd, + uint8_t* tx_buf, + size_t tx_size, + uint8_t* rx_buf, + size_t rx_size) { + bool success = false; + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + do { + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, (uint8_t*)&cmd, 1, SPI_MEM_SPI_TIMEOUT)) + break; + if(tx_buf) { + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, tx_buf, tx_size, SPI_MEM_SPI_TIMEOUT)) + break; + } + if(rx_buf) { + if(!furi_hal_spi_bus_rx( + &furi_hal_spi_bus_handle_external, rx_buf, rx_size, SPI_MEM_SPI_TIMEOUT)) + break; + } + success = true; + } while(0); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + return success; +} + +static bool spi_mem_tools_write_buffer(uint8_t* data, size_t size, size_t offset) { + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_external); + uint8_t cmd = (uint8_t)SPIMemChipCMDWriteData; + uint8_t address[4]; + uint8_t address_size = spi_mem_tools_addr_to_byte_arr(offset, address); + bool success = false; + do { + if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, &cmd, 1, SPI_MEM_SPI_TIMEOUT)) + break; + if(!furi_hal_spi_bus_tx( + &furi_hal_spi_bus_handle_external, address, address_size, SPI_MEM_SPI_TIMEOUT)) + break; + if(!furi_hal_spi_bus_tx(&furi_hal_spi_bus_handle_external, data, size, SPI_MEM_SPI_TIMEOUT)) + break; + success = true; + } while(0); + furi_hal_spi_release(&furi_hal_spi_bus_handle_external); + return success; +} + +bool spi_mem_tools_read_chip_info(SPIMemChip* chip) { + uint8_t rx_buf[3] = {0, 0, 0}; + do { + if(!spi_mem_tools_trx(SPIMemChipCMDReadJEDECChipID, NULL, 0, rx_buf, 3)) break; + if(rx_buf[0] == 0 || rx_buf[0] == 255) break; + chip->vendor_id = rx_buf[0]; + chip->type_id = rx_buf[1]; + chip->capacity_id = rx_buf[2]; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_check_chip_info(SPIMemChip* chip) { + SPIMemChip new_chip_info; + spi_mem_tools_read_chip_info(&new_chip_info); + do { + if(chip->vendor_id != new_chip_info.vendor_id) break; + if(chip->type_id != new_chip_info.type_id) break; + if(chip->capacity_id != new_chip_info.capacity_id) break; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { + if(!spi_mem_tools_check_chip_info(chip)) return false; + for(size_t i = 0; i < block_size; i += SPI_MEM_MAX_BLOCK_SIZE) { + uint8_t cmd[4]; + if((offset + SPI_MEM_MAX_BLOCK_SIZE) > chip->size) return false; + if(!spi_mem_tools_trx( + SPIMemChipCMDReadData, + cmd, + spi_mem_tools_addr_to_byte_arr(offset, cmd), + data, + SPI_MEM_MAX_BLOCK_SIZE)) + return false; + offset += SPI_MEM_MAX_BLOCK_SIZE; + data += SPI_MEM_MAX_BLOCK_SIZE; + } + return true; +} + +size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip) { + UNUSED(chip); + return (SPI_MEM_FILE_BUFFER_SIZE); +} + +SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip) { + UNUSED(chip); + uint8_t status; + if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) + return SPIMemChipStatusError; + if(status & SPIMemChipStatusBitBusy) return SPIMemChipStatusBusy; + return SPIMemChipStatusIdle; +} + +static bool spi_mem_tools_set_write_enabled(SPIMemChip* chip, bool enable) { + UNUSED(chip); + uint8_t status; + SPIMemChipCMD cmd = SPIMemChipCMDWriteDisable; + if(enable) cmd = SPIMemChipCMDWriteEnable; + do { + if(!spi_mem_tools_trx(cmd, NULL, 0, NULL, 0)) break; + if(!spi_mem_tools_trx(SPIMemChipCMDReadStatus, NULL, 0, &status, 1)) break; + if(!(status & SPIMemChipStatusBitWriteEnabled) && enable) break; + if((status & SPIMemChipStatusBitWriteEnabled) && !enable) break; + return true; + } while(0); + return false; +} + +bool spi_mem_tools_erase_chip(SPIMemChip* chip) { + do { + if(!spi_mem_tools_set_write_enabled(chip, true)) break; + if(!spi_mem_tools_trx(SPIMemChipCMDChipErase, NULL, 0, NULL, 0)) break; + return true; + } while(0); + return true; +} + +bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size) { + do { + if(!spi_mem_tools_check_chip_info(chip)) break; + if(!spi_mem_tools_set_write_enabled(chip, true)) break; + if((offset + block_size) > chip->size) break; + if(!spi_mem_tools_write_buffer(data, block_size, offset)) break; + return true; + } while(0); + return false; +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h new file mode 100644 index 000000000..ad006b8ff --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.h @@ -0,0 +1,14 @@ +#pragma once + +#include "spi_mem_chip.h" + +#define SPI_MEM_SPI_TIMEOUT 1000 +#define SPI_MEM_MAX_BLOCK_SIZE 256 +#define SPI_MEM_FILE_BUFFER_SIZE 4096 + +bool spi_mem_tools_read_chip_info(SPIMemChip* chip); +bool spi_mem_tools_read_block(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); +size_t spi_mem_tools_get_file_max_block_size(SPIMemChip* chip); +SPIMemChipStatus spi_mem_tools_get_chip_status(SPIMemChip* chip); +bool spi_mem_tools_erase_chip(SPIMemChip* chip); +bool spi_mem_tools_write_bytes(SPIMemChip* chip, size_t offset, uint8_t* data, size_t block_size); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c new file mode 100644 index 000000000..438f338f1 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.c @@ -0,0 +1,129 @@ +#include "spi_mem_worker_i.h" + +typedef enum { + SPIMemEventStopThread = (1 << 0), + SPIMemEventChipDetect = (1 << 1), + SPIMemEventRead = (1 << 2), + SPIMemEventVerify = (1 << 3), + SPIMemEventErase = (1 << 4), + SPIMemEventWrite = (1 << 5), + SPIMemEventAll = + (SPIMemEventStopThread | SPIMemEventChipDetect | SPIMemEventRead | SPIMemEventVerify | + SPIMemEventErase | SPIMemEventWrite) +} SPIMemEventEventType; + +static int32_t spi_mem_worker_thread(void* thread_context); + +SPIMemWorker* spi_mem_worker_alloc() { + SPIMemWorker* worker = malloc(sizeof(SPIMemWorker)); + worker->callback = NULL; + worker->thread = furi_thread_alloc(); + worker->mode_index = SPIMemWorkerModeIdle; + furi_thread_set_name(worker->thread, "SPIMemWorker"); + furi_thread_set_callback(worker->thread, spi_mem_worker_thread); + furi_thread_set_context(worker->thread, worker); + furi_thread_set_stack_size(worker->thread, 10240); + return worker; +} + +void spi_mem_worker_free(SPIMemWorker* worker) { + furi_thread_free(worker->thread); + free(worker); +} + +bool spi_mem_worker_check_for_stop(SPIMemWorker* worker) { + UNUSED(worker); + uint32_t flags = furi_thread_flags_get(); + return (flags & SPIMemEventStopThread); +} + +static int32_t spi_mem_worker_thread(void* thread_context) { + SPIMemWorker* worker = thread_context; + while(true) { + uint32_t flags = furi_thread_flags_wait(SPIMemEventAll, FuriFlagWaitAny, FuriWaitForever); + if(flags != (unsigned)FuriFlagErrorTimeout) { + if(flags & SPIMemEventStopThread) break; + if(flags & SPIMemEventChipDetect) worker->mode_index = SPIMemWorkerModeChipDetect; + if(flags & SPIMemEventRead) worker->mode_index = SPIMemWorkerModeRead; + if(flags & SPIMemEventVerify) worker->mode_index = SPIMemWorkerModeVerify; + if(flags & SPIMemEventErase) worker->mode_index = SPIMemWorkerModeErase; + if(flags & SPIMemEventWrite) worker->mode_index = SPIMemWorkerModeWrite; + if(spi_mem_worker_modes[worker->mode_index].process) { + spi_mem_worker_modes[worker->mode_index].process(worker); + } + worker->mode_index = SPIMemWorkerModeIdle; + } + } + return 0; +} + +void spi_mem_worker_start_thread(SPIMemWorker* worker) { + furi_thread_start(worker->thread); +} + +void spi_mem_worker_stop_thread(SPIMemWorker* worker) { + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventStopThread); + furi_thread_join(worker->thread); +} + +void spi_mem_worker_chip_detect_start( + SPIMemChip* chip_info, + found_chips_t* found_chips, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + worker->found_chips = found_chips; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventChipDetect); +} + +void spi_mem_worker_read_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventRead); +} + +void spi_mem_worker_verify_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventVerify); +} + +void spi_mem_worker_erase_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventErase); +} + +void spi_mem_worker_write_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context) { + furi_check(worker->mode_index == SPIMemWorkerModeIdle); + worker->callback = callback; + worker->cb_ctx = context; + worker->chip_info = chip_info; + furi_thread_flags_set(furi_thread_get_id(worker->thread), SPIMemEventWrite); +} diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h new file mode 100644 index 000000000..c3761cd5a --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "spi_mem_chip.h" + +typedef struct SPIMemWorker SPIMemWorker; + +typedef struct { + void (*const process)(SPIMemWorker* worker); +} SPIMemWorkerModeType; + +typedef enum { + SPIMemCustomEventWorkerChipIdentified, + SPIMemCustomEventWorkerChipUnknown, + SPIMemCustomEventWorkerBlockReaded, + SPIMemCustomEventWorkerChipFail, + SPIMemCustomEventWorkerFileFail, + SPIMemCustomEventWorkerDone, + SPIMemCustomEventWorkerVerifyFail, +} SPIMemCustomEventWorker; + +typedef void (*SPIMemWorkerCallback)(void* context, SPIMemCustomEventWorker event); + +SPIMemWorker* spi_mem_worker_alloc(); +void spi_mem_worker_free(SPIMemWorker* worker); +void spi_mem_worker_start_thread(SPIMemWorker* worker); +void spi_mem_worker_stop_thread(SPIMemWorker* worker); +bool spi_mem_worker_check_for_stop(SPIMemWorker* worker); +void spi_mem_worker_chip_detect_start( + SPIMemChip* chip_info, + found_chips_t* found_chips, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_read_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_verify_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_erase_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); +void spi_mem_worker_write_start( + SPIMemChip* chip_info, + SPIMemWorker* worker, + SPIMemWorkerCallback callback, + void* context); diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h new file mode 100644 index 000000000..43e2d2287 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_i.h @@ -0,0 +1,24 @@ +#pragma once + +#include "spi_mem_worker.h" + +typedef enum { + SPIMemWorkerModeIdle, + SPIMemWorkerModeChipDetect, + SPIMemWorkerModeRead, + SPIMemWorkerModeVerify, + SPIMemWorkerModeErase, + SPIMemWorkerModeWrite +} SPIMemWorkerMode; + +struct SPIMemWorker { + SPIMemChip* chip_info; + found_chips_t* found_chips; + SPIMemWorkerMode mode_index; + SPIMemWorkerCallback callback; + void* cb_ctx; + FuriThread* thread; + FuriString* file_name; +}; + +extern const SPIMemWorkerModeType spi_mem_worker_modes[]; diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c new file mode 100644 index 000000000..a393e5490 --- /dev/null +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_worker_modes.c @@ -0,0 +1,214 @@ +#include "spi_mem_worker_i.h" +#include "spi_mem_chip.h" +#include "spi_mem_tools.h" +#include "../../spi_mem_files.h" + +static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker); +static void spi_mem_worker_read_process(SPIMemWorker* worker); +static void spi_mem_worker_verify_process(SPIMemWorker* worker); +static void spi_mem_worker_erase_process(SPIMemWorker* worker); +static void spi_mem_worker_write_process(SPIMemWorker* worker); + +const SPIMemWorkerModeType spi_mem_worker_modes[] = { + [SPIMemWorkerModeIdle] = {.process = NULL}, + [SPIMemWorkerModeChipDetect] = {.process = spi_mem_worker_chip_detect_process}, + [SPIMemWorkerModeRead] = {.process = spi_mem_worker_read_process}, + [SPIMemWorkerModeVerify] = {.process = spi_mem_worker_verify_process}, + [SPIMemWorkerModeErase] = {.process = spi_mem_worker_erase_process}, + [SPIMemWorkerModeWrite] = {.process = spi_mem_worker_write_process}}; + +static void spi_mem_worker_run_callback(SPIMemWorker* worker, SPIMemCustomEventWorker event) { + if(worker->callback) { + worker->callback(worker->cb_ctx, event); + } +} + +static bool spi_mem_worker_await_chip_busy(SPIMemWorker* worker) { + while(true) { + furi_delay_tick(10); // to give some time to OS + if(spi_mem_worker_check_for_stop(worker)) return true; + SPIMemChipStatus chip_status = spi_mem_tools_get_chip_status(worker->chip_info); + if(chip_status == SPIMemChipStatusError) return false; + if(chip_status == SPIMemChipStatusBusy) continue; + return true; + } +} + +static size_t spi_mem_worker_modes_get_total_size(SPIMemWorker* worker) { + size_t chip_size = spi_mem_chip_get_size(worker->chip_info); + size_t file_size = spi_mem_file_get_size(worker->cb_ctx); + size_t total_size = chip_size; + if(chip_size > file_size) total_size = file_size; + return total_size; +} + +// ChipDetect +static void spi_mem_worker_chip_detect_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event; + while(!spi_mem_tools_read_chip_info(worker->chip_info)) { + furi_delay_tick(10); // to give some time to OS + if(spi_mem_worker_check_for_stop(worker)) return; + } + if(spi_mem_chip_find_all(worker->chip_info, *worker->found_chips)) { + event = SPIMemCustomEventWorkerChipIdentified; + } else { + event = SPIMemCustomEventWorkerChipUnknown; + } + spi_mem_worker_run_callback(worker, event); +} + +// Read +static bool spi_mem_worker_read(SPIMemWorker* worker, SPIMemCustomEventWorker* event) { + uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; + size_t chip_size = spi_mem_chip_get_size(worker->chip_info); + size_t offset = 0; + bool success = true; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= chip_size) break; + if((offset + block_size) > chip_size) block_size = chip_size - offset; + if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer, block_size)) { + *event = SPIMemCustomEventWorkerChipFail; + success = false; + break; + } + if(!spi_mem_file_write_block(worker->cb_ctx, data_buffer, block_size)) { + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + if(success) *event = SPIMemCustomEventWorkerDone; + return success; +} + +static void spi_mem_worker_read_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_file_create_open(worker->cb_ctx)) break; + if(!spi_mem_worker_read(worker, &event)) break; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} + +// Verify +static bool + spi_mem_worker_verify(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { + uint8_t data_buffer_chip[SPI_MEM_FILE_BUFFER_SIZE]; + uint8_t data_buffer_file[SPI_MEM_FILE_BUFFER_SIZE]; + size_t offset = 0; + bool success = true; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= total_size) break; + if((offset + block_size) > total_size) block_size = total_size - offset; + if(!spi_mem_tools_read_block(worker->chip_info, offset, data_buffer_chip, block_size)) { + *event = SPIMemCustomEventWorkerChipFail; + success = false; + break; + } + if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer_file, block_size)) { + success = false; + break; + } + if(memcmp(data_buffer_chip, data_buffer_file, block_size) != 0) { + *event = SPIMemCustomEventWorkerVerifyFail; + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + if(success) *event = SPIMemCustomEventWorkerDone; + return success; +} + +static void spi_mem_worker_verify_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerFileFail; + size_t total_size = spi_mem_worker_modes_get_total_size(worker); + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_file_open(worker->cb_ctx)) break; + if(!spi_mem_worker_verify(worker, total_size, &event)) break; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} + +// Erase +static void spi_mem_worker_erase_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; + do { + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_tools_erase_chip(worker->chip_info)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + event = SPIMemCustomEventWorkerDone; + } while(0); + spi_mem_worker_run_callback(worker, event); +} + +// Write +static bool spi_mem_worker_write_block_by_page( + SPIMemWorker* worker, + size_t offset, + uint8_t* data, + size_t block_size, + size_t page_size) { + for(size_t i = 0; i < block_size; i += page_size) { + if(!spi_mem_worker_await_chip_busy(worker)) return false; + if(!spi_mem_tools_write_bytes(worker->chip_info, offset, data, page_size)) return false; + offset += page_size; + data += page_size; + } + return true; +} + +static bool + spi_mem_worker_write(SPIMemWorker* worker, size_t total_size, SPIMemCustomEventWorker* event) { + bool success = true; + uint8_t data_buffer[SPI_MEM_FILE_BUFFER_SIZE]; + size_t page_size = spi_mem_chip_get_page_size(worker->chip_info); + size_t offset = 0; + while(true) { + furi_delay_tick(10); // to give some time to OS + size_t block_size = SPI_MEM_FILE_BUFFER_SIZE; + if(spi_mem_worker_check_for_stop(worker)) break; + if(offset >= total_size) break; + if((offset + block_size) > total_size) block_size = total_size - offset; + if(!spi_mem_file_read_block(worker->cb_ctx, data_buffer, block_size)) { + *event = SPIMemCustomEventWorkerFileFail; + success = false; + break; + } + if(!spi_mem_worker_write_block_by_page( + worker, offset, data_buffer, block_size, page_size)) { + success = false; + break; + } + offset += block_size; + spi_mem_worker_run_callback(worker, SPIMemCustomEventWorkerBlockReaded); + } + return success; +} + +static void spi_mem_worker_write_process(SPIMemWorker* worker) { + SPIMemCustomEventWorker event = SPIMemCustomEventWorkerChipFail; + size_t total_size = + spi_mem_worker_modes_get_total_size(worker); // need to be executed before opening file + do { + if(!spi_mem_file_open(worker->cb_ctx)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + if(!spi_mem_worker_write(worker, total_size, &event)) break; + if(!spi_mem_worker_await_chip_busy(worker)) break; + event = SPIMemCustomEventWorkerDone; + } while(0); + spi_mem_file_close(worker->cb_ctx); + spi_mem_worker_run_callback(worker, event); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c new file mode 100644 index 000000000..7780005f4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.c @@ -0,0 +1,30 @@ +#include "spi_mem_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const spi_mem_on_enter_handlers[])(void*) = { +#include "spi_mem_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_event handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, +bool (*const spi_mem_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "spi_mem_scene_config.h" +}; +#undef ADD_SCENE + +// Generate scene on_exit handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, +void (*const spi_mem_on_exit_handlers[])(void* context) = { +#include "spi_mem_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers spi_mem_scene_handlers = { + .on_enter_handlers = spi_mem_on_enter_handlers, + .on_event_handlers = spi_mem_on_event_handlers, + .on_exit_handlers = spi_mem_on_exit_handlers, + .scene_num = SPIMemSceneNum, +}; diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h new file mode 100644 index 000000000..2ac6d21e3 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) SPIMemScene##id, +typedef enum { +#include "spi_mem_scene_config.h" + SPIMemSceneNum, +} SPIMemScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers spi_mem_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "spi_mem_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_event handlers declaration +#define ADD_SCENE(prefix, name, id) \ + bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); +#include "spi_mem_scene_config.h" +#undef ADD_SCENE + +// Generate scene on_exit handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); +#include "spi_mem_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c new file mode 100644 index 000000000..dc0cc4fe4 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -0,0 +1,42 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +#define SPI_MEM_VERSION_APP "0.1.0" +#define SPI_MEM_DEVELOPER "DrunkBatya" +#define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" +#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" +#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" + +void spi_mem_scene_about_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* tmp_string = furi_string_alloc(); + + widget_add_text_box_element( + app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false); + widget_add_text_box_element( + app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false); + furi_string_printf(tmp_string, "\e#%s\n", "Information"); + furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP); + furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER); + furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB); + furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); + furi_string_cat_printf( + tmp_string, + "SPI memory dumper\n" + "Originally written by Hedger, ghettorce and x893 at\n" + "Flipper Hackathon 2021\n\n"); + widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(tmp_string)); + + furi_string_free(tmp_string); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void spi_mem_scene_about_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c new file mode 100644 index 000000000..d9b8f0aa3 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect.c @@ -0,0 +1,37 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_chip_detect_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_chip_detect_on_enter(void* context) { + SPIMemApp* app = context; + notification_message(app->notifications, &sequence_blink_start_yellow); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewDetect); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_chip_detect_start( + app->chip_info, &app->found_chips, app->worker, spi_mem_scene_chip_detect_callback, app); +} + +bool spi_mem_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventWorkerChipIdentified) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, 0); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectVendor); + } else if(event.event == SPIMemCustomEventWorkerChipUnknown) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetectFail); + } + } + return success; +} + +void spi_mem_scene_chip_detect_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + notification_message(app->notifications, &sequence_blink_stop); + popup_reset(app->popup); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c new file mode 100644 index 000000000..876a28721 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detect_fail.c @@ -0,0 +1,57 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +static void spi_mem_scene_chip_detect_fail_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_chip_detect_fail_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* str = furi_string_alloc(); + widget_add_button_element( + app->widget, + GuiButtonTypeCenter, + "Retry", + spi_mem_scene_chip_detect_fail_widget_callback, + app); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected"); + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignBottom, FontPrimary, "unknown SPI chip"); + furi_string_printf(str, "Vendor\nid: 0x%02X", spi_mem_chip_get_vendor_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 16, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_printf(str, "Type\nid: 0x%02X", spi_mem_chip_get_type_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 64, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_printf(str, "Capacity\nid: 0x%02X", spi_mem_chip_get_capacity_id(app->chip_info)); + widget_add_string_multiline_element( + app->widget, 110, 44, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_free(str); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_chip_detect_fail_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeCenter) { + scene_manager_previous_scene(app->scene_manager); + } + } + return success; +} +void spi_mem_scene_chip_detect_fail_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c new file mode 100644 index 000000000..539578a45 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_detected.c @@ -0,0 +1,94 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_chip_detected_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static void spi_mem_scene_chip_detected_print_chip_info(Widget* widget, SPIMemChip* chip_info) { + FuriString* tmp_string = furi_string_alloc(); + widget_add_string_element( + widget, + 40, + 12, + AlignLeft, + AlignTop, + FontSecondary, + spi_mem_chip_get_vendor_name(chip_info)); + widget_add_string_element( + widget, 40, 20, AlignLeft, AlignTop, FontSecondary, spi_mem_chip_get_model_name(chip_info)); + furi_string_printf(tmp_string, "Size: %zu KB", spi_mem_chip_get_size(chip_info) / 1024); + widget_add_string_element( + widget, 40, 28, AlignLeft, AlignTop, FontSecondary, furi_string_get_cstr(tmp_string)); + furi_string_free(tmp_string); +} + +static void spi_mem_scene_chip_detect_draw_next_button(SPIMemApp* app) { + FuriString* str = furi_string_alloc(); + if(app->mode == SPIMemModeRead) furi_string_printf(str, "%s", "Read"); + if(app->mode == SPIMemModeWrite) furi_string_printf(str, "%s", "Write"); + if(app->mode == SPIMemModeErase) furi_string_printf(str, "%s", "Erase"); + if(app->mode == SPIMemModeCompare) furi_string_printf(str, "%s", "Check"); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + furi_string_get_cstr(str), + spi_mem_scene_chip_detected_widget_callback, + app); + furi_string_free(str); +} + +static void spi_mem_scene_chip_detected_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) + scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +static void spi_mem_scene_chip_detected_set_next_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeRead) scene = SPIMemSceneReadFilename; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneErase; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneErase; + if(app->mode == SPIMemModeCompare) scene = SPIMemSceneVerify; + scene_manager_next_scene(app->scene_manager, scene); +} + +void spi_mem_scene_chip_detected_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Retry", spi_mem_scene_chip_detected_widget_callback, app); + spi_mem_scene_chip_detect_draw_next_button(app); + widget_add_icon_element(app->widget, 0, 12, &I_Dip8_32x36); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Detected SPI chip"); + spi_mem_scene_chip_detected_print_chip_info(app->widget, app->chip_info); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_chip_detected_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_chip_detected_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == GuiButtonTypeRight) { + spi_mem_scene_chip_detected_set_next_scene(app); + } + } + return success; +} +void spi_mem_scene_chip_detected_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c new file mode 100644 index 000000000..ca4b765a2 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_chip_error.c @@ -0,0 +1,52 @@ +#include "../spi_mem_app_i.h" + +static void + spi_mem_scene_chip_error_widget_callback(GuiButtonType result, InputType type, void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_chip_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_chip_error_widget_callback, app); + widget_add_string_element( + app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "SPI chip error"); + widget_add_string_multiline_element( + app->widget, + 85, + 52, + AlignCenter, + AlignBottom, + FontSecondary, + "Error while\ncommunicating\nwith chip"); + widget_add_icon_element(app->widget, 5, 6, &I_Dip8_32x36); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +static void spi_mem_scene_chip_error_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneChipDetect; + if(app->mode == SPIMemModeRead || app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_chip_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_chip_error_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + spi_mem_scene_chip_error_set_previous_scene(app); + } + } + return success; +} +void spi_mem_scene_chip_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h new file mode 100644 index 000000000..c0e377303 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_config.h @@ -0,0 +1,21 @@ +ADD_SCENE(spi_mem, start, Start) +ADD_SCENE(spi_mem, chip_detect, ChipDetect) +ADD_SCENE(spi_mem, chip_detected, ChipDetected) +ADD_SCENE(spi_mem, chip_detect_fail, ChipDetectFail) +ADD_SCENE(spi_mem, select_file, SelectFile) +ADD_SCENE(spi_mem, saved_file_menu, SavedFileMenu) +ADD_SCENE(spi_mem, read, Read) +ADD_SCENE(spi_mem, read_filename, ReadFilename) +ADD_SCENE(spi_mem, delete_confirm, DeleteConfirm) +ADD_SCENE(spi_mem, success, Success) +ADD_SCENE(spi_mem, about, About) +ADD_SCENE(spi_mem, verify, Verify) +ADD_SCENE(spi_mem, file_info, FileInfo) +ADD_SCENE(spi_mem, erase, Erase) +ADD_SCENE(spi_mem, chip_error, ChipError) +ADD_SCENE(spi_mem, verify_error, VerifyError) +ADD_SCENE(spi_mem, write, Write) +ADD_SCENE(spi_mem, storage_error, StorageError) +ADD_SCENE(spi_mem, select_vendor, SelectVendor) +ADD_SCENE(spi_mem, select_model, SelectModel) +ADD_SCENE(spi_mem, wiring, Wiring) diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c new file mode 100644 index 000000000..bb5142452 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c @@ -0,0 +1,62 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +static void spi_mem_scene_delete_confirm_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_delete_confirm_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* file_name = furi_string_alloc(); + FuriString* message = furi_string_alloc(); + path_extract_filename(app->file_path, file_name, true); + furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); + widget_add_text_box_element( + app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true); + widget_add_button_element( + app->widget, + GuiButtonTypeLeft, + "Cancel", + spi_mem_scene_delete_confirm_widget_callback, + app); + widget_add_button_element( + app->widget, + GuiButtonTypeRight, + "Delete", + spi_mem_scene_delete_confirm_widget_callback, + app); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); + furi_string_free(file_name); + furi_string_free(message); +} + +bool spi_mem_scene_delete_confirm_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeRight) { + app->mode = SPIMemModeDelete; + if(spi_mem_file_delete(app)) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } else if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneSavedFileMenu); + } + } + return success; +} + +void spi_mem_scene_delete_confirm_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c new file mode 100644 index 000000000..0d3ae66bf --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_erase.c @@ -0,0 +1,65 @@ +#include "../spi_mem_app_i.h" + +static void + spi_mem_scene_erase_widget_callback(GuiButtonType result, InputType type, void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +static void spi_mem_scene_erase_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_erase_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Cancel", spi_mem_scene_erase_widget_callback, app); + widget_add_string_element( + app->widget, 64, 15, AlignCenter, AlignBottom, FontPrimary, "Erasing SPI chip"); + widget_add_string_element( + app->widget, 64, 27, AlignCenter, AlignBottom, FontSecondary, "Please be patient"); + notification_message(app->notifications, &sequence_blink_start_magenta); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_erase_start(app->chip_info, app->worker, spi_mem_scene_erase_callback, app); +} + +static void spi_mem_scene_erase_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +static void spi_mem_scene_erase_set_next_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneSuccess; + if(app->mode == SPIMemModeWrite) scene = SPIMemSceneWrite; + scene_manager_next_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_erase_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_erase_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_previous_scene(app->scene_manager); + } else if(event.event == SPIMemCustomEventWorkerDone) { + spi_mem_scene_erase_set_next_scene(app); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } + } + return success; +} +void spi_mem_scene_erase_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + notification_message(app->notifications, &sequence_blink_stop); + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c new file mode 100644 index 000000000..687f17f81 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_file_info.c @@ -0,0 +1,29 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_file_info_on_enter(void* context) { + SPIMemApp* app = context; + FuriString* str = furi_string_alloc(); + furi_string_printf(str, "Size: %zu KB", spi_mem_file_get_size(app) / 1024); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "File info"); + widget_add_string_element( + app->widget, 64, 20, AlignCenter, AlignBottom, FontSecondary, furi_string_get_cstr(str)); + furi_string_free(str); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_file_info_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneSavedFileMenu); + } + return success; +} +void spi_mem_scene_file_info_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c new file mode 100644 index 000000000..bbf38a303 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read.c @@ -0,0 +1,57 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_read_progress_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); +} + +static void spi_mem_scene_read_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_read_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_read_callback( + app->view_progress, spi_mem_scene_read_progress_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_blue); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_read_start(app->chip_info, app->worker, spi_mem_scene_read_callback, app); +} + +bool spi_mem_scene_read_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewReadCancel) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } + return success; +} +void spi_mem_scene_read_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c new file mode 100644 index 000000000..4b16baa2e --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_read_filename.c @@ -0,0 +1,46 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_read_filename_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventTextEditResult); +} + +void spi_mem_scene_read_set_random_filename(SPIMemApp* app) { + if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { + size_t filename_start = furi_string_search_rchar(app->file_path, '/'); + furi_string_left(app->file_path, filename_start); + } + set_random_name(app->text_buffer, SPI_MEM_TEXT_BUFFER_SIZE); +} + +void spi_mem_scene_read_filename_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_scene_read_set_random_filename(app); + text_input_set_header_text(app->text_input, "Name the dump"); + text_input_set_result_callback( + app->text_input, + spi_mem_scene_read_filename_view_result_callback, + app, + app->text_buffer, + SPI_MEM_FILE_NAME_SIZE, + true); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewTextInput); +} + +bool spi_mem_scene_read_filename_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventTextEditResult) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneRead); + } + } + return success; +} +void spi_mem_scene_read_filename_on_exit(void* context) { + SPIMemApp* app = context; + text_input_reset(app->text_input); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c new file mode 100644 index 000000000..d5767455e --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_saved_file_menu.c @@ -0,0 +1,76 @@ +#include "../spi_mem_app_i.h" + +typedef enum { + SPIMemSceneSavedFileMenuSubmenuIndexWrite, + SPIMemSceneSavedFileMenuSubmenuIndexCompare, + SPIMemSceneSavedFileMenuSubmenuIndexInfo, + SPIMemSceneSavedFileMenuSubmenuIndexDelete, +} SPIMemSceneSavedFileMenuSubmenuIndex; + +static void spi_mem_scene_saved_file_menu_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_saved_file_menu_on_enter(void* context) { + SPIMemApp* app = context; + submenu_add_item( + app->submenu, + "Write", + SPIMemSceneSavedFileMenuSubmenuIndexWrite, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Compare", + SPIMemSceneSavedFileMenuSubmenuIndexCompare, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Info", + SPIMemSceneSavedFileMenuSubmenuIndexInfo, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Delete", + SPIMemSceneSavedFileMenuSubmenuIndexDelete, + spi_mem_scene_saved_file_menu_submenu_callback, + app); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_saved_file_menu_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, event.event); + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexWrite) { + app->mode = SPIMemModeWrite; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexCompare) { + app->mode = SPIMemModeCompare; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexDelete) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneDeleteConfirm); + success = true; + } + if(event.event == SPIMemSceneSavedFileMenuSubmenuIndexInfo) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneFileInfo); + success = true; + } + } + return success; +} + +void spi_mem_scene_saved_file_menu_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c new file mode 100644 index 000000000..cb48035b5 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_file.c @@ -0,0 +1,22 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" + +void spi_mem_scene_select_file_on_enter(void* context) { + SPIMemApp* app = context; + if(spi_mem_file_select(app)) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSavedFileMenu, 0); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSavedFileMenu); + } else { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, SPIMemSceneStart); + } +} + +bool spi_mem_scene_select_file_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void spi_mem_scene_select_file_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c new file mode 100644 index 000000000..c39c4a182 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_model.c @@ -0,0 +1,45 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_select_model_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + spi_mem_chip_copy_chip_info(app->chip_info, *found_chips_get(app->found_chips, index)); + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_select_model_on_enter(void* context) { + SPIMemApp* app = context; + size_t models_on_vendor = 0; + for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { + if(spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index)) != + app->chip_vendor_enum) + continue; + submenu_add_item( + app->submenu, + spi_mem_chip_get_model_name(*found_chips_get(app->found_chips, index)), + index, + spi_mem_scene_select_model_submenu_callback, + app); + models_on_vendor++; + } + if(models_on_vendor == 1) spi_mem_scene_select_model_submenu_callback(context, 0); + submenu_set_header(app->submenu, "Choose chip model"); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_select_model_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetected); + success = true; + } + return success; +} + +void spi_mem_scene_select_model_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c new file mode 100644 index 000000000..c7f736f88 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_select_vendor.c @@ -0,0 +1,70 @@ +#include "../spi_mem_app_i.h" +#include +#include + +ARRAY_DEF(vendors, uint32_t) +ALGO_DEF(vendors, ARRAY_OPLIST(vendors)) + +static void spi_mem_scene_select_vendor_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + app->chip_vendor_enum = index; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +static void spi_mem_scene_select_vendor_sort_vendors(SPIMemApp* app, vendors_t vendors_arr) { + for(size_t index = 0; index < found_chips_size(app->found_chips); index++) { + vendors_push_back( + vendors_arr, spi_mem_chip_get_vendor_enum(*found_chips_get(app->found_chips, index))); + } + vendors_uniq(vendors_arr); +} + +void spi_mem_scene_select_vendor_on_enter(void* context) { + SPIMemApp* app = context; + vendors_t vendors_arr; + vendors_init(vendors_arr); + spi_mem_scene_select_vendor_sort_vendors(app, vendors_arr); + size_t vendors_arr_size = vendors_size(vendors_arr); + if(vendors_arr_size == 1) + spi_mem_scene_select_vendor_submenu_callback(context, *vendors_get(vendors_arr, 0)); + for(size_t index = 0; index < vendors_arr_size; index++) { + uint32_t vendor_enum = *vendors_get(vendors_arr, index); + submenu_add_item( + app->submenu, + spi_mem_chip_get_vendor_name_by_enum(vendor_enum), + vendor_enum, + spi_mem_scene_select_vendor_submenu_callback, + app); + } + vendors_clear(vendors_arr); + submenu_set_header(app->submenu, "Choose chip vendor"); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneSelectVendor)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +static void spi_mem_scene_select_vendor_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneStart; + if(app->mode == SPIMemModeCompare || app->mode == SPIMemModeWrite) + scene = SPIMemSceneSavedFileMenu; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_select_vendor_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_select_vendor_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneSelectVendor, event.event); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectModel); + success = true; + } + return success; +} + +void spi_mem_scene_select_vendor_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c new file mode 100644 index 000000000..b664df687 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_start.c @@ -0,0 +1,84 @@ +#include "../spi_mem_app_i.h" + +typedef enum { + SPIMemSceneStartSubmenuIndexRead, + SPIMemSceneStartSubmenuIndexSaved, + SPIMemSceneStartSubmenuIndexErase, + SPIMemSceneStartSubmenuIndexWiring, + SPIMemSceneStartSubmenuIndexAbout +} SPIMemSceneStartSubmenuIndex; + +static void spi_mem_scene_start_submenu_callback(void* context, uint32_t index) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void spi_mem_scene_start_on_enter(void* context) { + SPIMemApp* app = context; + submenu_add_item( + app->submenu, + "Read", + SPIMemSceneStartSubmenuIndexRead, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Saved", + SPIMemSceneStartSubmenuIndexSaved, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Erase", + SPIMemSceneStartSubmenuIndexErase, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "Wiring", + SPIMemSceneStartSubmenuIndexWiring, + spi_mem_scene_start_submenu_callback, + app); + submenu_add_item( + app->submenu, + "About", + SPIMemSceneStartSubmenuIndexAbout, + spi_mem_scene_start_submenu_callback, + app); + submenu_set_selected_item( + app->submenu, scene_manager_get_scene_state(app->scene_manager, SPIMemSceneStart)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewSubmenu); +} + +bool spi_mem_scene_start_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(app->scene_manager, SPIMemSceneStart, event.event); + if(event.event == SPIMemSceneStartSubmenuIndexRead) { + app->mode = SPIMemModeRead; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexSaved) { + furi_string_set(app->file_path, SPI_MEM_FILE_FOLDER); + scene_manager_next_scene(app->scene_manager, SPIMemSceneSelectFile); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexErase) { + app->mode = SPIMemModeErase; + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipDetect); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexWiring) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneWiring); + success = true; + } else if(event.event == SPIMemSceneStartSubmenuIndexAbout) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneAbout); + success = true; + } + } + return success; +} + +void spi_mem_scene_start_on_exit(void* context) { + SPIMemApp* app = context; + submenu_reset(app->submenu); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c new file mode 100644 index 000000000..d5e289e24 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_storage_error.c @@ -0,0 +1,56 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_storage_error_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_storage_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_storage_error_widget_callback, app); + widget_add_string_element( + app->widget, 85, 15, AlignCenter, AlignBottom, FontPrimary, "Storage error"); + widget_add_string_multiline_element( + app->widget, + 85, + 52, + AlignCenter, + AlignBottom, + FontSecondary, + "Error while\nworking with\nfilesystem"); + widget_add_icon_element(app->widget, 5, 6, &I_SDQuestion_35x43); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +static void spi_mem_scene_storage_error_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneChipDetect; + if(app->mode == SPIMemModeRead) scene = SPIMemSceneStart; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + if(app->mode == SPIMemModeDelete) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_storage_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + spi_mem_scene_storage_error_set_previous_scene(app); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + spi_mem_scene_storage_error_set_previous_scene(app); + } + } + return success; +} +void spi_mem_scene_storage_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c new file mode 100644 index 000000000..39039466f --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_success.c @@ -0,0 +1,40 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_success_popup_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventPopupBack); +} + +void spi_mem_scene_success_on_enter(void* context) { + SPIMemApp* app = context; + popup_set_icon(app->popup, 32, 5, &I_DolphinNice_96x59); + popup_set_header(app->popup, "Success!", 5, 7, AlignLeft, AlignTop); + popup_set_callback(app->popup, spi_mem_scene_success_popup_callback); + popup_set_context(app->popup, app); + popup_set_timeout(app->popup, 1500); + popup_enable_timeout(app->popup); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewPopup); +} + +static void spi_mem_scene_success_set_previous_scene(SPIMemApp* app) { + uint32_t scene = SPIMemSceneSelectFile; + if(app->mode == SPIMemModeErase) scene = SPIMemSceneStart; + scene_manager_search_and_switch_to_another_scene(app->scene_manager, scene); +} + +bool spi_mem_scene_success_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventPopupBack) { + spi_mem_scene_success_set_previous_scene(app); + } + } + return success; +} + +void spi_mem_scene_success_on_exit(void* context) { + SPIMemApp* app = context; + popup_reset(app->popup); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c new file mode 100644 index 000000000..08a8d1057 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify.c @@ -0,0 +1,59 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_verify_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewVerifySkip); +} + +static void spi_mem_scene_verify_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_verify_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_verify_callback( + app->view_progress, spi_mem_scene_verify_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_cyan); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_verify_start(app->chip_info, app->worker, spi_mem_scene_verify_callback, app); +} + +bool spi_mem_scene_verify_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewVerifySkip) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneSuccess); + } else if(event.event == SPIMemCustomEventWorkerVerifyFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerifyError); + } + } + return success; +} +void spi_mem_scene_verify_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c new file mode 100644 index 000000000..fbe954fa6 --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_verify_error.c @@ -0,0 +1,43 @@ +#include "../spi_mem_app_i.h" + +static void spi_mem_scene_verify_error_widget_callback( + GuiButtonType result, + InputType type, + void* context) { + SPIMemApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void spi_mem_scene_verify_error_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_button_element( + app->widget, GuiButtonTypeLeft, "Back", spi_mem_scene_verify_error_widget_callback, app); + widget_add_string_element( + app->widget, 64, 9, AlignCenter, AlignBottom, FontPrimary, "Verification error"); + widget_add_string_element( + app->widget, 64, 21, AlignCenter, AlignBottom, FontSecondary, "Data mismatch"); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_verify_error_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == GuiButtonTypeLeft) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } + } + return success; +} +void spi_mem_scene_verify_error_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c new file mode 100644 index 000000000..22036f4bc --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_wiring.c @@ -0,0 +1,18 @@ +#include "../spi_mem_app_i.h" +#include "../lib/spi/spi_mem_chip.h" + +void spi_mem_scene_wiring_on_enter(void* context) { + SPIMemApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_Wiring_SPI_128x64); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewWidget); +} + +bool spi_mem_scene_wiring_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void spi_mem_scene_wiring_on_exit(void* context) { + SPIMemApp* app = context; + widget_reset(app->widget); +} diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c new file mode 100644 index 000000000..dfa384fbb --- /dev/null +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_write.c @@ -0,0 +1,58 @@ +#include "../spi_mem_app_i.h" +#include "../spi_mem_files.h" +#include "../lib/spi/spi_mem_chip.h" +#include "../lib/spi/spi_mem_tools.h" + +void spi_mem_scene_write_progress_view_result_callback(void* context) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, SPIMemCustomEventViewReadCancel); +} + +static void spi_mem_scene_write_callback(void* context, SPIMemCustomEventWorker event) { + SPIMemApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +void spi_mem_scene_write_on_enter(void* context) { + SPIMemApp* app = context; + spi_mem_view_progress_set_write_callback( + app->view_progress, spi_mem_scene_write_progress_view_result_callback, app); + notification_message(app->notifications, &sequence_blink_start_cyan); + spi_mem_view_progress_set_chip_size(app->view_progress, spi_mem_chip_get_size(app->chip_info)); + spi_mem_view_progress_set_file_size(app->view_progress, spi_mem_file_get_size(app)); + spi_mem_view_progress_set_block_size( + app->view_progress, spi_mem_tools_get_file_max_block_size(app->chip_info)); + view_dispatcher_switch_to_view(app->view_dispatcher, SPIMemViewProgress); + spi_mem_worker_start_thread(app->worker); + spi_mem_worker_write_start(app->chip_info, app->worker, spi_mem_scene_write_callback, app); +} + +bool spi_mem_scene_write_on_event(void* context, SceneManagerEvent event) { + SPIMemApp* app = context; + UNUSED(app); + bool success = false; + if(event.type == SceneManagerEventTypeBack) { + success = true; + } else if(event.type == SceneManagerEventTypeCustom) { + success = true; + if(event.event == SPIMemCustomEventViewReadCancel) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, SPIMemSceneChipDetect); + } else if(event.event == SPIMemCustomEventWorkerBlockReaded) { + spi_mem_view_progress_inc_progress(app->view_progress); + } else if(event.event == SPIMemCustomEventWorkerDone) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneVerify); + } else if(event.event == SPIMemCustomEventWorkerChipFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneChipError); + } else if(event.event == SPIMemCustomEventWorkerFileFail) { + scene_manager_next_scene(app->scene_manager, SPIMemSceneStorageError); + } + } + return success; +} +void spi_mem_scene_write_on_exit(void* context) { + SPIMemApp* app = context; + spi_mem_worker_stop_thread(app->worker); + spi_mem_view_progress_reset(app->view_progress); + notification_message(app->notifications, &sequence_blink_stop); +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.c b/applications/plugins/spi_mem_manager/spi_mem_app.c new file mode 100644 index 000000000..63531b74c --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app.c @@ -0,0 +1,112 @@ +#include +#include "spi_mem_app_i.h" +#include "spi_mem_files.h" +#include "lib/spi/spi_mem_chip_i.h" + +static bool spi_mem_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + SPIMemApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool spi_mem_back_event_callback(void* context) { + furi_assert(context); + SPIMemApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +SPIMemApp* spi_mem_alloc(void) { + SPIMemApp* instance = malloc(sizeof(SPIMemApp)); + + instance->file_path = furi_string_alloc(); + instance->gui = furi_record_open(RECORD_GUI); + instance->notifications = furi_record_open(RECORD_NOTIFICATION); + instance->view_dispatcher = view_dispatcher_alloc(); + instance->scene_manager = scene_manager_alloc(&spi_mem_scene_handlers, instance); + instance->submenu = submenu_alloc(); + instance->dialog_ex = dialog_ex_alloc(); + instance->popup = popup_alloc(); + instance->worker = spi_mem_worker_alloc(); + instance->dialogs = furi_record_open(RECORD_DIALOGS); + instance->storage = furi_record_open(RECORD_STORAGE); + instance->widget = widget_alloc(); + instance->chip_info = malloc(sizeof(SPIMemChip)); + found_chips_init(instance->found_chips); + instance->view_progress = spi_mem_view_progress_alloc(); + instance->view_detect = spi_mem_view_detect_alloc(); + instance->text_input = text_input_alloc(); + instance->mode = SPIMemModeUnknown; + + furi_string_set(instance->file_path, SPI_MEM_FILE_FOLDER); + + view_dispatcher_enable_queue(instance->view_dispatcher); + view_dispatcher_set_event_callback_context(instance->view_dispatcher, instance); + view_dispatcher_set_custom_event_callback( + instance->view_dispatcher, spi_mem_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + instance->view_dispatcher, spi_mem_back_event_callback); + view_dispatcher_attach_to_gui( + instance->view_dispatcher, instance->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewSubmenu, submenu_get_view(instance->submenu)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewDialogEx, dialog_ex_get_view(instance->dialog_ex)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewPopup, popup_get_view(instance->popup)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewWidget, widget_get_view(instance->widget)); + view_dispatcher_add_view( + instance->view_dispatcher, + SPIMemViewProgress, + spi_mem_view_progress_get_view(instance->view_progress)); + view_dispatcher_add_view( + instance->view_dispatcher, + SPIMemViewDetect, + spi_mem_view_detect_get_view(instance->view_detect)); + view_dispatcher_add_view( + instance->view_dispatcher, SPIMemViewTextInput, text_input_get_view(instance->text_input)); + + furi_hal_power_enable_otg(); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_external); + scene_manager_next_scene(instance->scene_manager, SPIMemSceneStart); + return instance; +} + +void spi_mem_free(SPIMemApp* instance) { + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewSubmenu); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDialogEx); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewPopup); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewWidget); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewProgress); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewDetect); + view_dispatcher_remove_view(instance->view_dispatcher, SPIMemViewTextInput); + spi_mem_view_progress_free(instance->view_progress); + spi_mem_view_detect_free(instance->view_detect); + submenu_free(instance->submenu); + dialog_ex_free(instance->dialog_ex); + popup_free(instance->popup); + widget_free(instance->widget); + text_input_free(instance->text_input); + view_dispatcher_free(instance->view_dispatcher); + scene_manager_free(instance->scene_manager); + spi_mem_worker_free(instance->worker); + free(instance->chip_info); + found_chips_clear(instance->found_chips); + furi_record_close(RECORD_STORAGE); + furi_record_close(RECORD_DIALOGS); + furi_record_close(RECORD_NOTIFICATION); + furi_record_close(RECORD_GUI); + furi_string_free(instance->file_path); + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_external); + furi_hal_power_disable_otg(); + free(instance); +} + +int32_t spi_mem_app(void* p) { + UNUSED(p); + SPIMemApp* instance = spi_mem_alloc(); + spi_mem_file_create_folder(instance); + view_dispatcher_run(instance->view_dispatcher); + spi_mem_free(instance); + return 0; +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_app.h b/applications/plugins/spi_mem_manager/spi_mem_app.h new file mode 100644 index 000000000..37ac927db --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct SPIMemApp SPIMemApp; diff --git a/applications/plugins/spi_mem_manager/spi_mem_app_i.h b/applications/plugins/spi_mem_manager/spi_mem_app_i.h new file mode 100644 index 000000000..4ce056175 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_app_i.h @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include "spi_mem_app.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scenes/spi_mem_scene.h" +#include "lib/spi/spi_mem_worker.h" +#include "spi_mem_manager_icons.h" +#include "views/spi_mem_view_progress.h" +#include "views/spi_mem_view_detect.h" + +#define TAG "SPIMem" +#define SPI_MEM_FILE_EXTENSION ".bin" +#define SPI_MEM_FILE_FOLDER EXT_PATH("spimem") +#define SPI_MEM_FILE_NAME_SIZE 100 +#define SPI_MEM_TEXT_BUFFER_SIZE 128 + +typedef enum { + SPIMemModeRead, + SPIMemModeWrite, + SPIMemModeCompare, + SPIMemModeErase, + SPIMemModeDelete, + SPIMemModeUnknown +} SPIMemMode; + +struct SPIMemApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + Submenu* submenu; + DialogEx* dialog_ex; + Popup* popup; + NotificationApp* notifications; + FuriString* file_path; + DialogsApp* dialogs; + Storage* storage; + File* file; + Widget* widget; + SPIMemWorker* worker; + SPIMemChip* chip_info; + found_chips_t found_chips; + uint32_t chip_vendor_enum; + SPIMemProgressView* view_progress; + SPIMemDetectView* view_detect; + TextInput* text_input; + SPIMemMode mode; + char text_buffer[SPI_MEM_TEXT_BUFFER_SIZE + 1]; +}; + +typedef enum { + SPIMemViewSubmenu, + SPIMemViewDialogEx, + SPIMemViewPopup, + SPIMemViewWidget, + SPIMemViewTextInput, + SPIMemViewProgress, + SPIMemViewDetect +} SPIMemView; + +typedef enum { + SPIMemCustomEventViewReadCancel, + SPIMemCustomEventViewVerifySkip, + SPIMemCustomEventTextEditResult, + SPIMemCustomEventPopupBack +} SPIMemCustomEvent; diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.c b/applications/plugins/spi_mem_manager/spi_mem_files.c new file mode 100644 index 000000000..a7374da19 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_files.c @@ -0,0 +1,74 @@ +#include "spi_mem_app_i.h" + +void spi_mem_file_create_folder(SPIMemApp* app) { + if(!storage_simply_mkdir(app->storage, SPI_MEM_FILE_FOLDER)) { + dialog_message_show_storage_error(app->dialogs, "Cannot create\napp folder"); + } +} + +bool spi_mem_file_delete(SPIMemApp* app) { + return (storage_simply_remove(app->storage, furi_string_get_cstr(app->file_path))); +} + +bool spi_mem_file_select(SPIMemApp* app) { + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, SPI_MEM_FILE_EXTENSION, &I_Dip8_10px); + browser_options.base_path = SPI_MEM_FILE_FOLDER; + bool success = + dialog_file_browser_show(app->dialogs, app->file_path, app->file_path, &browser_options); + return success; +} + +bool spi_mem_file_create_open(SPIMemApp* app) { + bool success = false; + app->file = storage_file_alloc(app->storage); + do { + if(furi_string_end_with(app->file_path, SPI_MEM_FILE_EXTENSION)) { + if(!spi_mem_file_delete(app)) break; + size_t filename_start = furi_string_search_rchar(app->file_path, '/'); + furi_string_left(app->file_path, filename_start); + } + furi_string_cat_printf(app->file_path, "/%s%s", app->text_buffer, SPI_MEM_FILE_EXTENSION); + if(!storage_file_open( + app->file, furi_string_get_cstr(app->file_path), FSAM_WRITE, FSOM_CREATE_NEW)) + break; + success = true; + } while(0); + if(!success) { //-V547 + dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); + } + return success; +} + +bool spi_mem_file_open(SPIMemApp* app) { + app->file = storage_file_alloc(app->storage); + if(!storage_file_open( + app->file, furi_string_get_cstr(app->file_path), FSAM_READ_WRITE, FSOM_OPEN_EXISTING)) { + dialog_message_show_storage_error(app->dialogs, "Cannot save\nfile"); + return false; + } + return true; +} + +bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size) { + if(storage_file_write(app->file, data, size) != size) return false; + return true; +} + +bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size) { + if(storage_file_read(app->file, data, size) != size) return false; + return true; +} + +void spi_mem_file_close(SPIMemApp* app) { + storage_file_close(app->file); + storage_file_free(app->file); +} + +size_t spi_mem_file_get_size(SPIMemApp* app) { + FileInfo file_info; + if(storage_common_stat(app->storage, furi_string_get_cstr(app->file_path), &file_info) != + FSE_OK) + return 0; + return file_info.size; +} diff --git a/applications/plugins/spi_mem_manager/spi_mem_files.h b/applications/plugins/spi_mem_manager/spi_mem_files.h new file mode 100644 index 000000000..0e735d951 --- /dev/null +++ b/applications/plugins/spi_mem_manager/spi_mem_files.h @@ -0,0 +1,14 @@ +#pragma once +#include "spi_mem_app.h" + +void spi_mem_file_create_folder(SPIMemApp* app); +bool spi_mem_file_select(SPIMemApp* app); +bool spi_mem_file_create(SPIMemApp* app, const char* file_name); +bool spi_mem_file_delete(SPIMemApp* app); +bool spi_mem_file_create_open(SPIMemApp* app); +bool spi_mem_file_open(SPIMemApp* app); +bool spi_mem_file_write_block(SPIMemApp* app, uint8_t* data, size_t size); +bool spi_mem_file_read_block(SPIMemApp* app, uint8_t* data, size_t size); +void spi_mem_file_close(SPIMemApp* app); +void spi_mem_file_show_storage_error(SPIMemApp* app, const char* error_text); +size_t spi_mem_file_get_size(SPIMemApp* app); diff --git a/applications/plugins/spi_mem_manager/tools/README.md b/applications/plugins/spi_mem_manager/tools/README.md new file mode 100644 index 000000000..91080941f --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/README.md @@ -0,0 +1,7 @@ +This utility can convert nofeletru's UsbAsp-flash's chiplist.xml to C array + +Usage: +```bash + ./chiplist_convert.py chiplist/chiplist.xml + mv spi_mem_chip_arr.c ../lib/spi/spi_mem_chip_arr.c +``` diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE b/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE new file mode 100644 index 000000000..56364f150 --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 nofeletru + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml b/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml new file mode 100644 index 000000000..91a654743 --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist/chiplist.xml @@ -0,0 +1,984 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_25AA010A page="16" size="128" spicmd="95"/> + <_25AA020A page="16" size="256" spicmd="95"/> + <_25AA040 page="16" size="512" spicmd="95"/> + <_25AA040A page="16" size="512" spicmd="95"/> + <_25AA080 page="16" size="1024" spicmd="95"/> + <_25AA080A page="16" size="1024" spicmd="95"/> + <_25AA080B page="32" size="1024" spicmd="95"/> + <_25AA080C page="16" size="1024" spicmd="95"/> + <_25AA080D page="32" size="1024" spicmd="95"/> + <_25AA1024 page="256" size="131072" spicmd="95"/> + <_25AA128 page="64" size="16384" spicmd="95"/> + <_25AA160 page="16" size="2048" spicmd="95"/> + <_25AA160A page="16" size="2048" spicmd="95"/> + <_25AA160B page="32" size="2048" spicmd="95"/> + <_25AA256 page="64" size="32768" spicmd="95"/> + <_25AA320 page="32" size="4096" spicmd="95"/> + <_25AA512 page="128" size="65536" spicmd="95"/> + <_25AA640 page="32" size="8192" spicmd="95"/> + <_25C040 page="16" size="512" spicmd="95"/> + <_25C080 page="16" size="1024" spicmd="95"/> + <_25C160 page="16" size="2048" spicmd="95"/> + <_25C320 page="32" size="4096" spicmd="95"/> + <_25C640 page="32" size="8192" spicmd="95"/> + <_25LC010A page="16" size="128" spicmd="95"/> + <_25LC020A page="16" size="256" spicmd="95"/> + <_25LC040 page="16" size="512" spicmd="95"/> + <_25LC040A page="16" size="512" spicmd="95"/> + <_25LC080 page="16" size="1024" spicmd="95"/> + <_25LC080A page="16" size="1024" spicmd="95"/> + <_25LC080B page="32" size="1024" spicmd="95"/> + <_25LC080C page="16" size="1024" spicmd="95"/> + <_25LC080D page="32" size="1024" spicmd="95"/> + <_25LC1024 page="256" size="131072" spicmd="95"/> + <_25LC128 page="64" size="16384" spicmd="95"/> + <_25LC160 page="16" size="2048" spicmd="95"/> + <_25LC160A page="16" size="2048" spicmd="95"/> + <_25LC160B page="32" size="2048" spicmd="95"/> + <_25LC256 page="64" size="32768" spicmd="95"/> + <_25LC320 page="32" size="4096" spicmd="95"/> + <_25LC512 page="128" size="65536" spicmd="95"/> + <_25LC640 page="32" size="8192" spicmd="95"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <_24Cxxx> + + <_24C01 page="1" size="128" addrtype="1"/> + <_24C02 page="1" size="256" addrtype="1"/> + <_24C04 page="1" size="512" addrtype="2"/> + <_24C08 page="16" size="1024" addrtype="3"/> + <_24C16 page="16" size="2048" addrtype="4"/> + <_24C32 page="32" size="4096" addrtype="5"/> + <_24C64 page="32" size="8192" addrtype="5"/> + <_24C128 page="64" size="16384" addrtype="5"/> + <_24C256 page="64" size="32768" addrtype="5"/> + <_24C512 page="128" size="65536" addrtype="5"/> + <_24C1024 page="128" size="131072" addrtype="6"/> + + + + + + + + + + + + + diff --git a/applications/plugins/spi_mem_manager/tools/chiplist_convert.py b/applications/plugins/spi_mem_manager/tools/chiplist_convert.py new file mode 100755 index 000000000..8b623eb3e --- /dev/null +++ b/applications/plugins/spi_mem_manager/tools/chiplist_convert.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 + +import argparse +import xml.etree.ElementTree as XML +import sys + + +def getArgs(): + parser = argparse.ArgumentParser( + description="chiplist.xml to C array converter", + ) + parser.add_argument("file", help="chiplist.xml file") + return parser.parse_args() + + +def getXML(file): + tree = XML.parse(file) + root = tree.getroot() + return root + + +def parseChip(cur, arr, vendor, vendorCodeArr): + chip = {} + chipAttr = cur.attrib + if "page" not in chipAttr: # chip without page size not supported + return + if "id" not in chipAttr: # I2C not supported yet + return + if len(chipAttr["id"]) < 6: # ID wihout capacity id not supported yet + return + chip["modelName"] = cur.tag + chip["vendorEnum"] = "SPIMemChipVendor" + vendor + chip["vendorID"] = "0x" + chipAttr["id"][0] + chipAttr["id"][1] + chip["typeID"] = chipAttr["id"][2] + chipAttr["id"][3] + chip["capacityID"] = chipAttr["id"][4] + chipAttr["id"][5] + chip["size"] = chipAttr["size"] + if chipAttr["page"] == "SSTW": + chip["writeMode"] = "SPIMemChipWriteModeAAIWord" + chip["pageSize"] = "1" + elif chipAttr["page"] == "SSTB": + chip["writeMode"] = "SPIMemChipWriteModeAAIByte" + chip["pageSize"] = "1" + else: + chip["writeMode"] = "SPIMemChipWriteModePage" + chip["pageSize"] = chipAttr["page"] + arr.append(chip) + vendorCodeArr[vendor].add(chip["vendorID"]) + + +def cleanEmptyVendors(vendors): + for cur in list(vendors): + if not vendors[cur]: + vendors.pop(cur) + + +def getVendors(xml, interface): + arr = {} + for cur in xml.find(interface): + arr[cur.tag] = set() + return arr + + +def parseXML(xml, interface, vendorCodeArr): + arr = [] + for vendor in xml.find(interface): + for cur in vendor: + parseChip(cur, arr, vendor.tag, vendorCodeArr) + return arr + + +def getVendorNameEnum(vendorID): + try: + return vendors[vendorID] + except: + print("Unknown vendor: " + vendorID) + sys.exit(1) + + +def generateCArr(arr, filename): + with open(filename, "w") as out: + print('#include "spi_mem_chip_i.h"', file=out) + print("const SPIMemChip SPIMemChips[] = {", file=out) + for cur in arr: + print(" {" + cur["vendorID"] + ",", file=out, end="") + print(" 0x" + cur["typeID"] + ",", file=out, end="") + print(" 0x" + cur["capacityID"] + ",", file=out, end="") + print(' "' + cur["modelName"] + '",', file=out, end="") + print(" " + cur["size"] + ",", file=out, end="") + print(" " + cur["pageSize"] + ",", file=out, end="") + print(" " + cur["vendorEnum"] + ",", file=out, end="") + if cur == arr[-1]: + print(" " + cur["writeMode"] + "}};", file=out) + else: + print(" " + cur["writeMode"] + "},", file=out) + +def main(): + filename = "spi_mem_chip_arr.c" + args = getArgs() + xml = getXML(args.file) + vendors = getVendors(xml, "SPI") + chipArr = parseXML(xml, "SPI", vendors) + cleanEmptyVendors(vendors) + for cur in vendors: + print(' {"' + cur + '", SPIMemChipVendor' + cur + "},") + generateCArr(chipArr, filename) + + +if __name__ == "__main__": + main() diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c new file mode 100644 index 000000000..eddf36e49 --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.c @@ -0,0 +1,64 @@ +#include "spi_mem_view_detect.h" +#include "spi_mem_manager_icons.h" +#include + +struct SPIMemDetectView { + View* view; + IconAnimation* icon; + SPIMemDetectViewCallback callback; + void* cb_ctx; +}; + +typedef struct { + IconAnimation* icon; +} SPIMemDetectViewModel; + +View* spi_mem_view_detect_get_view(SPIMemDetectView* app) { + return app->view; +} + +static void spi_mem_view_detect_draw_callback(Canvas* canvas, void* context) { + SPIMemDetectViewModel* model = context; + canvas_set_font(canvas, FontPrimary); + canvas_draw_icon_animation(canvas, 0, 0, model->icon); + canvas_draw_str_aligned(canvas, 64, 26, AlignLeft, AlignCenter, "Detecting"); + canvas_draw_str_aligned(canvas, 64, 36, AlignLeft, AlignCenter, "SPI chip..."); +} + +static void spi_mem_view_detect_enter_callback(void* context) { + SPIMemDetectView* app = context; + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_start(model->icon); }, false); +} + +static void spi_mem_view_detect_exit_callback(void* context) { + SPIMemDetectView* app = context; + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_stop(model->icon); }, false); +} + +SPIMemDetectView* spi_mem_view_detect_alloc() { + SPIMemDetectView* app = malloc(sizeof(SPIMemDetectView)); + app->view = view_alloc(); + view_set_context(app->view, app); + view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemDetectViewModel)); + with_view_model( + app->view, + SPIMemDetectViewModel * model, + { + model->icon = icon_animation_alloc(&A_ChipLooking_64x64); + view_tie_icon_animation(app->view, model->icon); + }, + false); + view_set_draw_callback(app->view, spi_mem_view_detect_draw_callback); + view_set_enter_callback(app->view, spi_mem_view_detect_enter_callback); + view_set_exit_callback(app->view, spi_mem_view_detect_exit_callback); + return app; +} + +void spi_mem_view_detect_free(SPIMemDetectView* app) { + with_view_model( + app->view, SPIMemDetectViewModel * model, { icon_animation_free(model->icon); }, false); + view_free(app->view); + free(app); +} diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h new file mode 100644 index 000000000..f95edb60d --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_detect.h @@ -0,0 +1,9 @@ +#pragma once +#include + +typedef struct SPIMemDetectView SPIMemDetectView; +typedef void (*SPIMemDetectViewCallback)(void* context); + +View* spi_mem_view_detect_get_view(SPIMemDetectView* app); +SPIMemDetectView* spi_mem_view_detect_alloc(); +void spi_mem_view_detect_free(SPIMemDetectView* app); diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c new file mode 100644 index 000000000..790f97997 --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.c @@ -0,0 +1,230 @@ +#include "spi_mem_view_progress.h" +#include + +struct SPIMemProgressView { + View* view; + SPIMemProgressViewCallback callback; + void* cb_ctx; +}; + +typedef enum { + SPIMemProgressViewTypeRead, + SPIMemProgressViewTypeVerify, + SPIMemProgressViewTypeWrite, + SPIMemProgressViewTypeUnknown +} SPIMemProgressViewType; + +typedef struct { + size_t chip_size; + size_t file_size; + size_t blocks_written; + size_t block_size; + float progress; + SPIMemProgressViewType view_type; +} SPIMemProgressViewModel; + +View* spi_mem_view_progress_get_view(SPIMemProgressView* app) { + return app->view; +} + +static void spi_mem_view_progress_draw_progress(Canvas* canvas, float progress) { + FuriString* progress_str = furi_string_alloc(); + if(progress > 1.0) progress = 1.0; + furi_string_printf(progress_str, "%d %%", (int)(progress * 100)); + elements_progress_bar(canvas, 13, 35, 100, progress); + canvas_draw_str_aligned( + canvas, 64, 25, AlignCenter, AlignTop, furi_string_get_cstr(progress_str)); + furi_string_free(progress_str); +} + +static void + spi_mem_view_progress_read_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Reading dump"); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_left(canvas, "Cancel"); +} + +static void + spi_mem_view_progress_draw_size_warning(Canvas* canvas, SPIMemProgressViewModel* model) { + if(model->file_size > model->chip_size) { + canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to chip!"); + } + if(model->chip_size > model->file_size) { + canvas_draw_str_aligned(canvas, 64, 13, AlignCenter, AlignTop, "Size clamped to file!"); + } +} + +static void + spi_mem_view_progress_verify_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 2, AlignCenter, AlignTop, "Verifying dump"); + spi_mem_view_progress_draw_size_warning(canvas, model); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_center(canvas, "Skip"); +} + +static void + spi_mem_view_progress_write_draw_callback(Canvas* canvas, SPIMemProgressViewModel* model) { + canvas_draw_str_aligned(canvas, 64, 4, AlignCenter, AlignTop, "Writing dump"); + spi_mem_view_progress_draw_size_warning(canvas, model); + spi_mem_view_progress_draw_progress(canvas, model->progress); + elements_button_left(canvas, "Cancel"); +} + +static void spi_mem_view_progress_draw_callback(Canvas* canvas, void* context) { + SPIMemProgressViewModel* model = context; + SPIMemProgressViewType view_type = model->view_type; + if(view_type == SPIMemProgressViewTypeRead) { + spi_mem_view_progress_read_draw_callback(canvas, model); + } else if(view_type == SPIMemProgressViewTypeVerify) { + spi_mem_view_progress_verify_draw_callback(canvas, model); + } else if(view_type == SPIMemProgressViewTypeWrite) { + spi_mem_view_progress_write_draw_callback(canvas, model); + } +} + +static bool + spi_mem_view_progress_read_write_input_callback(InputEvent* event, SPIMemProgressView* app) { + bool success = false; + if(event->type == InputTypeShort && event->key == InputKeyLeft) { + if(app->callback) { + app->callback(app->cb_ctx); + } + success = true; + } + return success; +} + +static bool + spi_mem_view_progress_verify_input_callback(InputEvent* event, SPIMemProgressView* app) { + bool success = false; + if(event->type == InputTypeShort && event->key == InputKeyOk) { + if(app->callback) { + app->callback(app->cb_ctx); + } + success = true; + } + return success; +} + +static bool spi_mem_view_progress_input_callback(InputEvent* event, void* context) { + SPIMemProgressView* app = context; + bool success = false; + SPIMemProgressViewType view_type; + with_view_model( + app->view, SPIMemProgressViewModel * model, { view_type = model->view_type; }, true); + if(view_type == SPIMemProgressViewTypeRead) { + success = spi_mem_view_progress_read_write_input_callback(event, app); + } else if(view_type == SPIMemProgressViewTypeVerify) { + success = spi_mem_view_progress_verify_input_callback(event, app); + } else if(view_type == SPIMemProgressViewTypeWrite) { + success = spi_mem_view_progress_read_write_input_callback(event, app); + } + return success; +} + +SPIMemProgressView* spi_mem_view_progress_alloc() { + SPIMemProgressView* app = malloc(sizeof(SPIMemProgressView)); + app->view = view_alloc(); + view_allocate_model(app->view, ViewModelTypeLocking, sizeof(SPIMemProgressViewModel)); + view_set_context(app->view, app); + view_set_draw_callback(app->view, spi_mem_view_progress_draw_callback); + view_set_input_callback(app->view, spi_mem_view_progress_input_callback); + spi_mem_view_progress_reset(app); + return app; +} + +void spi_mem_view_progress_free(SPIMemProgressView* app) { + view_free(app->view); + free(app); +} + +void spi_mem_view_progress_set_read_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeRead; }, + true); +} + +void spi_mem_view_progress_set_verify_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeVerify; }, + true); +} + +void spi_mem_view_progress_set_write_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx) { + app->callback = callback; + app->cb_ctx = cb_ctx; + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { model->view_type = SPIMemProgressViewTypeWrite; }, + true); +} + +void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->chip_size = chip_size; }, true); +} + +void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->file_size = file_size; }, true); +} + +void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size) { + with_view_model( + app->view, SPIMemProgressViewModel * model, { model->block_size = block_size; }, true); +} + +static size_t spi_mem_view_progress_set_total_size(SPIMemProgressViewModel* model) { + size_t total_size = model->chip_size; + if((model->chip_size > model->file_size) && model->view_type != SPIMemProgressViewTypeRead) { + total_size = model->file_size; + } + return total_size; +} + +void spi_mem_view_progress_inc_progress(SPIMemProgressView* app) { + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { + size_t total_size = spi_mem_view_progress_set_total_size(model); + if(total_size == 0) total_size = 1; + model->blocks_written++; + model->progress = + ((float)model->block_size * (float)model->blocks_written) / ((float)total_size); + }, + true); +} + +void spi_mem_view_progress_reset(SPIMemProgressView* app) { + with_view_model( + app->view, + SPIMemProgressViewModel * model, + { + model->blocks_written = 0; + model->block_size = 0; + model->chip_size = 0; + model->file_size = 0; + model->progress = 0; + model->view_type = SPIMemProgressViewTypeUnknown; + }, + true); +} diff --git a/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h new file mode 100644 index 000000000..6a8645b6c --- /dev/null +++ b/applications/plugins/spi_mem_manager/views/spi_mem_view_progress.h @@ -0,0 +1,26 @@ +#pragma once +#include + +typedef struct SPIMemProgressView SPIMemProgressView; +typedef void (*SPIMemProgressViewCallback)(void* context); + +View* spi_mem_view_progress_get_view(SPIMemProgressView* app); +SPIMemProgressView* spi_mem_view_progress_alloc(); +void spi_mem_view_progress_free(SPIMemProgressView* app); +void spi_mem_view_progress_set_read_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_verify_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_write_callback( + SPIMemProgressView* app, + SPIMemProgressViewCallback callback, + void* cb_ctx); +void spi_mem_view_progress_set_chip_size(SPIMemProgressView* app, size_t chip_size); +void spi_mem_view_progress_set_file_size(SPIMemProgressView* app, size_t file_size); +void spi_mem_view_progress_set_block_size(SPIMemProgressView* app, size_t block_size); +void spi_mem_view_progress_inc_progress(SPIMemProgressView* app); +void spi_mem_view_progress_reset(SPIMemProgressView* app); From 1be72a94a82125101aaf6cf81790825d6e85853b Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 14:06:52 +0000 Subject: [PATCH 087/231] Fix build for now --- .github/workflows/build.yml | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5b19d123b..edaff1d31 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -79,14 +79,14 @@ jobs: - name: 'Bundle core2 firmware' run: | cp build/core2_firmware.tgz "artifacts/flipper-z-any-core2_firmware-${SUFFIX}.tgz" - + - name: 'Updater artifact' uses: actions/upload-artifact@v3 with: name: updater path: | artifacts/f7-* - + - name: 'Firmware artifact' uses: actions/upload-artifact@v3 with: @@ -94,31 +94,31 @@ jobs: path: | artifacts - - name: 'Find Previous Comment' - if: ${{ github.event.pull_request }} - uses: peter-evans/find-comment@v1 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: 'Compiled firmware for commit' - - - name: Artifact info - id: artifact-info - uses: dawidd6/action-download-artifact@v2 - with: - dry_run: true + # - name: 'Find Previous Comment' + # if: ${{ github.event.pull_request }} + # uses: peter-evans/find-comment@v1 + # id: fc + # with: + # issue-number: ${{ github.event.pull_request.number }} + # comment-author: 'github-actions[bot]' + # body-includes: 'Compiled firmware for commit' - - name: 'Create or update comment' - if: ${{ github.event.pull_request}} - uses: peter-evans/create-or-update-comment@v1 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - issue-number: ${{ github.event.pull_request.number }} - body: | - **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** - - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) - edit-mode: replace + # - name: Artifact info + # id: artifact-info + # uses: dawidd6/action-download-artifact@v2 + # with: + # dry_run: true + + # - name: 'Create or update comment' + # if: ${{ github.event.pull_request}} + # uses: peter-evans/create-or-update-comment@v1 + # with: + # comment-id: ${{ steps.fc.outputs.comment-id }} + # issue-number: ${{ github.event.pull_request.number }} + # body: | + # **Compiled firmware for commit `${{steps.names.outputs.commit_sha}}`:** + # - [📦 Update package](${{steps.artifact-info.outputs.artifacts[0].archive_download_url}}) + # edit-mode: replace compact: if: ${{ !startsWith(github.ref, 'refs/tags') }} From 3acfe5b2153914c35f74c8d906059d26e426817c Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 14:16:03 +0000 Subject: [PATCH 088/231] Format --- applications/services/bt/bt_service/bt.c | 4 ++-- applications/services/bt/bt_service/bt.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index cc5e8f33e..6949bbc41 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -445,12 +445,12 @@ GapPairing bt_get_profile_pairing_method(Bt* bt) { return furi_hal_bt_get_profile_pairing_method(get_hal_bt_profile(bt->profile)); } -void bt_disable_peer_key_update(Bt *bt) { +void bt_disable_peer_key_update(Bt* bt) { UNUSED(bt); furi_hal_bt_set_key_storage_change_callback(NULL, NULL); } -void bt_enable_peer_key_update(Bt *bt) { +void bt_enable_peer_key_update(Bt* bt) { furi_hal_bt_set_key_storage_change_callback(bt_on_key_storage_change_callback, bt); } diff --git a/applications/services/bt/bt_service/bt.h b/applications/services/bt/bt_service/bt.h index ddf9382e8..a79c227f7 100644 --- a/applications/services/bt/bt_service/bt.h +++ b/applications/services/bt/bt_service/bt.h @@ -55,13 +55,13 @@ GapPairing bt_get_profile_pairing_method(Bt* bt); /** Stop saving new peer key to flash (in .bt.keys file) * */ -void bt_disable_peer_key_update(Bt *bt); +void bt_disable_peer_key_update(Bt* bt); /** Enable saving peer key to internal flash (enable by default) * * @note This function should be called if bt_disable_peer_key_update was called before */ -void bt_enable_peer_key_update(Bt *bt); +void bt_enable_peer_key_update(Bt* bt); /** Disconnect from Central * From 6e179bda1f69f3ce1c216faa90d24577ebeea631 Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Mon, 6 Feb 2023 17:56:36 +0300 Subject: [PATCH 089/231] Script that can find programmer and flash firmware via it. (#2193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Init * Fallback to networked interface * remove unneeded cmsis_dap_backend * serial number * windows :( * remove jlink, fix path handling * scripts: program: path normalization * scripts: program: path normalization: second encounter Co-authored-by: hedger Co-authored-by: あく --- scripts/program.py | 459 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100755 scripts/program.py diff --git a/scripts/program.py b/scripts/program.py new file mode 100755 index 000000000..c140a9024 --- /dev/null +++ b/scripts/program.py @@ -0,0 +1,459 @@ +#!/usr/bin/env python3 +import typing +import subprocess +import logging +import time +import os +import socket + +from abc import ABC, abstractmethod +from dataclasses import dataclass +from flipper.app import App + + +class Programmer(ABC): + @abstractmethod + def flash(self, bin: str) -> bool: + pass + + @abstractmethod + def probe(self) -> bool: + pass + + @abstractmethod + def get_name(self) -> str: + pass + + @abstractmethod + def set_serial(self, serial: str): + pass + + +@dataclass +class OpenOCDInterface: + name: str + file: str + serial_cmd: str + additional_args: typing.Optional[list[str]] = None + + +class OpenOCDProgrammer(Programmer): + def __init__(self, interface: OpenOCDInterface): + self.interface = interface + self.logger = logging.getLogger("OpenOCD") + self.serial: typing.Optional[str] = None + + def _add_file(self, params: list[str], file: str): + params.append("-f") + params.append(file) + + def _add_command(self, params: list[str], command: str): + params.append("-c") + params.append(command) + + def _add_serial(self, params: list[str], serial: str): + self._add_command(params, f"{self.interface.serial_cmd} {serial}") + + def set_serial(self, serial: str): + self.serial = serial + + def flash(self, bin: str) -> bool: + i = self.interface + + if os.altsep: + bin = bin.replace(os.sep, os.altsep) + + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, i.file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if i.additional_args: + for a in i.additional_args: + self._add_command(openocd_launch_params, a) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + self._add_command(openocd_launch_params, f"program {bin} reset exit 0x8000000") + + # join the list of parameters into a string, but add quote if there are spaces + openocd_launch_params_string = " ".join( + [f'"{p}"' if " " in p else p for p in openocd_launch_params] + ) + + self.logger.debug(f"Launching: {openocd_launch_params_string}") + + process = subprocess.Popen( + openocd_launch_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + while process.poll() is None: + time.sleep(0.25) + print(".", end="", flush=True) + print() + + success = process.returncode == 0 + + if not success: + self.logger.error("OpenOCD failed to flash") + if process.stdout: + self.logger.error(process.stdout.read().decode("utf-8").strip()) + + return success + + def probe(self) -> bool: + i = self.interface + + openocd_launch_params = ["openocd"] + self._add_file(openocd_launch_params, i.file) + if self.serial: + self._add_serial(openocd_launch_params, self.serial) + if i.additional_args: + for a in i.additional_args: + self._add_command(openocd_launch_params, a) + self._add_file(openocd_launch_params, "target/stm32wbx.cfg") + self._add_command(openocd_launch_params, "init") + self._add_command(openocd_launch_params, "exit") + + self.logger.debug(f"Launching: {' '.join(openocd_launch_params)}") + + process = subprocess.Popen( + openocd_launch_params, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, + ) + + # Wait for OpenOCD to end and get the return code + process.wait() + found = process.returncode == 0 + + if process.stdout: + self.logger.debug(process.stdout.read().decode("utf-8").strip()) + + return found + + def get_name(self) -> str: + return self.interface.name + + +def blackmagic_find_serial(serial: str): + import serial.tools.list_ports as list_ports + + if serial and os.name == "nt": + if not serial.startswith("\\\\.\\"): + serial = f"\\\\.\\{serial}" + + ports = list(list_ports.grep("blackmagic")) + if len(ports) == 0: + return None + elif len(ports) > 2: + if serial: + ports = list( + filter( + lambda p: p.serial_number == serial + or p.name == serial + or p.device == serial, + ports, + ) + ) + if len(ports) == 0: + return None + + if len(ports) > 2: + raise Exception("More than one Blackmagic probe found") + + # If you're getting any issues with auto lookup, uncomment this + # print("\n".join([f"{p.device} {vars(p)}" for p in ports])) + port = sorted(ports, key=lambda p: f"{p.location}_{p.name}")[0] + + if serial: + if ( + serial != port.serial_number + and serial != port.name + and serial != port.device + ): + return None + + if os.name == "nt": + port.device = f"\\\\.\\{port.device}" + return port.device + + +def _resolve_hostname(hostname): + try: + return socket.gethostbyname(hostname) + except socket.gaierror: + return None + + +def blackmagic_find_networked(serial: str): + if not serial: + serial = "blackmagic.local" + + # remove the tcp: prefix if it's there + if serial.startswith("tcp:"): + serial = serial[4:] + + # remove the port if it's there + if ":" in serial: + serial = serial.split(":")[0] + + if not (probe := _resolve_hostname(serial)): + return None + + return f"tcp:{probe}:2345" + + +class BlackmagicProgrammer(Programmer): + def __init__( + self, + port_resolver, # typing.Callable[typing.Union[str, None], typing.Optional[str]] + name: str, + ): + self.port_resolver = port_resolver + self.name = name + self.logger = logging.getLogger("BlackmagicUSB") + self.port: typing.Optional[str] = None + + def _add_command(self, params: list[str], command: str): + params.append("-ex") + params.append(command) + + def _valid_ip(self, address): + try: + socket.inet_aton(address) + return True + except: + return False + + def set_serial(self, serial: str): + if self._valid_ip(serial): + self.port = f"{serial}:2345" + elif ip := _resolve_hostname(serial): + self.port = f"{ip}:2345" + else: + self.port = serial + + def flash(self, bin: str) -> bool: + if not self.port: + if not self.probe(): + return False + + # We can convert .bin to .elf with objcopy: + # arm-none-eabi-objcopy -I binary -O elf32-littlearm --change-section-address=.data=0x8000000 -B arm -S app.bin app.elf + # But I choose to use the .elf file directly because we are flashing our own firmware and it always has an elf predecessor. + elf = bin.replace(".bin", ".elf") + if not os.path.exists(elf): + self.logger.error( + f"Sorry, but Blackmagic can't flash .bin file, and {elf} doesn't exist" + ) + return False + + # arm-none-eabi-gdb build/f7-firmware-D/firmware.bin + # -ex 'set pagination off' + # -ex 'target extended-remote /dev/cu.usbmodem21201' + # -ex 'set confirm off' + # -ex 'monitor swdp_scan' + # -ex 'attach 1' + # -ex 'set mem inaccessible-by-default off' + # -ex 'load' + # -ex 'compare-sections' + # -ex 'quit' + + gdb_launch_params = ["arm-none-eabi-gdb", elf] + self._add_command(gdb_launch_params, f"target extended-remote {self.port}") + self._add_command(gdb_launch_params, "set pagination off") + self._add_command(gdb_launch_params, "set confirm off") + self._add_command(gdb_launch_params, "monitor swdp_scan") + self._add_command(gdb_launch_params, "attach 1") + self._add_command(gdb_launch_params, "set mem inaccessible-by-default off") + self._add_command(gdb_launch_params, "load") + self._add_command(gdb_launch_params, "compare-sections") + self._add_command(gdb_launch_params, "quit") + + self.logger.debug(f"Launching: {' '.join(gdb_launch_params)}") + + process = subprocess.Popen( + gdb_launch_params, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + + while process.poll() is None: + time.sleep(0.5) + print(".", end="", flush=True) + print() + + if not process.stdout: + return False + + output = process.stdout.read().decode("utf-8").strip() + flashed = "Loading section .text," in output + + # Check flash verification + if "MIS-MATCHED!" in output: + flashed = False + + if "target image does not match the loaded file" in output: + flashed = False + + if not flashed: + self.logger.error("Blackmagic failed to flash") + self.logger.error(output) + + return flashed + + def probe(self) -> bool: + if not (port := self.port_resolver(self.port)): + return False + + self.port = port + return True + + def get_name(self) -> str: + return self.name + + +programmers: list[Programmer] = [ + OpenOCDProgrammer( + OpenOCDInterface( + "cmsis-dap", + "interface/cmsis-dap.cfg", + "cmsis_dap_serial", + ["transport select swd"], + ), + ), + OpenOCDProgrammer( + OpenOCDInterface( + "stlink", "interface/stlink.cfg", "hla_serial", ["transport select hla_swd"] + ), + ), + BlackmagicProgrammer(blackmagic_find_serial, "blackmagic_usb"), +] + +network_programmers = [ + BlackmagicProgrammer(blackmagic_find_networked, "blackmagic_wifi") +] + + +class Main(App): + def init(self): + self.subparsers = self.parser.add_subparsers(help="sub-command help") + self.parser_flash = self.subparsers.add_parser("flash", help="Flash a binary") + self.parser_flash.add_argument( + "bin", + type=str, + help="Binary to flash", + ) + interfaces = [i.get_name() for i in programmers] + interfaces.extend([i.get_name() for i in network_programmers]) + self.parser_flash.add_argument( + "--interface", + choices=interfaces, + type=str, + help="Interface to use", + ) + self.parser_flash.add_argument( + "--serial", + type=str, + help="Serial number or port of the programmer", + ) + self.parser_flash.set_defaults(func=self.flash) + + def _search_interface(self, serial: typing.Optional[str]) -> list[Programmer]: + found_programmers = [] + + for p in programmers: + name = p.get_name() + if serial: + p.set_serial(serial) + self.logger.debug(f"Trying {name} with {serial}") + else: + self.logger.debug(f"Trying {name}") + + if p.probe(): + self.logger.debug(f"Found {name}") + found_programmers += [p] + else: + self.logger.debug(f"Failed to probe {name}") + + return found_programmers + + def _search_network_interface( + self, serial: typing.Optional[str] + ) -> list[Programmer]: + found_programmers = [] + + for p in network_programmers: + name = p.get_name() + + if serial: + p.set_serial(serial) + self.logger.debug(f"Trying {name} with {serial}") + else: + self.logger.debug(f"Trying {name}") + + if p.probe(): + self.logger.debug(f"Found {name}") + found_programmers += [p] + else: + self.logger.debug(f"Failed to probe {name}") + + return found_programmers + + def flash(self): + start_time = time.time() + bin_path = os.path.abspath(self.args.bin) + + if not os.path.exists(bin_path): + self.logger.error(f"Binary file not found: {bin_path}") + return 1 + + if self.args.interface: + i_name = self.args.interface + interfaces = [p for p in programmers if p.get_name() == i_name] + if len(interfaces) == 0: + interfaces = [p for p in network_programmers if p.get_name() == i_name] + else: + self.logger.info(f"Probing for interfaces...") + interfaces = self._search_interface(self.args.serial) + + if len(interfaces) == 0: + # Probe network blackmagic + self.logger.info(f"Probing for network interfaces...") + interfaces = self._search_network_interface(self.args.serial) + + if len(interfaces) == 0: + self.logger.error("No interface found") + return 1 + + if len(interfaces) > 1: + self.logger.error("Multiple interfaces found: ") + self.logger.error( + f"Please specify '--interface={[i.get_name() for i in interfaces]}'" + ) + return 1 + + interface = interfaces[0] + + if self.args.serial: + interface.set_serial(self.args.serial) + self.logger.info( + f"Flashing {bin_path} via {interface.get_name()} with {self.args.serial}" + ) + else: + self.logger.info(f"Flashing {bin_path} via {interface.get_name()}") + + if not interface.flash(bin_path): + self.logger.error(f"Failed to flash via {interface.get_name()}") + return 1 + + flash_time = time.time() - start_time + bin_size = os.path.getsize(bin_path) + self.logger.info(f"Flashed successfully in {flash_time:.2f}s") + self.logger.info(f"Effective speed: {bin_size / flash_time / 1024:.2f} KiB/s") + return 0 + + +if __name__ == "__main__": + Main()() From 79d45c97f93d54b0082519d7a6a7e9a77e579406 Mon Sep 17 00:00:00 2001 From: nas Date: Mon, 6 Feb 2023 20:24:05 +0500 Subject: [PATCH 090/231] AleX83Xpert/add f keys to keyboard layout (#2362) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Assets: png images for F1..F12 keys: Will be used for HID keyboard layout * HID KEYBOARD F1..F12 keys were added - a new row with functional keys was added - logic for displaying the keyboard layout was improved * HidApp: hid functional keys by default Co-authored-by: あく --- .../plugins/hid_app/assets/ButtonF10_5x8.png | Bin 0 -> 172 bytes .../plugins/hid_app/assets/ButtonF11_5x8.png | Bin 0 -> 173 bytes .../plugins/hid_app/assets/ButtonF12_5x8.png | Bin 0 -> 180 bytes .../plugins/hid_app/assets/ButtonF1_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF2_5x8.png | Bin 0 -> 179 bytes .../plugins/hid_app/assets/ButtonF3_5x8.png | Bin 0 -> 178 bytes .../plugins/hid_app/assets/ButtonF4_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF5_5x8.png | Bin 0 -> 178 bytes .../plugins/hid_app/assets/ButtonF6_5x8.png | Bin 0 -> 177 bytes .../plugins/hid_app/assets/ButtonF7_5x8.png | Bin 0 -> 176 bytes .../plugins/hid_app/assets/ButtonF8_5x8.png | Bin 0 -> 176 bytes .../plugins/hid_app/assets/ButtonF9_5x8.png | Bin 0 -> 179 bytes .../plugins/hid_app/views/hid_keyboard.c | 28 ++++++++++++++++-- 13 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 applications/plugins/hid_app/assets/ButtonF10_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF11_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF12_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF1_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF2_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF3_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF4_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF5_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF6_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF7_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF8_5x8.png create mode 100644 applications/plugins/hid_app/assets/ButtonF9_5x8.png diff --git a/applications/plugins/hid_app/assets/ButtonF10_5x8.png b/applications/plugins/hid_app/assets/ButtonF10_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d1a7a04f06dcc4b5446aad5a40a9863038bf56b5 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA~jDJ#}JO|$tewu|Ns9tHZU+a6e)0^L#NYq9;5F(PTxtKzKT}z3_A)0e;QviHwNlp N@O1TaS?83{1OPU#E%g8Z literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF11_5x8.png b/applications/plugins/hid_app/assets/ButtonF11_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7e177358e81695342f2a283a220c7cacc7bda939 GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOpBPSmNg(OQ{BTAg}b8}PkN*J7rQWHy3QxwWGOEMJPJ$(bh8~Mb6 ziqt(_978y+C#N(t{{R2q*ucQxP^7?t4xLU{IYmyznHN-MN(3-k$uq=X7x{LAUCkJ% Og~8L+&t;ucLK6T7Y%hHP literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF12_5x8.png b/applications/plugins/hid_app/assets/ButtonF12_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..50d2a7dc63b9d366ccfbacbc05e6bb0d9e335b5b GIT binary patch literal 180 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk)EfEV+hCf+#W|R1_Pc$J^%l2ww|&zRcE|OcGEe{j literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF1_5x8.png b/applications/plugins/hid_app/assets/ButtonF1_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..7394d27105fd0a27067803bfd633a26bedd0f1d5 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|$tewu|Ns9tHZU+a6e)0^L+9jy0|#0HPM+X+s?4mGa`wiPi$55Iw(~PB TTpxE5sExtX)z4*}Q$iB}`k6I% literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF2_5x8.png b/applications/plugins/hid_app/assets/ButtonF2_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..9d922a3858147116d65b6f03e2b36ea846b2f60c GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk*=qUV+hCf)NV&U1_ho&w~qX;m*hWYIaS!#v1}4USoB8kx*gxNJa@^Tf4zarr_o#d UH)s04hd_-Cp00i_>zopr0BX-NbN~PV literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF3_5x8.png b/applications/plugins/hid_app/assets/ButtonF3_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..95c2dd4f4198e182a1a62927c4d3627400a7b883 GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO zk&dT}V+hCf)GkXd1_g%0LLdIeuWPOlF*aSY$&;z#+9C5#-hV?Uh3H=_oX_AhhhO~n UO!>#_f%+IcUHx3vIVCg!073*Z7ytkO literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF4_5x8.png b/applications/plugins/hid_app/assets/ButtonF4_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..602466f4b667b6df4d5335517bd9d43e5f0b6e69 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|vE3Va85}s64*o6Qr{DAJBNr3n8pa7vN_+nsOQj%x4ZT`lf}PS TtvtgA)W+cH>gTe~DWM4fb!sy& literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF5_5x8.png b/applications/plugins/hid_app/assets/ButtonF5_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..d73b5405275f6c53f7a2f157df063db1ca351caa GIT binary patch literal 178 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOFVdQ&MBb@0Fom!%>V!Z literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF6_5x8.png b/applications/plugins/hid_app/assets/ButtonF6_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..c50748257ab8e06f90007e93b913d5be4999d096 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tOA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zB5h9>#}JO|shy5|3<^BWD?a{@Kh_*L{p89i1p$qBwtDvdG5Wpwnq5@=JeG)jb@A^; T#rkJ}+88`t{an^LB{Ts5$FwwN literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF7_5x8.png b/applications/plugins/hid_app/assets/ButtonF7_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..396c98f5104f94b6310593ce6c7e6ce3d2369ef3 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO+nf~Hxmr&P}udD~(3jVRo RuLQY`!PC{xWt~$(69B*FH3|R# literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/assets/ButtonF8_5x8.png b/applications/plugins/hid_app/assets/ButtonF8_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..6304d7fb888b2cf38c54e7124aaa604d1610629c GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^tU%1c!2~4tO5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA}voB#}JO|wVjT93<^BWEB^mCmh0Jd#>H=GOE1@xHLh7tre2KydVRe*qgvi_@vq`% Sf29C*F?hQAxvXEZzkxv|` zNY~TFF@)oKa!Nzv|NsAu4GatpMG73~&^g&~GSNx=@BjbyU3DUS%F5hxSQ8l-I!}xL U>{@7g2&j?4)78&qol`;+0Ic0IyZ`_I literal 0 HcmV?d00001 diff --git a/applications/plugins/hid_app/views/hid_keyboard.c b/applications/plugins/hid_app/views/hid_keyboard.c index 8b12e8fd1..82e772b15 100644 --- a/applications/plugins/hid_app/views/hid_keyboard.c +++ b/applications/plugins/hid_app/views/hid_keyboard.c @@ -46,11 +46,25 @@ typedef struct { #define KEY_WIDTH 9 #define KEY_HEIGHT 12 #define KEY_PADDING 1 -#define ROW_COUNT 6 +#define ROW_COUNT 7 #define COLUMN_COUNT 12 // 0 width items are not drawn, but there value is used const HidKeyboardKey hid_keyboard_keyset[ROW_COUNT][COLUMN_COUNT] = { + { + {.width = 1, .icon = &I_ButtonF1_5x8, .value = HID_KEYBOARD_F1}, + {.width = 1, .icon = &I_ButtonF2_5x8, .value = HID_KEYBOARD_F2}, + {.width = 1, .icon = &I_ButtonF3_5x8, .value = HID_KEYBOARD_F3}, + {.width = 1, .icon = &I_ButtonF4_5x8, .value = HID_KEYBOARD_F4}, + {.width = 1, .icon = &I_ButtonF5_5x8, .value = HID_KEYBOARD_F5}, + {.width = 1, .icon = &I_ButtonF6_5x8, .value = HID_KEYBOARD_F6}, + {.width = 1, .icon = &I_ButtonF7_5x8, .value = HID_KEYBOARD_F7}, + {.width = 1, .icon = &I_ButtonF8_5x8, .value = HID_KEYBOARD_F8}, + {.width = 1, .icon = &I_ButtonF9_5x8, .value = HID_KEYBOARD_F9}, + {.width = 1, .icon = &I_ButtonF10_5x8, .value = HID_KEYBOARD_F10}, + {.width = 1, .icon = &I_ButtonF11_5x8, .value = HID_KEYBOARD_F11}, + {.width = 1, .icon = &I_ButtonF12_5x8, .value = HID_KEYBOARD_F12}, + }, { {.width = 1, .icon = NULL, .key = "1", .shift_key = "!", .value = HID_KEYBOARD_1}, {.width = 1, .icon = NULL, .key = "2", .shift_key = "@", .value = HID_KEYBOARD_2}, @@ -224,7 +238,12 @@ static void hid_keyboard_draw_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontKeyboard); // Start shifting the all keys up if on the next row (Scrolling) - uint8_t initY = model->y - 4 > 0 ? model->y - 4 : 0; + uint8_t initY = model->y == 0 ? 0 : 1; + + if(model->y > 5) { + initY = model->y - 4; + } + for(uint8_t y = initY; y < ROW_COUNT; y++) { const HidKeyboardKey* keyboardKeyRow = hid_keyboard_keyset[y]; uint8_t x = 0; @@ -365,7 +384,10 @@ HidKeyboard* hid_keyboard_alloc(Hid* bt_hid) { with_view_model( hid_keyboard->view, HidKeyboardModel * model, - { model->transport = bt_hid->transport; }, + { + model->transport = bt_hid->transport; + model->y = 1; + }, true); return hid_keyboard; From 147f42a2b7bbcd5a497e862be47a72ef2c233801 Mon Sep 17 00:00:00 2001 From: Wild Rat Date: Mon, 6 Feb 2023 17:49:34 +0200 Subject: [PATCH 091/231] Add Daikin FTXM20M & Mitsubishi SRK63HE (#2349) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- assets/resources/infrared/assets/ac.ir | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/assets/resources/infrared/assets/ac.ir b/assets/resources/infrared/assets/ac.ir index 1f9c2145f..96a2a0f38 100644 --- a/assets/resources/infrared/assets/ac.ir +++ b/assets/resources/infrared/assets/ac.ir @@ -297,3 +297,77 @@ type: raw frequency: 38000 duty_cycle: 0.330000 data: 2320 634 837 637 838 637 838 640 835 642 832 1378 836 645 826 670 809 667 808 1406 806 672 803 674 802 1412 802 1412 800 676 801 675 802 1412 802 674 802 1413 801 1412 801 1413 802 1412 802 50937 2285 671 801 1411 802 51225 2280 696 775 1412 801 51212 2283 671 775 1412 802 +# Model: Daikin FTXM20M. +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 393 473 392 473 368 498 368 497 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1263 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1268 464 1269 463 1269 463 1292 440 1269 463 404 462 426 439 1293 439 426 440 426 440 426 440 426 440 426 440 427 439 426 440 427 439 427 438 427 439 1294 438 427 439 1294 438 427 439 427 439 428 438 1294 438 1294 438 428 438 428 437 428 438 428 438 1294 438 428 438 428 438 429 437 429 437 429 436 429 437 429 437 429 437 429 436 429 437 430 436 1296 436 1296 436 1296 436 430 436 430 435 1297 435 1297 435 1298 434 35482 3500 1699 464 1268 464 402 463 402 463 403 463 1269 463 426 440 426 439 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1294 438 427 438 427 439 1294 438 427 439 428 438 428 438 428 438 427 438 428 438 428 437 428 438 428 437 428 438 428 438 1295 437 429 437 429 437 429 436 429 437 1296 436 429 437 429 436 429 437 430 436 430 436 430 435 430 436 430 435 431 435 431 435 431 435 455 410 456 409 1322 410 456 409 456 410 456 410 456 410 456 410 1323 409 457 409 457 409 1323 409 1323 409 457 409 35483 3500 1699 464 1268 464 402 464 402 463 403 463 1269 463 426 440 426 440 426 440 426 439 1293 439 426 439 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1294 438 1293 438 427 439 427 438 1294 438 428 438 427 438 428 438 428 438 428 438 428 437 428 438 428 438 428 438 428 438 428 438 428 438 429 437 429 437 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 430 436 1297 435 430 436 430 436 431 435 431 435 456 410 456 410 456 410 456 409 1323 409 1322 410 456 410 456 409 457 409 457 409 457 409 457 409 457 408 457 409 1324 408 1324 408 1324 408 1324 408 458 408 1325 406 459 407 1351 356 1376 356 1376 356 1376 356 1377 355 510 355 510 356 511 355 511 354 511 355 537 328 537 329 538 328 538 327 538 328 539 327 565 300 566 300 1432 300 1433 298 593 272 594 271 621 245 621 244 622 243 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 500 368 498 369 497 367 499 366 499 367 499 25050 3533 1662 502 1230 501 392 472 393 471 395 469 1264 467 400 465 401 465 401 465 401 465 1267 465 401 465 1268 464 1268 464 402 464 1269 463 1269 463 1269 463 1292 440 1292 440 426 440 426 440 1293 439 426 440 426 439 426 440 426 440 427 439 427 438 427 439 427 439 427 439 427 439 1293 439 427 439 1294 438 427 439 427 439 428 437 1294 438 1294 438 428 437 428 438 428 437 429 437 1295 437 428 437 429 437 429 437 429 436 429 437 429 437 429 437 429 437 429 437 429 437 430 436 1296 436 1297 435 1297 435 431 435 455 410 1298 434 1322 410 1322 410 35482 3500 1700 464 1269 463 403 463 403 463 403 462 1292 440 426 440 426 440 426 440 426 439 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 439 1293 439 1293 439 427 439 427 439 1294 438 427 439 427 439 427 439 428 438 427 438 428 438 428 438 428 437 428 438 428 438 428 438 1295 437 429 437 429 436 429 437 429 437 1296 436 429 437 429 437 429 437 430 436 430 436 430 436 431 435 431 435 431 435 455 410 456 410 456 410 456 410 1322 410 456 410 456 410 456 409 457 409 456 409 1323 409 457 409 457 409 1323 409 1324 408 458 408 35483 3500 1700 464 1268 464 402 464 403 463 402 464 1292 440 426 440 426 440 426 439 426 440 1293 439 426 440 1293 439 1293 439 427 439 1293 439 1293 439 1293 438 1293 439 1293 439 427 439 427 438 1294 438 428 438 428 438 427 438 428 438 428 438 428 438 428 438 428 438 428 438 428 437 428 438 429 436 429 437 429 437 429 437 429 437 429 437 429 437 1296 436 430 435 430 436 1297 435 431 435 1297 435 431 435 456 409 456 410 456 409 456 410 456 410 456 410 456 410 1323 409 1323 409 457 409 457 409 457 408 457 409 457 409 458 408 458 407 458 408 1325 407 1326 405 1327 406 1351 381 485 356 1376 356 510 355 1377 355 1377 355 1377 355 1378 354 1378 354 512 354 512 354 538 327 538 328 538 328 539 326 565 300 566 300 566 299 567 299 593 272 621 244 596 270 1488 244 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 504 389 477 364 501 364 502 364 501 364 502 25048 3511 1684 505 1228 504 389 475 390 474 392 472 1260 470 396 469 396 470 396 469 397 469 1263 469 397 469 1263 469 1263 469 397 468 1263 469 1263 469 1264 468 1263 469 1264 468 397 468 398 468 1264 468 398 468 398 468 398 468 398 468 398 468 399 467 398 468 399 467 423 443 400 466 1289 443 423 443 1289 443 423 443 423 443 423 443 1289 443 1290 442 424 442 423 442 423 443 424 442 1290 442 424 442 423 443 424 442 424 441 424 442 424 441 424 442 424 442 424 441 424 442 424 442 1291 441 1291 441 1291 441 425 441 425 441 1291 441 1291 441 1291 441 35478 3505 1695 468 1264 468 398 467 398 468 398 468 1265 467 398 467 398 468 398 468 399 467 1265 467 399 467 1266 466 1266 466 423 443 1289 443 1290 442 1290 442 1290 442 1290 442 424 442 423 442 1290 442 423 443 424 442 424 442 424 442 424 441 424 442 424 442 424 441 424 442 424 442 424 442 1291 441 425 441 425 441 424 442 424 441 1291 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 440 426 440 426 440 1292 440 426 440 426 440 426 440 426 439 427 439 1293 439 427 439 427 438 1294 438 1294 438 428 437 35479 3504 1695 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 466 399 467 1265 467 399 467 1289 443 1290 442 423 443 1290 442 1290 442 1290 442 1290 442 1290 442 423 443 424 442 1290 442 424 442 424 442 424 441 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1291 441 425 441 425 441 1292 440 1292 440 1292 440 426 440 425 441 426 440 426 440 1292 440 426 440 426 440 1292 440 426 440 426 440 426 440 427 439 426 440 426 440 427 438 427 439 427 439 427 439 1294 438 1295 437 1294 438 1319 413 453 413 1319 413 453 412 1320 412 1319 413 1319 413 1319 413 1320 412 453 412 453 413 453 412 454 412 454 411 454 412 454 412 454 412 454 412 454 412 455 411 454 412 455 411 1321 411 1321 411 456 409 456 410 481 384 482 384 482 383 482 359 506 360 507 359 507 359 507 358 1374 358 1374 358 508 357 508 358 509 357 534 332 534 332 534 332 534 332 535 331 535 331 535 331 535 330 536 330 536 330 562 303 563 303 563 302 564 302 1430 302 565 301 564 302 591 274 619 247 619 247 1512 219 1539 190 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 502 390 476 365 501 365 500 365 500 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 469 397 468 398 468 398 468 398 468 1264 468 398 468 1265 467 1265 467 399 467 1265 467 1265 467 1265 467 1266 466 1265 467 400 466 401 465 1266 466 401 465 401 465 401 465 424 442 424 441 424 442 424 441 424 442 424 442 424 442 1291 441 424 442 1291 441 424 442 425 440 425 441 1291 441 1291 441 425 441 425 441 425 440 425 441 1292 440 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 1293 439 1293 439 426 439 427 439 1293 439 1293 439 1293 439 35480 3503 1696 467 1265 467 399 467 398 468 399 466 1266 466 399 467 399 467 399 467 399 467 1266 466 400 466 1267 465 1290 442 424 442 1290 442 1290 442 1290 442 1290 441 1291 441 424 442 424 442 1291 441 424 442 424 442 425 441 424 442 424 442 425 441 425 440 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 440 426 440 1292 440 426 440 426 439 426 440 426 439 426 440 426 440 426 440 426 440 426 440 427 438 427 439 427 439 427 439 1293 439 427 438 427 439 428 438 428 438 428 438 1294 438 428 438 428 438 1295 437 1320 412 453 413 35480 3503 1696 467 1265 467 399 467 399 467 399 467 1266 466 399 467 399 467 400 466 400 466 1290 442 424 442 1289 443 1290 442 424 442 1291 441 1290 442 1290 442 1291 441 1291 441 424 442 424 442 1291 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 440 426 440 426 440 426 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 427 439 426 439 427 439 1293 439 1293 439 1293 439 427 438 1294 438 427 439 427 438 428 438 427 438 453 413 429 437 429 437 429 437 453 412 454 412 1320 412 1320 412 1320 412 1320 412 454 412 1321 411 454 412 1320 412 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 411 455 411 456 410 456 410 457 409 481 384 457 409 482 383 483 383 483 358 1374 358 1374 358 508 357 508 358 509 357 509 357 509 357 509 357 510 356 535 330 536 330 536 330 1402 330 1403 329 537 329 536 329 563 302 564 302 564 302 565 300 565 300 592 273 619 246 620 246 619 246 620 245 674 188 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 504 365 500 365 500 365 500 365 501 365 501 25049 3535 1660 504 1229 502 390 474 391 473 393 471 1261 470 397 468 397 469 398 468 398 468 1264 468 398 467 1265 467 1265 467 398 468 1265 467 1265 467 1265 467 1266 466 1266 466 399 466 399 467 1266 466 400 466 400 466 400 465 400 466 424 442 424 442 424 442 424 441 424 442 424 442 1291 441 424 442 1291 441 424 442 424 442 425 441 1291 441 1291 441 425 440 425 441 425 441 425 440 1292 440 425 441 425 440 425 441 425 441 425 441 425 441 425 441 426 440 426 440 426 440 426 439 1293 439 1293 439 1293 439 426 440 427 439 1293 439 1293 439 1293 439 35480 3503 1696 468 1264 468 398 468 398 468 398 467 1265 467 399 467 399 467 399 467 399 467 1267 465 400 466 1266 466 1267 465 424 441 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 442 1291 441 424 442 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 425 441 425 441 425 441 425 441 1292 440 426 440 426 439 426 440 426 440 426 439 426 440 426 440 426 439 427 439 426 440 427 439 427 439 427 438 1293 439 427 439 427 439 427 439 428 438 428 438 1295 437 428 438 429 436 1296 436 1296 436 453 412 35481 3502 1696 467 1265 467 399 466 399 467 399 467 1265 467 399 467 399 467 399 466 400 466 1266 466 400 466 1290 442 1267 465 424 442 1290 442 1290 442 1290 442 1290 442 1291 441 424 442 424 441 1291 441 424 442 424 442 424 442 424 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 441 425 440 426 440 425 441 425 441 425 441 426 440 1292 440 426 440 426 440 1292 440 426 440 426 439 1293 439 427 439 427 439 427 439 1293 439 1294 438 1294 438 1293 439 427 439 428 438 428 438 428 438 429 436 429 437 429 437 453 412 453 413 453 412 1320 412 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1322 410 455 411 455 411 456 410 456 410 455 410 456 410 456 409 481 384 458 408 482 383 482 384 483 358 508 358 1374 358 1374 358 508 357 509 357 509 357 510 355 535 330 536 330 536 330 536 329 536 330 536 330 1403 329 1430 301 564 302 564 302 564 301 591 274 566 301 592 273 619 247 619 247 620 245 647 219 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 503 365 500 364 501 366 499 365 500 364 502 25049 3535 1660 504 1228 503 390 474 391 473 393 471 1261 469 397 468 397 469 397 469 397 469 1264 468 398 468 1264 468 1264 468 398 468 1265 467 1265 467 1265 467 1265 467 1265 467 399 467 399 467 1266 466 399 467 400 466 400 466 400 466 423 443 423 443 401 465 423 442 424 442 424 442 1290 442 424 442 1290 442 424 442 424 441 424 442 1290 442 1291 441 425 441 424 442 425 441 425 441 1291 441 425 440 425 441 425 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 426 440 1292 440 1292 440 1292 440 426 440 426 440 1292 440 1293 439 1293 439 35480 3503 1696 467 1264 468 398 468 398 467 398 468 1265 467 398 467 399 467 399 466 399 467 1265 467 399 467 1266 466 1267 465 400 466 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 442 1290 442 424 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 425 441 425 441 1291 441 425 441 425 441 425 441 425 440 1292 440 425 441 425 440 426 440 426 440 426 440 426 440 426 440 426 440 426 440 426 439 427 439 426 440 426 440 1293 439 427 439 427 439 427 438 427 439 428 438 1294 438 428 437 428 438 1295 437 1319 413 453 413 35480 3503 1696 468 1265 467 398 468 398 468 398 468 1265 467 398 468 399 466 399 467 399 467 1266 466 399 466 1267 465 1290 442 401 465 1290 442 1290 442 1290 442 1290 442 1290 442 424 442 424 441 1291 441 424 442 424 442 424 442 424 442 424 441 424 442 425 441 424 442 424 441 425 441 425 441 425 440 425 441 425 441 425 441 425 441 425 441 425 441 1292 440 426 440 426 440 1292 440 426 440 426 440 1293 439 426 440 426 440 1293 439 1293 439 1293 439 427 439 1294 438 427 438 427 439 427 438 428 438 428 438 428 438 428 438 453 413 429 437 453 413 1319 413 1320 412 1320 412 1320 412 454 412 1320 412 454 412 1321 411 1321 411 1321 411 1321 411 1321 411 455 410 455 411 455 411 455 410 456 410 456 410 456 410 481 384 481 385 482 383 482 383 483 358 507 359 1374 358 1374 358 508 358 508 358 508 358 509 357 509 357 535 331 535 331 535 330 535 330 536 330 1403 329 1403 329 563 302 564 301 564 302 564 301 565 301 591 274 619 246 593 273 620 245 620 245 621 245 673 189 +# Model: Mitsubishi SRK63HE. +# +name: Off +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3232 1526 463 334 462 1127 464 334 460 334 461 1128 463 336 459 1128 464 359 436 359 436 1133 458 1156 434 1157 434 362 433 1159 432 363 432 1159 432 1159 432 1159 432 363 433 363 432 363 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 363 432 364 432 1160 431 363 432 363 432 1160 432 363 432 364 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 432 364 432 364 431 364 431 1160 432 364 432 364 431 364 431 1160 431 1160 431 1160 431 364 431 1161 430 1161 430 1160 431 1161 430 364 432 364 431 365 431 1161 430 364 431 365 431 364 431 365 430 365 431 1161 430 1161 430 1161 430 365 430 1161 430 365 430 365 430 1161 430 365 430 365 431 365 430 1161 430 365 430 1161 430 1161 430 +# +name: Dh +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3229 1528 461 335 461 1128 463 334 461 335 460 1129 463 337 458 1129 463 359 436 360 434 1156 434 1157 433 1159 432 364 431 1161 430 364 431 1161 430 1161 430 1160 431 364 431 364 431 365 430 365 430 1161 430 1161 430 365 430 1161 430 1161 431 365 430 365 431 1161 430 365 430 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 1161 430 1161 430 1161 430 1161 430 1161 431 1161 430 365 430 1161 430 1161 430 1161 431 365 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 1162 429 1162 429 1162 430 365 430 1162 429 1162 429 1162 429 1162 429 366 430 366 429 366 430 1162 429 366 429 366 430 366 429 366 429 1162 429 366 429 1163 428 366 429 1162 429 366 429 366 429 1162 429 366 430 1162 429 366 429 1163 429 366 430 1163 428 1163 428 367 428 +# +name: Cool_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3230 1526 463 335 460 1128 464 333 461 335 460 1128 463 337 458 1128 463 359 436 359 436 1155 435 1157 433 1158 432 363 432 1160 431 364 431 1161 430 1160 431 1160 431 364 431 364 431 364 431 364 431 1161 430 1161 430 364 431 1161 430 1160 431 365 430 364 431 1161 431 364 431 364 431 1161 430 365 430 365 431 1161 430 1161 431 364 431 1161 430 1161 430 1161 430 1161 431 1161 430 1161 431 365 430 1161 431 1161 430 1162 430 365 430 365 430 365 431 365 430 1161 430 365 430 365 430 365 431 1161 430 1162 430 1162 429 365 430 1162 429 1162 429 1162 429 1162 429 366 429 366 430 366 430 1162 429 366 430 366 429 366 429 366 430 366 429 1162 429 1163 429 366 429 366 429 1163 428 1163 428 1163 429 1163 428 366 429 366 430 1163 428 1163 428 367 428 367 429 366 429 +# +name: Cool_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3231 1526 463 334 461 1127 465 333 461 334 460 1128 463 336 459 1129 463 359 436 359 436 1133 458 1156 434 1157 433 362 433 1159 432 363 432 1159 432 1160 431 1159 433 363 432 363 432 363 432 363 432 1159 433 1159 432 363 432 1159 432 1160 432 363 432 363 432 1159 432 363 432 363 433 1159 432 364 432 363 432 1160 431 1160 432 363 432 1160 431 1160 431 1160 431 1160 432 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 364 431 364 431 364 432 364 431 1160 432 364 431 364 431 364 432 1160 431 1161 430 1161 430 364 431 1161 431 1161 430 1161 430 1161 430 364 432 364 431 364 431 1161 430 365 430 365 430 365 430 365 431 365 430 1161 430 1161 431 365 430 1161 430 365 430 365 431 1161 430 1161 430 365 431 365 430 1162 430 365 431 1161 430 1162 429 365 430 +# +name: Heat_hi +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3230 1526 463 334 461 1128 464 333 461 334 461 1128 463 336 459 1129 463 359 436 359 436 1155 435 1156 434 1158 433 363 432 1159 432 363 432 1159 432 1160 431 1160 431 363 433 363 432 363 432 363 432 1160 432 1160 431 364 431 1160 431 1160 432 364 431 364 431 1160 431 364 431 364 432 1160 431 364 431 364 431 1160 431 1160 431 364 432 1160 431 1160 431 1160 431 1160 431 1160 431 1160 431 364 431 1160 432 1160 431 1160 431 364 432 364 431 364 431 364 431 1161 430 364 431 364 432 364 431 1161 430 1161 430 1161 430 365 430 1161 431 1161 430 1161 430 1161 430 365 430 365 430 365 430 1161 430 365 431 365 431 365 430 365 431 1161 430 1161 430 365 430 365 430 365 430 1162 430 365 430 365 430 365 431 365 430 1162 429 1162 430 1162 429 366 429 1162 429 1162 429 +# +name: Heat_lo +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3234 1525 463 333 462 1127 465 332 462 333 436 1153 518 307 488 1073 518 307 488 308 434 1131 459 1155 435 1156 434 362 433 1159 432 363 432 1159 432 1159 432 1159 433 363 432 363 432 363 433 363 432 1159 433 1159 432 363 432 1159 433 1159 432 363 432 363 432 1159 432 363 433 363 432 1159 432 363 432 363 432 1159 432 1159 432 363 432 1160 432 1160 431 1160 432 1160 431 1160 431 1160 431 364 431 1160 431 1160 431 1160 431 364 431 364 431 364 431 364 431 1160 431 364 431 364 431 364 431 1160 432 1160 431 1160 432 364 431 1160 431 1160 431 1161 431 1161 430 364 431 364 431 364 431 1160 432 364 431 364 431 364 432 364 431 1161 431 1161 431 364 431 364 431 1161 430 364 432 364 431 1161 430 365 431 365 431 1161 430 1161 430 365 431 1161 430 1161 430 365 430 From 05ddeb3066190f2397ee0806a46ab40d035e060b Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 19:28:26 +0000 Subject: [PATCH 092/231] Alpha sort asset packs setting --- .../scenes/xtreme_settings_scene_start.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 53d679efb..f3e41ba70 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -145,9 +145,20 @@ void xtreme_settings_scene_start_on_enter(void* context) { if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); - asset_packs_push_back(app->asset_packs, copy); - if(strcmp(name, xtreme_settings->asset_pack) == 0) - current_pack = asset_packs_size(app->asset_packs); + uint idx; + for(idx = 0; idx < asset_packs_size(app->asset_packs); idx++) { + if(strcasecmp(copy, *asset_packs_get(app->asset_packs, idx)) < 0) { + break; + } + } + asset_packs_push_at(app->asset_packs, idx, copy); + if(current_pack != 0) { + if(idx <= current_pack) + current_pack++; + } else { + if(strcmp(copy, xtreme_settings->asset_pack) == 0) + current_pack = idx + 1; + } } } } while(false); From d6dcf0efa26f77f99d9aac9cba00ad6636e1ebc4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 20:51:07 +0000 Subject: [PATCH 093/231] Case insensitive file browser list --- applications/main/archive/helpers/archive_files.h | 2 +- applications/services/gui/modules/file_browser.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index db624f5b5..881d1f134 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -88,7 +88,7 @@ static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { return -1; } - return furi_string_cmp(a->path, b->path); + return furi_string_cmpi(a->path, b->path); } #define M_OPL_ArchiveFile_t() \ diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 9d65df46b..5d87fcb45 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -84,7 +84,7 @@ static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { return -1; } - return furi_string_cmp(a->path, b->path); + return furi_string_cmpi(a->path, b->path); } #define M_OPL_BrowserItem_t() \ From e393285064aaa6d2264e25e29f6c1db1f9f76080 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 6 Feb 2023 23:05:34 +0000 Subject: [PATCH 094/231] Fix file browser long list jump bug --- .../gui/modules/file_browser_worker.c | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4386fdfd0..d4e83aefb 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -203,55 +203,57 @@ static bool uint32_t items_cnt = 0; + bool ret = false; do { if(!storage_dir_open(directory, furi_string_get_cstr(path))) { break; } - items_cnt = 0; - while(items_cnt < offset) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - items_cnt++; - } - } else { - break; - } - } - if(items_cnt != offset) { - break; - } + // items_cnt = 0; + // while(items_cnt < offset) { + // if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + // break; + // } + // if(storage_file_get_error(directory) == FSE_OK) { + // furi_string_set(name_str, name_temp); + // if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + // items_cnt++; + // } + // } else { + // break; + // } + // } + // if(items_cnt != offset) { + // break; + // } + // FLIPPER DEVS MOMENT + // this used to load the file list in chunks, and then sort it... + // so while scrolling, it loads more files and sorts them... + // chances are, the new files are higner in the sorted list... + // so the files keep shifting around while scrolling... + // now this does something intelligent and loads all in one go. + // might take a few milliseconds longer, but atleast it works :kekw: + UNUSED(offset); + UNUSED(count); if(browser->list_load_cb) { - browser->list_load_cb(browser->cb_ctx, offset); + browser->list_load_cb(browser->cb_ctx, 0); } - - items_cnt = 0; - while(items_cnt < count) { - if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - break; - } - if(storage_file_get_error(directory) == FSE_OK) { - furi_string_set(name_str, name_temp); - if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); - if(browser->list_item_cb) { - browser->list_item_cb( - browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); - } - items_cnt++; + while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && storage_file_get_error(directory) == FSE_OK) { + furi_string_set(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); + if(browser->list_item_cb) { + browser->list_item_cb( + browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); } - } else { - break; + items_cnt++; } } if(browser->list_item_cb) { browser->list_item_cb(browser->cb_ctx, NULL, false, true); } + ret = true; } while(0); furi_string_free(name_str); @@ -261,7 +263,7 @@ static bool furi_record_close(RECORD_STORAGE); - return (items_cnt == count); + return ret; } static int32_t browser_worker(void* context) { From e98ad6fc2c2bcce58b3b5e726286fb56b2abeef0 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 00:24:19 +0000 Subject: [PATCH 095/231] Fix file browser sorting for good --- applications/main/archive/helpers/archive_files.h | 3 +++ applications/services/gui/modules/file_browser.c | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 881d1f134..27f6353a7 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -87,6 +87,9 @@ static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { return -1; } + if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { + return 1; + } return furi_string_cmpi(a->path, b->path); } diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 5d87fcb45..eecea0dbd 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -78,11 +78,18 @@ static void BrowserItem_t_clear(BrowserItem_t* obj) { static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { // Back indicator comes before everything, then folders, then all other files. - if((a->type == BrowserItemTypeBack) || - (a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder && - b->type != BrowserItemTypeBack)) { + if(a->type == BrowserItemTypeBack) { return -1; } + if(b->type == BrowserItemTypeBack) { + return 1; + } + if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { + return -1; + } + if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { + return 1; + } return furi_string_cmpi(a->path, b->path); } From edd3171d6c2a8895a7f1f8ac4375891bf3d45d56 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 00:50:38 +0000 Subject: [PATCH 096/231] Add xfw setting for sort dirs before files --- applications/main/archive/helpers/archive_files.h | 13 ++++++++----- applications/services/gui/modules/file_browser.c | 13 ++++++++----- .../scenes/xtreme_settings_scene_start.c | 13 +++++++++++++ .../settings/xtreme_settings/xtreme_settings.h | 1 + 4 files changed, 30 insertions(+), 10 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 27f6353a7..a9c33c9f6 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -6,6 +6,7 @@ #include #include #include "toolbox/path.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define FAP_MANIFEST_MAX_ICON_SIZE 32 @@ -84,11 +85,13 @@ static void ArchiveFile_t_clear(ArchiveFile_t* obj) { } static int ArchiveFile_t_cmp(const ArchiveFile_t* a, const ArchiveFile_t* b) { - if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { - return -1; - } - if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { - return 1; + if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(a->type == ArchiveFileTypeFolder && b->type != ArchiveFileTypeFolder) { + return -1; + } + if(a->type != ArchiveFileTypeFolder && b->type == ArchiveFileTypeFolder) { + return 1; + } } return furi_string_cmpi(a->path, b->path); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index eecea0dbd..c6971c82a 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -11,6 +11,7 @@ #include #include #include "toolbox/path.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define LIST_ITEMS 5u #define MAX_LEN_PX 110 @@ -84,11 +85,13 @@ static int BrowserItem_t_cmp(const BrowserItem_t* a, const BrowserItem_t* b) { if(b->type == BrowserItemTypeBack) { return 1; } - if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { - return -1; - } - if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { - return 1; + if(!XTREME_SETTINGS()->sort_ignore_dirs) { + if(a->type == BrowserItemTypeFolder && b->type != BrowserItemTypeFolder) { + return -1; + } + if(a->type != BrowserItemTypeFolder && b->type == BrowserItemTypeFolder) { + return 1; + } } return furi_string_cmpi(a->path, b->path); diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index f3e41ba70..d6532af2c 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -87,6 +87,14 @@ static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item app->settings_changed = true; } +static void xtreme_settings_scene_start_sort_folders_before_changed(VariableItem* item) { + XtremeSettingsApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->sort_ignore_dirs = !value; + app->settings_changed = true; +} + static void xtreme_settings_scene_start_xp_level_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); app->dolphin_level = variable_item_get_current_value_index(item) + 1; @@ -219,6 +227,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, battery_style_names[value_index]); + item = variable_item_list_add( + var_item_list, "Sort folders before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); + variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); + char level_str[4]; snprintf(level_str, 4, "%i", app->dolphin_level); item = variable_item_list_add( diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index 0a19ca643..ee0ec5583 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -22,6 +22,7 @@ typedef struct { char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; + bool sort_ignore_dirs; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); From d3cf757eade9c4e16ce392f745851db4759278d1 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:08:05 +0000 Subject: [PATCH 097/231] Fix typos --- applications/services/gui/modules/file_browser_worker.c | 2 +- .../xtreme_settings/scenes/xtreme_settings_scene_start.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index d4e83aefb..6cbf4b3e8 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -230,7 +230,7 @@ static bool // FLIPPER DEVS MOMENT // this used to load the file list in chunks, and then sort it... // so while scrolling, it loads more files and sorts them... - // chances are, the new files are higner in the sorted list... + // chances are, the new files are higher in the sorted list... // so the files keep shifting around while scrolling... // now this does something intelligent and loads all in one go. // might take a few milliseconds longer, but atleast it works :kekw: diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index d6532af2c..c0f2262fd 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -228,7 +228,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, battery_style_names[value_index]); item = variable_item_list_add( - var_item_list, "Sort folders before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + var_item_list, "Sort dirs before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); From 3bd3b7f122b1ae798a5c713f3777eb3a66ed8985 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:08:43 +0000 Subject: [PATCH 098/231] Format --- .../services/gui/modules/file_browser_worker.c | 3 ++- .../scenes/xtreme_settings_scene_start.c | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 6cbf4b3e8..0ae0907de 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -239,7 +239,8 @@ static bool if(browser->list_load_cb) { browser->list_load_cb(browser->cb_ctx, 0); } - while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && storage_file_get_error(directory) == FSE_OK) { + while(storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX) && + storage_file_get_error(directory) == FSE_OK) { furi_string_set(name_str, name_temp); if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index c0f2262fd..03d75def7 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -161,11 +161,9 @@ void xtreme_settings_scene_start_on_enter(void* context) { } asset_packs_push_at(app->asset_packs, idx, copy); if(current_pack != 0) { - if(idx <= current_pack) - current_pack++; + if(idx <= current_pack) current_pack++; } else { - if(strcmp(copy, xtreme_settings->asset_pack) == 0) - current_pack = idx + 1; + if(strcmp(copy, xtreme_settings->asset_pack) == 0) current_pack = idx + 1; } } } @@ -228,7 +226,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, battery_style_names[value_index]); item = variable_item_list_add( - var_item_list, "Sort dirs before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); + var_item_list, + "Sort dirs before", + 2, + xtreme_settings_scene_start_sort_folders_before_changed, + app); variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); From 497f369d16076c39a98b8fceebf0084642ca06f4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 7 Feb 2023 01:59:40 +0000 Subject: [PATCH 099/231] Fix asset pack setting sorting --- .../xtreme_settings/scenes/xtreme_settings_scene_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 03d75def7..3323cf1df 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -161,7 +161,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { } asset_packs_push_at(app->asset_packs, idx, copy); if(current_pack != 0) { - if(idx <= current_pack) current_pack++; + if(idx < current_pack) current_pack++; } else { if(strcmp(copy, xtreme_settings->asset_pack) == 0) current_pack = idx + 1; } From 1ff5843ee690e3e6c0d74a5fa569de3ffcf2b8d1 Mon Sep 17 00:00:00 2001 From: Round-Pi <9893098+Round-Pi@users.noreply.github.com> Date: Mon, 6 Feb 2023 22:05:52 -0500 Subject: [PATCH 100/231] battery info temperature shown in C or F based on settings (#2360) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * battery Info temperature displays C or F * PowerSettings: add locale module to dependencies Co-authored-by: あく --- .../settings/power_settings_app/application.fam | 1 + .../settings/power_settings_app/views/battery_info.c | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/applications/settings/power_settings_app/application.fam b/applications/settings/power_settings_app/application.fam index a5b1966b2..6f3589e1a 100644 --- a/applications/settings/power_settings_app/application.fam +++ b/applications/settings/power_settings_app/application.fam @@ -6,6 +6,7 @@ App( requires=[ "gui", "power", + "locale", ], flags=["InsomniaSafe"], stack_size=1 * 1024, diff --git a/applications/settings/power_settings_app/views/battery_info.c b/applications/settings/power_settings_app/views/battery_info.c index 5353a2e2a..d29769d21 100644 --- a/applications/settings/power_settings_app/views/battery_info.c +++ b/applications/settings/power_settings_app/views/battery_info.c @@ -2,6 +2,7 @@ #include #include #include +#include #define LOW_CHARGE_THRESHOLD 10 #define HIGH_DRAIN_CURRENT_THRESHOLD 100 @@ -101,7 +102,15 @@ static void battery_info_draw_callback(Canvas* canvas, void* context) { char health[10]; snprintf(batt_level, sizeof(batt_level), "%lu%%", (uint32_t)model->charge); - snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + if(locale_get_measurement_unit() == LocaleMeasurementUnitsMetric) { + snprintf(temperature, sizeof(temperature), "%lu C", (uint32_t)model->gauge_temperature); + } else { + snprintf( + temperature, + sizeof(temperature), + "%lu F", + (uint32_t)locale_celsius_to_fahrenheit(model->gauge_temperature)); + } snprintf( voltage, sizeof(voltage), From d035872cf657eccc7a488c61e4cbf1850ab0bf7c Mon Sep 17 00:00:00 2001 From: Krzysztof Zdulski Date: Tue, 7 Feb 2023 04:21:25 +0100 Subject: [PATCH 101/231] nfc: Add mifare classic value block commands (#2317) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: gornekich Co-authored-by: あく --- firmware/targets/f7/api_symbols.csv | 11 +- lib/nfc/protocols/mifare_classic.c | 555 +++++++++++++++++++++++----- lib/nfc/protocols/mifare_classic.h | 37 ++ lib/nfc/protocols/nfc_util.c | 25 +- lib/nfc/protocols/nfc_util.h | 4 +- 5 files changed, 541 insertions(+), 91 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 02f476cc0..14036ef1a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1863,8 +1863,10 @@ Function,+,menu_reset,void,Menu* Function,+,menu_set_selected_item,void,"Menu*, uint32_t" Function,-,mf_classic_auth_attempt,_Bool,"FuriHalNfcTxRxContext*, MfClassicAuthContext*, uint64_t" Function,-,mf_classic_auth_init_context,void,"MfClassicAuthContext*, uint8_t" +Function,-,mf_classic_auth_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" Function,-,mf_classic_authenticate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey" Function,-,mf_classic_authenticate_skip_activate,_Bool,"FuriHalNfcTxRxContext*, uint8_t, uint64_t, MfClassicKey, _Bool, uint32_t" +Function,-,mf_classic_block_to_value,_Bool,"const uint8_t*, int32_t*, uint8_t*" Function,-,mf_classic_check_card_type,_Bool,"uint8_t, uint8_t, uint8_t" Function,-,mf_classic_dict_add_key,_Bool,"MfClassicDict*, uint8_t*" Function,-,mf_classic_dict_add_key_str,_Bool,"MfClassicDict*, FuriString*" @@ -1891,6 +1893,7 @@ Function,-,mf_classic_get_sector_trailer_by_sector,MfClassicSectorTrailer*,"MfCl Function,-,mf_classic_get_total_block_num,uint16_t,MfClassicType Function,-,mf_classic_get_total_sectors_num,uint8_t,MfClassicType Function,-,mf_classic_get_type_str,const char*,MfClassicType +Function,-,mf_classic_halt,void,"FuriHalNfcTxRxContext*, Crypto1*" Function,-,mf_classic_is_allowed_access_data_block,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" Function,-,mf_classic_is_allowed_access_sector_trailer,_Bool,"MfClassicData*, uint8_t, MfClassicKey, MfClassicAction" Function,-,mf_classic_is_block_read,_Bool,"MfClassicData*, uint8_t" @@ -1899,6 +1902,8 @@ Function,-,mf_classic_is_key_found,_Bool,"MfClassicData*, uint8_t, MfClassicKey" Function,-,mf_classic_is_sector_data_read,_Bool,"MfClassicData*, uint8_t" Function,-,mf_classic_is_sector_read,_Bool,"MfClassicData*, uint8_t" Function,-,mf_classic_is_sector_trailer,_Bool,uint8_t +Function,-,mf_classic_is_value_block,_Bool,"MfClassicData*, uint8_t" +Function,-,mf_classic_read_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" Function,-,mf_classic_read_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicReader*, MfClassicData*" Function,-,mf_classic_read_sector,void,"FuriHalNfcTxRxContext*, MfClassicData*, uint8_t" Function,-,mf_classic_reader_add_sector,void,"MfClassicReader*, uint8_t, uint64_t, uint64_t" @@ -1906,8 +1911,12 @@ Function,-,mf_classic_set_block_read,void,"MfClassicData*, uint8_t, MfClassicBlo Function,-,mf_classic_set_key_found,void,"MfClassicData*, uint8_t, MfClassicKey, uint64_t" Function,-,mf_classic_set_key_not_found,void,"MfClassicData*, uint8_t, MfClassicKey" Function,-,mf_classic_set_sector_data_not_read,void,MfClassicData* +Function,-,mf_classic_transfer,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t" Function,-,mf_classic_update_card,uint8_t,"FuriHalNfcTxRxContext*, MfClassicData*" -Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t" +Function,-,mf_classic_value_cmd,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, uint8_t, int32_t" +Function,-,mf_classic_value_cmd_full,_Bool,"FuriHalNfcTxRxContext*, MfClassicBlock*, uint8_t, MfClassicKey, uint64_t, int32_t" +Function,-,mf_classic_value_to_block,void,"int32_t, uint8_t, uint8_t*" +Function,-,mf_classic_write_block,_Bool,"FuriHalNfcTxRxContext*, Crypto1*, uint8_t, MfClassicBlock*" Function,-,mf_classic_write_sector,_Bool,"FuriHalNfcTxRxContext*, MfClassicData*, MfClassicData*, uint8_t" Function,-,mf_df_cat_application,void,"MifareDesfireApplication*, FuriString*" Function,-,mf_df_cat_application_info,void,"MifareDesfireApplication*, FuriString*" diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 5887ab4c1..28667f09b 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -7,10 +7,17 @@ #define TAG "MfClassic" -#define MF_CLASSIC_AUTH_KEY_A_CMD (0x60U) -#define MF_CLASSIC_AUTH_KEY_B_CMD (0x61U) -#define MF_CLASSIC_READ_BLOCK_CMD (0x30) -#define MF_CLASSIC_WRITE_BLOCK_CMD (0xA0) +#define MF_CLASSIC_ACK_CMD 0xAU +#define MF_CLASSIC_NACK_BUF_VALID_CMD 0x0U +#define MF_CLASSIC_NACK_BUF_INVALID_CMD 0x4U +#define MF_CLASSIC_AUTH_KEY_A_CMD 0x60U +#define MF_CLASSIC_AUTH_KEY_B_CMD 0x61U +#define MF_CLASSIC_READ_BLOCK_CMD 0x30U +#define MF_CLASSIC_WRITE_BLOCK_CMD 0xA0U +#define MF_CLASSIC_TRANSFER_CMD 0xB0U +#define MF_CLASSIC_DECREMENT_CMD 0xC0U +#define MF_CLASSIC_INCREMENT_CMD 0xC1U +#define MF_CLASSIC_RESTORE_CMD 0xC2U const char* mf_classic_get_type_str(MfClassicType type) { if(type == MfClassicTypeMini) { @@ -217,8 +224,8 @@ void mf_classic_get_read_sectors_and_keys( uint8_t first_block = mf_classic_get_first_block_num_of_sector(i); uint8_t total_blocks_in_sec = mf_classic_get_blocks_num_in_sector(i); bool blocks_read = true; - for(size_t i = first_block; i < first_block + total_blocks_in_sec; i++) { - blocks_read = mf_classic_is_block_read(data, i); + for(size_t j = first_block; j < first_block + total_blocks_in_sec; j++) { + blocks_read = mf_classic_is_block_read(data, j); if(!blocks_read) break; } if(blocks_read) { @@ -353,6 +360,16 @@ static bool mf_classic_is_allowed_access( } } +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num) { + // Check if key A can write, if it can, it's transport configuration, not data block + return !mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyA, MfClassicActionDataWrite) && + (mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyB, MfClassicActionDataInc) || + mf_classic_is_allowed_access_data_block( + data, block_num, MfClassicKeyB, MfClassicActionDataDec)); +} + bool mf_classic_check_card_type(uint8_t ATQA0, uint8_t ATQA1, uint8_t SAK) { UNUSED(ATQA1); if((ATQA0 == 0x44 || ATQA0 == 0x04) && (SAK == 0x08 || SAK == 0x88 || SAK == 0x09)) { @@ -401,6 +418,36 @@ void mf_classic_reader_add_sector( } } +bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr) { + uint32_t v = *(uint32_t*)&block[0]; + uint32_t v_inv = *(uint32_t*)&block[4]; + uint32_t v1 = *(uint32_t*)&block[8]; + + bool val_checks = + ((v == v1) && (v == ~v_inv) && (block[12] == (~block[13] & 0xFF)) && + (block[14] == (~block[15] & 0xFF)) && (block[12] == block[14])); + if(value) { + *value = (int32_t)v; + } + if(addr) { + *addr = block[12]; + } + return val_checks; +} + +void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { + uint32_t v_inv = ~((uint32_t)value); + + memcpy(block, &value, 4); + memcpy(block + 4, &v_inv, 4); + memcpy(block + 8, &value, 4); + + block[12] = addr; + block[13] = ~addr & 0xFF; + block[14] = addr; + block[15] = ~addr & 0xFF; +} + void mf_classic_auth_init_context(MfClassicAuthContext* auth_ctx, uint8_t sector) { furi_assert(auth_ctx); auth_ctx->sector = sector; @@ -553,8 +600,9 @@ bool mf_classic_read_block( uint8_t plain_cmd[4] = {MF_CLASSIC_READ_BLOCK_CMD, block_num, 0x00, 0x00}; nfca_append_crc16(plain_cmd, 2); - crypto1_encrypt(crypto, NULL, plain_cmd, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 4 * 9; + crypto1_encrypt( + crypto, NULL, plain_cmd, sizeof(plain_cmd) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = sizeof(plain_cmd) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { @@ -779,6 +827,9 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ bool is_encrypted = false; uint8_t plain_data[MF_CLASSIC_MAX_DATA_SIZE]; MfClassicKey access_key = MfClassicKeyA; + // Used for decrement and increment - copy to block on transfer + uint8_t transfer_buf[MF_CLASSIC_BLOCK_SIZE] = {}; + bool transfer_buf_valid = false; // Read command while(!command_processed) { //-V654 @@ -797,18 +848,25 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); } - if(plain_data[0] == 0x50 && plain_data[1] == 0x00) { + // After increment, decrement or restore the only allowed command is transfer + uint8_t cmd = plain_data[0]; + if(transfer_buf_valid && cmd != MF_CLASSIC_TRANSFER_CMD) { + break; + } + + if(cmd == 0x50 && plain_data[1] == 0x00) { FURI_LOG_T(TAG, "Halt received"); furi_hal_nfc_listen_sleep(); command_processed = true; break; - } else if(plain_data[0] == 0x60 || plain_data[0] == 0x61) { + } + if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD || cmd == MF_CLASSIC_AUTH_KEY_B_CMD) { uint8_t block = plain_data[1]; uint64_t key = 0; uint8_t sector_trailer_block = mf_classic_get_sector_trailer_num_by_block(block); MfClassicSectorTrailer* sector_trailer = (MfClassicSectorTrailer*)emulator->data.block[sector_trailer_block].value; - if(plain_data[0] == 0x60) { + if(cmd == MF_CLASSIC_AUTH_KEY_A_CMD) { key = nfc_util_bytes2num(sector_trailer->key_a, 6); access_key = MfClassicKeyA; } else { @@ -826,9 +884,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ crypto1_word(&emulator->crypto, emulator->cuid ^ nonce, 0); memcpy(tx_rx->tx_data, nt, sizeof(nt)); tx_rx->tx_parity[0] = 0; - for(size_t i = 0; i < sizeof(nt); i++) { - tx_rx->tx_parity[0] |= nfc_util_odd_parity8(nt[i]) << (7 - i); - } + nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(nt)); tx_rx->tx_bits = sizeof(nt) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; } else { @@ -849,7 +905,7 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } if(tx_rx->rx_bits != 64) { - FURI_LOG_W(TAG, "Incorrect nr + ar"); + FURI_LOG_W(TAG, "Incorrect nr + ar length: %d", tx_rx->rx_bits); command_processed = true; break; } @@ -857,16 +913,6 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ uint32_t nr = nfc_util_bytes2num(tx_rx->rx_data, 4); uint32_t ar = nfc_util_bytes2num(&tx_rx->rx_data[4], 4); - FURI_LOG_D( - TAG, - "%08lx key%c block %d nt/nr/ar: %08lx %08lx %08lx", - emulator->cuid, - access_key == MfClassicKeyA ? 'A' : 'B', - sector_trailer_block, - nonce, - nr, - ar); - crypto1_word(&emulator->crypto, nr, 1); uint32_t cardRr = ar ^ crypto1_word(&emulator->crypto, 0, 0); if(cardRr != prng_successor(nonce, 64)) { @@ -877,22 +923,30 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ } uint32_t ans = prng_successor(nonce, 96); - uint8_t responce[4] = {}; - nfc_util_num2bytes(ans, 4, responce); + uint8_t response[4] = {}; + nfc_util_num2bytes(ans, 4, response); crypto1_encrypt( &emulator->crypto, NULL, - responce, - sizeof(responce) * 8, + response, + sizeof(response) * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = sizeof(responce) * 8; + tx_rx->tx_bits = sizeof(response) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; is_encrypted = true; - } else if(is_encrypted && plain_data[0] == 0x30) { + continue; + } + + if(!is_encrypted) { + FURI_LOG_T(TAG, "Invalid command before auth session established: %02X", cmd); + break; + } + + if(cmd == MF_CLASSIC_READ_BLOCK_CMD) { uint8_t block = plain_data[1]; - uint8_t block_data[18] = {}; + uint8_t block_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { if(!mf_classic_is_allowed_access( @@ -927,24 +981,24 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ sizeof(block_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 18 * 8; + tx_rx->tx_bits = (MF_CLASSIC_BLOCK_SIZE + 2) * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; - } else if(is_encrypted && plain_data[0] == 0xA0) { + } else if(cmd == MF_CLASSIC_WRITE_BLOCK_CMD) { uint8_t block = plain_data[1]; if(block > mf_classic_get_total_block_num(emulator->data.type)) { break; } // Send ACK - uint8_t ack = 0x0A; + uint8_t ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; - if(tx_rx->rx_bits != 18 * 8) break; + if(tx_rx->rx_bits != (MF_CLASSIC_BLOCK_SIZE + 2) * 8) break; crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); - uint8_t block_data[16] = {}; + uint8_t block_data[MF_CLASSIC_BLOCK_SIZE] = {}; memcpy(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE); if(mf_classic_is_sector_trailer(block)) { if(mf_classic_is_allowed_access( @@ -963,6 +1017,8 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ if(mf_classic_is_allowed_access( emulator, block, access_key, MfClassicActionDataWrite)) { memcpy(block_data, plain_data, MF_CLASSIC_BLOCK_SIZE); + } else { + break; } } if(memcmp(block_data, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != 0) { @@ -970,19 +1026,83 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ emulator->data_changed = true; } // Send ACK - ack = 0x0A; + ack = MF_CLASSIC_ACK_CMD; + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + } else if( + cmd == MF_CLASSIC_DECREMENT_CMD || cmd == MF_CLASSIC_INCREMENT_CMD || + cmd == MF_CLASSIC_RESTORE_CMD) { + uint8_t block = plain_data[1]; + + if(block > mf_classic_get_total_block_num(emulator->data.type)) { + break; + } + + MfClassicAction action = MfClassicActionDataDec; + if(cmd == MF_CLASSIC_INCREMENT_CMD) { + action = MfClassicActionDataInc; + } + if(!mf_classic_is_allowed_access(emulator, block, access_key, action)) { + break; + } + + int32_t prev_value; + uint8_t addr; + if(!mf_classic_block_to_value(emulator->data.block[block].value, &prev_value, &addr)) { + break; + } + + // Send ACK + uint8_t ack = MF_CLASSIC_ACK_CMD; + crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; + tx_rx->tx_bits = 4; + + if(!furi_hal_nfc_tx_rx(tx_rx, 300)) break; + if(tx_rx->rx_bits != (sizeof(int32_t) + 2) * 8) break; + + crypto1_decrypt(&emulator->crypto, tx_rx->rx_data, tx_rx->rx_bits, plain_data); + int32_t value = *(int32_t*)&plain_data[0]; + if(value < 0) { + value = -value; + } + if(cmd == MF_CLASSIC_DECREMENT_CMD) { + value = -value; + } else if(cmd == MF_CLASSIC_RESTORE_CMD) { + value = 0; + } + + mf_classic_value_to_block(prev_value + value, addr, transfer_buf); + transfer_buf_valid = true; + // Commands do not ACK + tx_rx->tx_bits = 0; + } else if(cmd == MF_CLASSIC_TRANSFER_CMD) { + uint8_t block = plain_data[1]; + if(!mf_classic_is_allowed_access(emulator, block, access_key, MfClassicActionDataDec)) { + break; + } + if(memcmp(transfer_buf, emulator->data.block[block].value, MF_CLASSIC_BLOCK_SIZE) != + 0) { + memcpy(emulator->data.block[block].value, transfer_buf, MF_CLASSIC_BLOCK_SIZE); + emulator->data_changed = true; + } + transfer_buf_valid = false; + + uint8_t ack = MF_CLASSIC_ACK_CMD; crypto1_encrypt(&emulator->crypto, NULL, &ack, 4, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_rx_type = FuriHalNfcTxRxTransparent; tx_rx->tx_bits = 4; } else { - // Unknown command + FURI_LOG_T(TAG, "Unknown command: %02X", cmd); break; } } if(!command_processed) { // Send NACK - uint8_t nack = 0x04; + uint8_t nack = transfer_buf_valid ? MF_CLASSIC_NACK_BUF_VALID_CMD : + MF_CLASSIC_NACK_BUF_INVALID_CMD; if(is_encrypted) { crypto1_encrypt(&emulator->crypto, NULL, &nack, 4, tx_rx->tx_data, tx_rx->tx_parity); } else { @@ -996,37 +1116,50 @@ bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_ return true; } +void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto) { + furi_assert(tx_rx); + + uint8_t plain_data[4] = {0x50, 0x00, 0x00, 0x00}; + + nfca_append_crc16(plain_data, 2); + if(crypto) { + crypto1_encrypt( + crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); + } else { + memcpy(tx_rx->tx_data, plain_data, sizeof(plain_data)); + nfc_util_odd_parity(tx_rx->tx_data, tx_rx->tx_parity, sizeof(plain_data)); + } + + tx_rx->tx_bits = sizeof(plain_data) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + furi_hal_nfc_tx_rx(tx_rx, 50); +} + bool mf_classic_write_block( FuriHalNfcTxRxContext* tx_rx, - MfClassicBlock* src_block, + Crypto1* crypto, uint8_t block_num, - MfClassicKey key_type, - uint64_t key) { + MfClassicBlock* src_block) { furi_assert(tx_rx); + furi_assert(crypto); furi_assert(src_block); - Crypto1 crypto = {}; - uint8_t plain_data[18] = {}; - uint8_t resp = 0; bool write_success = false; + uint8_t plain_data[MF_CLASSIC_BLOCK_SIZE + 2] = {}; + uint8_t resp; do { - furi_hal_nfc_sleep(); - if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { - FURI_LOG_D(TAG, "Auth fail"); - break; - } // Send write command plain_data[0] = MF_CLASSIC_WRITE_BLOCK_CMD; plain_data[1] = block_num; nfca_append_crc16(plain_data, 2); - crypto1_encrypt(&crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); tx_rx->tx_bits = 4 * 8; tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); if(resp != 0x0A) { FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); break; @@ -1044,7 +1177,7 @@ bool mf_classic_write_block( memcpy(plain_data, src_block->value, MF_CLASSIC_BLOCK_SIZE); nfca_append_crc16(plain_data, MF_CLASSIC_BLOCK_SIZE); crypto1_encrypt( - &crypto, + crypto, NULL, plain_data, (MF_CLASSIC_BLOCK_SIZE + 2) * 8, @@ -1054,8 +1187,8 @@ bool mf_classic_write_block( tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; if(furi_hal_nfc_tx_rx(tx_rx, 50)) { if(tx_rx->rx_bits == 4) { - crypto1_decrypt(&crypto, tx_rx->rx_data, 4, &resp); - if(resp != 0x0A) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != MF_CLASSIC_ACK_CMD) { FURI_LOG_D(TAG, "NACK received on sending data"); break; } @@ -1067,22 +1200,200 @@ bool mf_classic_write_block( FURI_LOG_D(TAG, "Failed to send data"); break; } - write_success = true; - // Send Halt - plain_data[0] = 0x50; - plain_data[1] = 0x00; - nfca_append_crc16(plain_data, 2); - crypto1_encrypt(&crypto, NULL, plain_data, 2 * 8, tx_rx->tx_data, tx_rx->tx_parity); - tx_rx->tx_bits = 2 * 8; - tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; - // No response is expected - furi_hal_nfc_tx_rx(tx_rx, 50); + write_success = true; } while(false); return write_success; } +bool mf_classic_auth_write_block( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + bool write_success = false; + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { + FURI_LOG_D(TAG, "Auth fail"); + break; + } + + if(!mf_classic_write_block(tx_rx, &crypto, block_num, src_block)) { + FURI_LOG_D(TAG, "Write fail"); + break; + } + write_success = true; + + mf_classic_halt(tx_rx, &crypto); + } while(false); + + return write_success; +} + +bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num) { + furi_assert(tx_rx); + furi_assert(crypto); + + // Send transfer command + uint8_t plain_data[4] = {MF_CLASSIC_TRANSFER_CMD, block_num, 0, 0}; + uint8_t resp = 0; + bool transfer_success = false; + + nfca_append_crc16(plain_data, 2); + crypto1_encrypt( + crypto, NULL, plain_data, sizeof(plain_data) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = sizeof(plain_data) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + do { + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send transfer cmd"); + break; + } + + transfer_success = true; + } while(false); + + return transfer_success; +} + +bool mf_classic_value_cmd( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + uint8_t cmd, + int32_t d_value) { + furi_assert(tx_rx); + furi_assert(crypto); + furi_assert( + cmd == MF_CLASSIC_INCREMENT_CMD || cmd == MF_CLASSIC_DECREMENT_CMD || + cmd == MF_CLASSIC_RESTORE_CMD); + furi_assert(d_value >= 0); + + uint8_t plain_data[sizeof(d_value) + 2] = {}; + uint8_t resp = 0; + bool success = false; + + do { + // Send cmd + plain_data[0] = cmd; + plain_data[1] = block_num; + nfca_append_crc16(plain_data, 2); + crypto1_encrypt(crypto, NULL, plain_data, 4 * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = 4 * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on write cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not ACK received"); + break; + } + } else { + FURI_LOG_D(TAG, "Failed to send write cmd"); + break; + } + + // Send data + memcpy(plain_data, &d_value, sizeof(d_value)); + nfca_append_crc16(plain_data, sizeof(d_value)); + crypto1_encrypt( + crypto, NULL, plain_data, (sizeof(d_value) + 2) * 8, tx_rx->tx_data, tx_rx->tx_parity); + tx_rx->tx_bits = (sizeof(d_value) + 2) * 8; + tx_rx->tx_rx_type = FuriHalNfcTxRxTypeRaw; + // inc, dec, restore do not ACK, but they do NACK + if(furi_hal_nfc_tx_rx(tx_rx, 50)) { + if(tx_rx->rx_bits == 4) { + crypto1_decrypt(crypto, tx_rx->rx_data, 4, &resp); + if(resp != 0x0A) { + FURI_LOG_D(TAG, "NACK received on transfer cmd: %02X", resp); + break; + } + } else { + FURI_LOG_D(TAG, "Not NACK received"); + break; + } + } + + success = true; + + } while(false); + + return success; +} + +bool mf_classic_value_cmd_full( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key, + int32_t d_value) { + furi_assert(tx_rx); + furi_assert(src_block); + + Crypto1 crypto = {}; + uint8_t cmd; + bool success = false; + + if(d_value > 0) { + cmd = MF_CLASSIC_INCREMENT_CMD; + } else if(d_value < 0) { + cmd = MF_CLASSIC_DECREMENT_CMD; + d_value = -d_value; + } else { + cmd = MF_CLASSIC_RESTORE_CMD; + } + + do { + furi_hal_nfc_sleep(); + if(!mf_classic_auth(tx_rx, block_num, key, key_type, &crypto, false, 0)) { + FURI_LOG_D(TAG, "Value cmd auth fail"); + break; + } + if(!mf_classic_value_cmd(tx_rx, &crypto, block_num, cmd, d_value)) { + FURI_LOG_D(TAG, "Value cmd inc/dec/res fail"); + break; + } + + if(!mf_classic_transfer(tx_rx, &crypto, block_num)) { + FURI_LOG_D(TAG, "Value cmd transfer fail"); + break; + } + + success = true; + + // Send Halt + mf_classic_halt(tx_rx, &crypto); + } while(false); + + return success; +} + bool mf_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, @@ -1103,31 +1414,99 @@ bool mf_classic_write_sector( // Compare blocks if(memcmp(dest_data->block[i].value, src_data->block[i].value, MF_CLASSIC_BLOCK_SIZE) != 0) { - bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( - dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); - bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( - dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + if(mf_classic_is_value_block(dest_data, i)) { + bool key_a_inc_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataInc); + bool key_b_inc_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataInc); + bool key_a_dec_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataDec); + bool key_b_dec_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataDec); - if(key_a_found && key_a_write_allowed) { - FURI_LOG_I(TAG, "Writing block %d with key A", i); - uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); - if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { - FURI_LOG_E(TAG, "Failed to write block %d", i); - write_success = false; - break; - } - } else if(key_b_found && key_b_write_allowed) { - FURI_LOG_I(TAG, "Writing block %d with key A", i); - uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); - if(!mf_classic_write_block(tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { - FURI_LOG_E(TAG, "Failed to write block %d", i); - write_success = false; - break; + int32_t src_value, dst_value; + + mf_classic_block_to_value(src_data->block[i].value, &src_value, NULL); + mf_classic_block_to_value(dest_data->block[i].value, &dst_value, NULL); + + int32_t diff = src_value - dst_value; + + if(diff > 0) { + if(key_a_found && key_a_inc_allowed) { + FURI_LOG_I(TAG, "Incrementing block %d with key A by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_inc_allowed) { + FURI_LOG_I(TAG, "Incrementing block %d with key B by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to increment block %d", i); + } + } else if(diff < 0) { + if(key_a_found && key_a_dec_allowed) { + FURI_LOG_I(TAG, "Decrementing block %d with key A by %ld", i, -diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key, diff)) { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_dec_allowed) { + FURI_LOG_I(TAG, "Decrementing block %d with key B by %ld", i, diff); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_value_cmd_full( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key, diff)) { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to decrement block %d", i); + } + } else { + FURI_LOG_E(TAG, "Value block %d address changed, cannot write it", i); } } else { - FURI_LOG_E(TAG, "Failed to find key with write access"); - write_success = false; - break; + bool key_a_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyA, MfClassicActionDataWrite); + bool key_b_write_allowed = mf_classic_is_allowed_access_data_block( + dest_data, i, MfClassicKeyB, MfClassicActionDataWrite); + + if(key_a_found && key_a_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_a, 6); + if(!mf_classic_auth_write_block( + tx_rx, &src_data->block[i], i, MfClassicKeyA, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else if(key_b_found && key_b_write_allowed) { + FURI_LOG_I(TAG, "Writing block %d with key A", i); + uint64_t key = nfc_util_bytes2num(sec_tr->key_b, 6); + if(!mf_classic_auth_write_block( + tx_rx, &src_data->block[i], i, MfClassicKeyB, key)) { + FURI_LOG_E(TAG, "Failed to write block %d", i); + write_success = false; + break; + } + } else { + FURI_LOG_E(TAG, "Failed to find key with write access"); + write_success = false; + break; + } } } else { FURI_LOG_D(TAG, "Blocks %d are equal", i); diff --git a/lib/nfc/protocols/mifare_classic.h b/lib/nfc/protocols/mifare_classic.h index 321ab5f03..a88781f9c 100644 --- a/lib/nfc/protocols/mifare_classic.h +++ b/lib/nfc/protocols/mifare_classic.h @@ -120,6 +120,12 @@ bool mf_classic_is_allowed_access_data_block( MfClassicKey key, MfClassicAction action); +bool mf_classic_is_value_block(MfClassicData* data, uint8_t block_num); + +bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* addr); + +void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block); + bool mf_classic_is_key_found(MfClassicData* data, uint8_t sector_num, MfClassicKey key_type); void mf_classic_set_key_found( @@ -177,6 +183,12 @@ void mf_classic_reader_add_sector( uint64_t key_a, uint64_t key_b); +bool mf_classic_read_block( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + MfClassicBlock* block); + void mf_classic_read_sector(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data, uint8_t sec_num); uint8_t mf_classic_read_card( @@ -188,13 +200,38 @@ uint8_t mf_classic_update_card(FuriHalNfcTxRxContext* tx_rx, MfClassicData* data bool mf_classic_emulator(MfClassicEmulator* emulator, FuriHalNfcTxRxContext* tx_rx); +void mf_classic_halt(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto); + bool mf_classic_write_block( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + MfClassicBlock* src_block); + +bool mf_classic_auth_write_block( FuriHalNfcTxRxContext* tx_rx, MfClassicBlock* src_block, uint8_t block_num, MfClassicKey key_type, uint64_t key); +bool mf_classic_transfer(FuriHalNfcTxRxContext* tx_rx, Crypto1* crypto, uint8_t block_num); + +bool mf_classic_value_cmd( + FuriHalNfcTxRxContext* tx_rx, + Crypto1* crypto, + uint8_t block_num, + uint8_t cmd, + int32_t d_value); + +bool mf_classic_value_cmd_full( + FuriHalNfcTxRxContext* tx_rx, + MfClassicBlock* src_block, + uint8_t block_num, + MfClassicKey key_type, + uint64_t key, + int32_t d_value); + bool mf_classic_write_sector( FuriHalNfcTxRxContext* tx_rx, MfClassicData* dest_data, diff --git a/lib/nfc/protocols/nfc_util.c b/lib/nfc/protocols/nfc_util.c index 9de6f982b..8cb6d57f2 100644 --- a/lib/nfc/protocols/nfc_util.c +++ b/lib/nfc/protocols/nfc_util.c @@ -23,7 +23,7 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest) { } } -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len) { +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len) { furi_assert(src); furi_assert(len <= 8); @@ -45,3 +45,26 @@ uint8_t nfc_util_even_parity32(uint32_t data) { uint8_t nfc_util_odd_parity8(uint8_t data) { return nfc_util_odd_byte_parity[data]; } + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len) { + furi_assert(src); + furi_assert(dst); + + uint8_t parity = 0; + uint8_t bit = 0; + while(len--) { + parity |= nfc_util_odd_parity8(*src) << (7 - bit); // parity is MSB first + bit++; + if(bit == 8) { + *dst = parity; + dst++; + parity = 0; + bit = 0; + } + src++; + } + + if(bit) { + *dst = parity; + } +} \ No newline at end of file diff --git a/lib/nfc/protocols/nfc_util.h b/lib/nfc/protocols/nfc_util.h index d530badc3..04fa7622b 100644 --- a/lib/nfc/protocols/nfc_util.h +++ b/lib/nfc/protocols/nfc_util.h @@ -4,8 +4,10 @@ void nfc_util_num2bytes(uint64_t src, uint8_t len, uint8_t* dest); -uint64_t nfc_util_bytes2num(uint8_t* src, uint8_t len); +uint64_t nfc_util_bytes2num(const uint8_t* src, uint8_t len); uint8_t nfc_util_even_parity32(uint32_t data); uint8_t nfc_util_odd_parity8(uint8_t data); + +void nfc_util_odd_parity(const uint8_t* src, uint8_t* dst, uint8_t len); From 904b55d6cfd0147c589867589745b34db24541c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Vesel=C3=BD?= Date: Tue, 7 Feb 2023 15:53:21 +0100 Subject: [PATCH 102/231] Fix CTRL-SHIFT in mousejacker --- applications/plugins/mousejacker/mousejacker_ducky.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/mousejacker/mousejacker_ducky.c b/applications/plugins/mousejacker/mousejacker_ducky.c index 7a57856e1..b3b04d836 100644 --- a/applications/plugins/mousejacker/mousejacker_ducky.c +++ b/applications/plugins/mousejacker/mousejacker_ducky.c @@ -291,7 +291,7 @@ static bool mj_process_ducky_line( strncmp(line_tmp, "CONTROL-SHIFT", strlen("CONTROL-SHIFT")) == 0) { line_tmp = &line_tmp[mj_ducky_get_command_len(line_tmp) + 1]; if(!mj_get_ducky_key(line_tmp, strlen(line_tmp), &dk)) return false; - send_hid_packet(handle, addr, addr_size, rate, dk.mod | 4 | 2, dk.hid, plugin_state); + send_hid_packet(handle, addr, addr_size, rate, dk.mod | 1 | 2, dk.hid, plugin_state); return true; } else if( strncmp(line_tmp, "CTRL", strlen("CTRL")) == 0 || From 1eda913367e3ab7a42344c42c5d8e7e0f9144d12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 00:35:49 +0900 Subject: [PATCH 103/231] [FL-3075] Pin Reset (#2367) * Nfc: fix PVS warnings * Factory reset combo, initial version * Recovery screen and correct input pin initialization * Better pin and factory reset message * Down to cancel factory reset --- firmware/targets/f7/Inc/alt_boot.h | 2 + firmware/targets/f7/Src/main.c | 4 ++ firmware/targets/f7/Src/recovery.c | 54 +++++++++++++++++++ .../targets/f7/furi_hal/furi_hal_resources.c | 18 +++++-- lib/nfc/protocols/mifare_classic.c | 6 +-- 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 firmware/targets/f7/Src/recovery.c diff --git a/firmware/targets/f7/Inc/alt_boot.h b/firmware/targets/f7/Inc/alt_boot.h index 91bf9bdf7..d8be3aa48 100644 --- a/firmware/targets/f7/Inc/alt_boot.h +++ b/firmware/targets/f7/Inc/alt_boot.h @@ -8,6 +8,8 @@ void flipper_boot_update_exec(); void flipper_boot_dfu_exec(); +void flipper_boot_recovery_exec(); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/Src/main.c index d9a2221a2..1f2b5d6e4 100644 --- a/firmware/targets/f7/Src/main.c +++ b/firmware/targets/f7/Src/main.c @@ -49,6 +49,10 @@ int main() { // But if we do, abandon to avoid bootloops furi_hal_rtc_set_boot_mode(FuriHalRtcBootModeNormal); furi_hal_power_reset(); + } else if(!furi_hal_gpio_read(&gpio_button_up)) { + furi_hal_light_sequence("rgb WR"); + flipper_boot_recovery_exec(); + furi_hal_power_reset(); } else { furi_hal_light_sequence("rgb G"); furi_thread_start(main_thread); diff --git a/firmware/targets/f7/Src/recovery.c b/firmware/targets/f7/Src/recovery.c new file mode 100644 index 000000000..fa57482ca --- /dev/null +++ b/firmware/targets/f7/Src/recovery.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include +#include + +#define COUNTER_VALUE (100U) + +static void flipper_boot_recovery_draw_splash(u8g2_t* fb, size_t progress) { + u8g2_ClearBuffer(fb); + u8g2_SetDrawColor(fb, 0x01); + + u8g2_SetFont(fb, u8g2_font_helvB08_tr); + u8g2_DrawStr(fb, 2, 8, "PIN and Factory Reset"); + u8g2_SetFont(fb, u8g2_font_haxrcorp4089_tr); + u8g2_DrawStr(fb, 2, 21, "Hold Right to confirm"); + u8g2_DrawStr(fb, 2, 31, "Press Down to cancel"); + + if(progress < COUNTER_VALUE) { + size_t width = progress / (COUNTER_VALUE / 100); + u8g2_DrawBox(fb, 14 + (50 - width / 2), 54, width, 3); + } + + u8g2_SetPowerSave(fb, 0); + u8g2_SendBuffer(fb); +} + +void flipper_boot_recovery_exec() { + u8g2_t* fb = malloc(sizeof(u8g2_t)); + u8g2_Setup_st756x_flipper(fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32); + u8g2_InitDisplay(fb); + + size_t counter = COUNTER_VALUE; + while(counter) { + if(!furi_hal_gpio_read(&gpio_button_down)) { + break; + } + + if(!furi_hal_gpio_read(&gpio_button_right)) { + counter--; + } else { + counter = COUNTER_VALUE; + } + + flipper_boot_recovery_draw_splash(fb, counter); + } + + if(!counter) { + furi_hal_rtc_set_flag(FuriHalRtcFlagFactoryReset); + furi_hal_rtc_set_pin_fails(0); + furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); + } +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 98ebedf51..4a32d7087 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -73,8 +73,18 @@ const InputPin input_pins[] = { const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + void furi_hal_resources_init_early() { - furi_hal_gpio_init(&gpio_button_left, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_resources_init_input_pins(GpioModeInput); // SD Card stepdown control furi_hal_gpio_write(&periph_power, 1); @@ -117,14 +127,12 @@ void furi_hal_resources_init_early() { } void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); } void furi_hal_resources_init() { // Button pins - for(size_t i = 0; i < input_pins_count; i++) { - furi_hal_gpio_init( - input_pins[i].gpio, GpioModeInterruptRiseFall, GpioPullUp, GpioSpeedLow); - } + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); // Display pins furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/lib/nfc/protocols/mifare_classic.c b/lib/nfc/protocols/mifare_classic.c index 28667f09b..e4d5e0274 100644 --- a/lib/nfc/protocols/mifare_classic.c +++ b/lib/nfc/protocols/mifare_classic.c @@ -438,9 +438,9 @@ bool mf_classic_block_to_value(const uint8_t* block, int32_t* value, uint8_t* ad void mf_classic_value_to_block(int32_t value, uint8_t addr, uint8_t* block) { uint32_t v_inv = ~((uint32_t)value); - memcpy(block, &value, 4); - memcpy(block + 4, &v_inv, 4); - memcpy(block + 8, &value, 4); + memcpy(block, &value, 4); //-V1086 + memcpy(block + 4, &v_inv, 4); //-V1086 + memcpy(block + 8, &value, 4); //-V1086 block[12] = addr; block[13] = ~addr & 0xFF; From 224d0aefe41edd8356aa41aa6b39cbe7659c5d98 Mon Sep 17 00:00:00 2001 From: hedger Date: Tue, 7 Feb 2023 19:33:05 +0300 Subject: [PATCH 104/231] [FL-2733] multitarget support for fbt (#2209) * First part of multitarget porting * Delete firmware/targets/f7/Inc directory * Delete firmware/targets/f7/Src directory * gpio: cli fixes; about: using version from HAL * sdk: path fixes * gui: include fixes * applications: more include fixes * gpio: ported to new apis * hal: introduced furi_hal_target_hw.h; libs: added one_wire * hal: f18 target * github: also build f18 by default * typo fix * fbt: removed extra checks on app list * api: explicitly bundling select mlib headers with sdk * hal: f18: changed INPUT_DEBOUNCE_TICKS to match f7 * cleaned up commented out code * docs: added info on hw targets * docs: targets: formatting fixes * f18: fixed link error * f18: fixed API version to match f7 * docs: hardware: minor wording fixes * faploader: added fw target check * docs: typo fixes * github: not building komi target by default * fbt: support for `targets` field for built-in apps * github: reworked build flow to exclude app_set; fbt: removed komi-specific appset; added additional target buildset check * github: fixed build; nfc: fixed pvs warnings * attempt to fix target id * f7, f18: removed certain HAL function from public API * apps: debug: enabled bt_debug_app for f18 * Targets: backport input pins configuration routine from F7 to F18 Co-authored-by: Aleksandr Kutuzov --- .github/workflows/build.yml | 11 +- .github/workflows/pvs_studio.yml | 2 +- .vscode/extensions.json | 7 +- applications/debug/accessor/application.fam | 1 + .../bt_debug_app/views/bt_carrier_test.c | 2 +- .../debug/bt_debug_app/views/bt_packet_test.c | 2 +- .../file_browser_test/file_browser_app.c | 9 +- .../debug/lfrfid_debug/application.fam | 1 + .../main/archive/helpers/archive_browser.c | 5 +- .../main/archive/views/archive_browser_view.h | 9 +- .../scenes/bad_usb_scene_file_select.c | 4 +- .../main/bad_usb/scenes/bad_usb_scene_work.c | 2 +- applications/main/gpio/gpio_app.c | 4 +- applications/main/gpio/gpio_app_i.h | 3 +- applications/main/gpio/gpio_item.c | 51 - applications/main/gpio/gpio_item.h | 15 - applications/main/gpio/gpio_items.c | 69 + applications/main/gpio/gpio_items.h | 29 + .../main/gpio/scenes/gpio_scene_start.c | 4 +- .../main/gpio/scenes/gpio_scene_test.c | 8 +- .../gpio/scenes/gpio_scene_usb_uart_config.c | 2 +- applications/main/gpio/usb_uart_bridge.c | 8 +- applications/main/gpio/views/gpio_test.c | 30 +- applications/main/gpio/views/gpio_test.h | 4 +- applications/main/gpio/views/gpio_usb_uart.c | 2 +- applications/main/ibutton/application.fam | 1 + applications/main/infrared/application.fam | 1 + .../main/infrared/scenes/infrared_scene_rpc.c | 2 +- .../main/infrared/views/infrared_debug_view.c | 6 +- .../infrared/views/infrared_progress_view.c | 20 +- applications/main/lfrfid/application.fam | 1 + applications/main/nfc/application.fam | 1 + applications/main/nfc/nfc.c | 2 +- applications/main/subghz/application.fam | 1 + applications/main/u2f/scenes/u2f_scene_main.c | 2 +- .../music_player/music_player_worker.c | 1 + .../plugins/nfc_magic/application.fam | 1 + applications/plugins/picopass/application.fam | 1 + .../signal_generator/signal_gen_app_i.h | 4 +- .../signal_generator/views/signal_gen_pwm.c | 2 +- .../plugins/weather_station/application.fam | 1 + .../weather_station/protocols/ws_generic.h | 2 +- applications/services/cli/cli_command_gpio.c | 66 +- applications/services/dialogs/dialogs_api.c | 2 +- .../dialogs/dialogs_module_file_browser.c | 3 +- applications/services/dolphin/dolphin.h | 5 +- applications/services/gui/elements.c | 7 +- applications/services/gui/gui.c | 1 - .../services/gui/modules/button_menu.c | 13 +- .../services/gui/modules/button_panel.c | 13 +- .../services/gui/modules/byte_input.c | 7 +- .../services/gui/modules/file_browser.c | 13 +- .../gui/modules/file_browser_worker.c | 11 +- applications/services/gui/modules/loading.c | 9 +- applications/services/gui/modules/menu.c | 2 +- applications/services/gui/modules/submenu.c | 2 +- applications/services/gui/modules/text_box.c | 4 +- .../services/gui/modules/validators.c | 2 +- .../services/gui/modules/validators.h | 1 + .../services/gui/modules/variable_item_list.c | 6 +- applications/services/gui/modules/widget.c | 4 +- applications/services/gui/view_stack.c | 5 +- .../services/notification/notification_app.c | 2 +- .../notification/notification_messages.c | 2 +- applications/settings/about/about.c | 24 +- .../bt_settings_scene_forget_dev_confirm.c | 2 +- .../bt_settings_scene_forget_dev_success.c | 2 +- .../scenes/bt_settings_scene_start.c | 2 +- .../scenes/storage_move_to_sd_scene_confirm.c | 6 +- .../storage_move_to_sd/storage_move_to_sd.h | 7 +- applications/system/updater/cli/updater_cli.c | 2 +- .../system/updater/util/update_task.c | 4 +- documentation/HardwareTargets.md | 44 + firmware.scons | 57 +- firmware/SConscript | 17 +- firmware/targets/f18/api_symbols.csv | 2307 +++++++++++++++++ firmware/targets/f18/furi_hal/furi_hal.c | 91 + .../targets/f18/furi_hal/furi_hal_resources.c | 201 ++ .../targets/f18/furi_hal/furi_hal_resources.h | 116 + .../f18/furi_hal/furi_hal_spi_config.c | 377 +++ .../f18/furi_hal/furi_hal_spi_config.h | 55 + .../targets/f18/furi_hal/furi_hal_target_hw.h | 1 + .../f18/furi_hal/furi_hal_version_device.c | 21 + firmware/targets/f18/target.json | 55 + firmware/targets/f7/api_symbols.csv | 41 +- firmware/targets/f7/furi_hal/furi_hal.c | 6 +- .../targets/f7/furi_hal/furi_hal_bt_hid.c | 4 +- .../targets/f7/furi_hal/furi_hal_bt_serial.c | 2 +- .../targets/f7/furi_hal/furi_hal_cortex.c | 2 +- .../targets/f7/furi_hal/furi_hal_i2c_config.c | 2 +- .../furi_hal}/furi_hal_ibutton.h | 2 +- .../targets/f7/furi_hal/furi_hal_infrared.c | 2 +- .../targets/f7/furi_hal/furi_hal_interrupt.c | 4 +- firmware/targets/f7/furi_hal/furi_hal_light.c | 2 +- firmware/targets/f7/furi_hal/furi_hal_nfc.c | 2 +- .../furi_hal}/furi_hal_nfc.h | 0 firmware/targets/f7/furi_hal/furi_hal_pwm.c | 2 +- .../targets/f7/furi_hal/furi_hal_random.c | 2 +- .../targets/f7/furi_hal/furi_hal_resources.c | 17 + .../targets/f7/furi_hal/furi_hal_resources.h | 9 + .../furi_hal}/furi_hal_rfid.h | 0 firmware/targets/f7/furi_hal/furi_hal_sd.c | 13 +- firmware/targets/f7/furi_hal/furi_hal_spi.c | 28 +- .../targets/f7/furi_hal/furi_hal_spi_config.c | 25 + .../targets/f7/furi_hal/furi_hal_subghz.c | 4 +- .../furi_hal}/furi_hal_subghz.h | 0 .../targets/f7/furi_hal/furi_hal_target_hw.h | 6 + firmware/targets/f7/furi_hal/furi_hal_usb.c | 6 +- .../targets/f7/furi_hal/furi_hal_usb_cdc.c | 8 +- .../targets/f7/furi_hal/furi_hal_usb_hid.c | 8 +- .../targets/f7/furi_hal/furi_hal_usb_u2f.c | 8 +- .../targets/f7/furi_hal/furi_hal_version.c | 8 - .../f7/furi_hal/furi_hal_version_device.c | 21 + .../targets/f7/{Inc => inc}/FreeRTOSConfig.h | 0 firmware/targets/f7/{Inc => inc}/alt_boot.h | 0 firmware/targets/f7/{Inc => inc}/stm32.h | 0 .../targets/f7/{Inc => inc}/stm32_assert.h | 0 firmware/targets/f7/{Src => src}/dfu.c | 0 firmware/targets/f7/{Src => src}/main.c | 0 firmware/targets/f7/{Src => src}/recovery.c | 2 +- .../f7/{Src => src}/system_stm32wbxx.c | 0 firmware/targets/f7/{Src => src}/update.c | 2 +- firmware/targets/f7/stm32wb55xx_flash.ld | 2 +- firmware/targets/f7/stm32wb55xx_ram_fw.ld | 2 +- firmware/targets/f7/target.json | 45 + firmware/targets/furi_hal_include/furi_hal.h | 59 +- .../targets/furi_hal_include/furi_hal_bt.h | 2 +- .../targets/furi_hal_include/furi_hal_spi.h | 6 +- .../furi_hal_include/furi_hal_version.h | 18 + lib/SConscript | 34 +- .../view_modules/popup_vm.cpp | 3 +- .../application_manifest.c | 7 + .../application_manifest.h | 8 + lib/flipper_application/flipper_application.c | 4 + lib/misc.scons | 1 - lib/nfc/parsers/all_in_one.c | 2 +- lib/nfc/parsers/plantain_4k_parser.c | 2 +- lib/nfc/parsers/plantain_parser.c | 2 +- lib/nfc/parsers/two_cities.c | 2 +- lib/nfc/protocols/mifare_ultralight.c | 2 +- lib/one_wire/SConscript | 27 + lib/subghz/blocks/generic.h | 4 +- lib/subghz/protocols/princeton_for_testing.c | 2 +- lib/subghz/subghz_setting.c | 2 +- lib/update_util/update_operation.c | 7 +- scripts/fbt/appmanifest.py | 74 +- scripts/fbt_tools/fbt_apps.py | 11 +- scripts/fbt_tools/fbt_hwtarget.py | 134 + scripts/fbt_tools/sconsmodular.py | 2 + site_scons/commandline.scons | 3 +- site_scons/extapps.scons | 17 +- site_scons/firmwareopts.scons | 9 +- 152 files changed, 4140 insertions(+), 495 deletions(-) delete mode 100644 applications/main/gpio/gpio_item.c delete mode 100644 applications/main/gpio/gpio_item.h create mode 100644 applications/main/gpio/gpio_items.c create mode 100644 applications/main/gpio/gpio_items.h create mode 100644 documentation/HardwareTargets.md create mode 100644 firmware/targets/f18/api_symbols.csv create mode 100644 firmware/targets/f18/furi_hal/furi_hal.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_resources.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_resources.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_spi_config.c create mode 100644 firmware/targets/f18/furi_hal/furi_hal_spi_config.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_target_hw.h create mode 100644 firmware/targets/f18/furi_hal/furi_hal_version_device.c create mode 100644 firmware/targets/f18/target.json rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_ibutton.h (98%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_nfc.h (100%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_rfid.h (100%) rename firmware/targets/{furi_hal_include => f7/furi_hal}/furi_hal_subghz.h (100%) create mode 100644 firmware/targets/f7/furi_hal/furi_hal_target_hw.h create mode 100644 firmware/targets/f7/furi_hal/furi_hal_version_device.c rename firmware/targets/f7/{Inc => inc}/FreeRTOSConfig.h (100%) rename firmware/targets/f7/{Inc => inc}/alt_boot.h (100%) rename firmware/targets/f7/{Inc => inc}/stm32.h (100%) rename firmware/targets/f7/{Inc => inc}/stm32_assert.h (100%) rename firmware/targets/f7/{Src => src}/dfu.c (100%) rename firmware/targets/f7/{Src => src}/main.c (100%) rename firmware/targets/f7/{Src => src}/recovery.c (99%) rename firmware/targets/f7/{Src => src}/system_stm32wbxx.c (100%) rename firmware/targets/f7/{Src => src}/update.c (99%) create mode 100644 firmware/targets/f7/target.json create mode 100644 lib/one_wire/SConscript create mode 100644 scripts/fbt_tools/fbt_hwtarget.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f7233e46..080e7c2c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: pull_request: env: - TARGETS: f7 + TARGETS: f7 f18 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work @@ -60,8 +60,9 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ - copro_dist updater_package ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET copro_dist updater_package \ + ${{ startsWith(github.ref, 'refs/tags') && 'DEBUG=0 COMPACT=1' || '' }} done - name: 'Move upload files' @@ -186,6 +187,6 @@ jobs: run: | set -e for TARGET in ${TARGETS}; do - ./fbt TARGET_HW="$(echo "${TARGET}" | sed 's/f//')" \ - updater_package DEBUG=0 COMPACT=1 + TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package done diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index 50f8f0aa6..a4ac6e301 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -89,6 +89,6 @@ jobs: - name: 'Raise exception' if: ${{ steps.pvs-warn.outputs.warnings != 0 }} run: | - echo "Please fix all PVS varnings before merge" + echo "Please fix all PVS warnings before merge" exit 1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b53ffc24c..b5791a91e 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -11,5 +11,8 @@ "augustocdias.tasks-shell-input" ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. - "unwantedRecommendations": [] -} \ No newline at end of file + "unwantedRecommendations": [ + "twxs.cmake", + "ms-vscode.cmake-tools" + ] +} diff --git a/applications/debug/accessor/application.fam b/applications/debug/accessor/application.fam index 93fc9bc3f..6b8472711 100644 --- a/applications/debug/accessor/application.fam +++ b/applications/debug/accessor/application.fam @@ -2,6 +2,7 @@ App( appid="accessor", name="Accessor", apptype=FlipperAppType.DEBUG, + targets=["f7"], entry_point="accessor_app", cdefines=["APP_ACCESSOR"], requires=["gui"], diff --git a/applications/debug/bt_debug_app/views/bt_carrier_test.c b/applications/debug/bt_debug_app/views/bt_carrier_test.c index c09aa3fdf..8e2240495 100644 --- a/applications/debug/bt_debug_app/views/bt_carrier_test.c +++ b/applications/debug/bt_debug_app/views/bt_carrier_test.c @@ -1,7 +1,7 @@ #include "bt_carrier_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtCarrierTest { BtTest* bt_test; diff --git a/applications/debug/bt_debug_app/views/bt_packet_test.c b/applications/debug/bt_debug_app/views/bt_packet_test.c index 7cbc3c5c5..8a56a3003 100644 --- a/applications/debug/bt_debug_app/views/bt_packet_test.c +++ b/applications/debug/bt_debug_app/views/bt_packet_test.c @@ -1,7 +1,7 @@ #include "bt_packet_test.h" #include "bt_test.h" #include "bt_test_types.h" -#include "furi_hal_bt.h" +#include struct BtPacketTest { BtTest* bt_test; diff --git a/applications/debug/file_browser_test/file_browser_app.c b/applications/debug/file_browser_test/file_browser_app.c index bf423d34e..c3e7c898b 100644 --- a/applications/debug/file_browser_test/file_browser_app.c +++ b/applications/debug/file_browser_test/file_browser_app.c @@ -1,10 +1,11 @@ -#include #include "file_browser_app_i.h" -#include "gui/modules/file_browser.h" -#include -#include +#include + +#include #include #include +#include +#include static bool file_browser_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); diff --git a/applications/debug/lfrfid_debug/application.fam b/applications/debug/lfrfid_debug/application.fam index 6844f9291..323f77818 100644 --- a/applications/debug/lfrfid_debug/application.fam +++ b/applications/debug/lfrfid_debug/application.fam @@ -2,6 +2,7 @@ App( appid="lfrfid_debug", name="LF-RFID Debug", apptype=FlipperAppType.DEBUG, + targets=["f7"], entry_point="lfrfid_debug_app", requires=[ "gui", diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 6c8c9633a..1133f6d71 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -1,10 +1,11 @@ -#include #include "archive_files.h" #include "archive_apps.h" #include "archive_browser.h" +#include "../views/archive_browser_view.h" + #include #include -#include "gui/modules/file_browser_worker.h" +#include #include #include diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 915b5307a..0a000e5ac 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -1,14 +1,15 @@ #pragma once +#include "../helpers/archive_files.h" +#include "../helpers/archive_favorites.h" + #include #include #include #include -#include +#include #include -#include "../helpers/archive_files.h" -#include "../helpers/archive_favorites.h" -#include "gui/modules/file_browser_worker.h" +#include #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index 9264eb976..f1f34f5bc 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -1,6 +1,6 @@ #include "../bad_usb_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" +#include +#include #include static bool bad_usb_file_select(BadUsbApp* bad_usb) { diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 689f3b4ea..1e3534822 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -1,7 +1,7 @@ #include "../bad_usb_script.h" #include "../bad_usb_app_i.h" #include "../views/bad_usb_view.h" -#include "furi_hal.h" +#include #include "toolbox/path.h" void bad_usb_scene_work_ok_callback(InputType type, void* context) { diff --git a/applications/main/gpio/gpio_app.c b/applications/main/gpio/gpio_app.c index b8afdc8ea..1ecff1ec2 100644 --- a/applications/main/gpio/gpio_app.c +++ b/applications/main/gpio/gpio_app.c @@ -25,6 +25,7 @@ GpioApp* gpio_app_alloc() { GpioApp* app = malloc(sizeof(GpioApp)); app->gui = furi_record_open(RECORD_GUI); + app->gpio_items = gpio_items_alloc(); app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&gpio_scene_handlers, app); @@ -47,7 +48,7 @@ GpioApp* gpio_app_alloc() { app->view_dispatcher, GpioAppViewVarItemList, variable_item_list_get_view(app->var_item_list)); - app->gpio_test = gpio_test_alloc(); + app->gpio_test = gpio_test_alloc(app->gpio_items); view_dispatcher_add_view( app->view_dispatcher, GpioAppViewGpioTest, gpio_test_get_view(app->gpio_test)); @@ -91,6 +92,7 @@ void gpio_app_free(GpioApp* app) { furi_record_close(RECORD_GUI); furi_record_close(RECORD_NOTIFICATION); + gpio_items_free(app->gpio_items); free(app); } diff --git a/applications/main/gpio/gpio_app_i.h b/applications/main/gpio/gpio_app_i.h index 8f805891b..03fe9f489 100644 --- a/applications/main/gpio/gpio_app_i.h +++ b/applications/main/gpio/gpio_app_i.h @@ -1,7 +1,7 @@ #pragma once #include "gpio_app.h" -#include "gpio_item.h" +#include "gpio_items.h" #include "scenes/gpio_scene.h" #include "gpio_custom_event.h" #include "usb_uart_bridge.h" @@ -28,6 +28,7 @@ struct GpioApp { VariableItem* var_item_flow; GpioTest* gpio_test; GpioUsbUart* gpio_usb_uart; + GPIOItems* gpio_items; UsbUartBridge* usb_uart_bridge; UsbUartConfig* usb_uart_cfg; }; diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c deleted file mode 100644 index 2d0f5f676..000000000 --- a/applications/main/gpio/gpio_item.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "gpio_item.h" - -#include - -typedef struct { - const char* name; - const GpioPin* pin; -} GpioItem; - -static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { - {"1.2: PA7", &gpio_ext_pa7}, - {"1.3: PA6", &gpio_ext_pa6}, - {"1.4: PA4", &gpio_ext_pa4}, - {"1.5: PB3", &gpio_ext_pb3}, - {"1.6: PB2", &gpio_ext_pb2}, - {"1.7: PC3", &gpio_ext_pc3}, - {"2.7: PC1", &gpio_ext_pc1}, - {"2.8: PC0", &gpio_ext_pc0}, -}; - -void gpio_item_configure_pin(uint8_t index, GpioMode mode) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, false); - furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); -} - -void gpio_item_configure_all_pins(GpioMode mode) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_configure_pin(i, mode); - } -} - -void gpio_item_set_pin(uint8_t index, bool level) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, level); -} - -void gpio_item_set_all_pins(bool level) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_set_pin(i, level); - } -} - -const char* gpio_item_get_pin_name(uint8_t index) { - furi_assert(index < GPIO_ITEM_COUNT + 1); - if(index == GPIO_ITEM_COUNT) { - return "ALL"; - } else { - return gpio_item[index].name; - } -} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h deleted file mode 100644 index 5cb2b86c1..000000000 --- a/applications/main/gpio/gpio_item.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#define GPIO_ITEM_COUNT 8 - -void gpio_item_configure_pin(uint8_t index, GpioMode mode); - -void gpio_item_configure_all_pins(GpioMode mode); - -void gpio_item_set_pin(uint8_t index, bool level); - -void gpio_item_set_all_pins(bool level); - -const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/main/gpio/gpio_items.c b/applications/main/gpio/gpio_items.c new file mode 100644 index 000000000..02f7d95b0 --- /dev/null +++ b/applications/main/gpio/gpio_items.c @@ -0,0 +1,69 @@ +#include "gpio_items.h" + +#include + +struct GPIOItems { + GpioPinRecord* pins; + size_t count; +}; + +GPIOItems* gpio_items_alloc() { + GPIOItems* items = malloc(sizeof(GPIOItems)); + + items->count = 0; + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug) { + items->count++; + } + } + + items->pins = malloc(sizeof(GpioPinRecord) * items->count); + for(size_t i = 0; i < items->count; i++) { + if(!gpio_pins[i].debug) { + items->pins[i].pin = gpio_pins[i].pin; + items->pins[i].name = gpio_pins[i].name; + } + } + return items; +} + +void gpio_items_free(GPIOItems* items) { + free(items->pins); + free(items); +} + +uint8_t gpio_items_get_count(GPIOItems* items) { + return items->count; +} + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, false); + furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_configure_pin(items, i, mode); + } +} + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) { + furi_assert(index < items->count); + furi_hal_gpio_write(items->pins[index].pin, level); +} + +void gpio_items_set_all_pins(GPIOItems* items, bool level) { + for(uint8_t i = 0; i < items->count; i++) { + gpio_items_set_pin(items, i, level); + } +} + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) { + furi_assert(index < items->count + 1); + if(index == items->count) { + return "ALL"; + } else { + return items->pins[index].name; + } +} diff --git a/applications/main/gpio/gpio_items.h b/applications/main/gpio/gpio_items.h new file mode 100644 index 000000000..68afbe693 --- /dev/null +++ b/applications/main/gpio/gpio_items.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct GPIOItems GPIOItems; + +GPIOItems* gpio_items_alloc(); + +void gpio_items_free(GPIOItems* items); + +uint8_t gpio_items_get_count(GPIOItems* items); + +void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode); + +void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode); + +void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level); + +void gpio_items_set_all_pins(GPIOItems* items, bool level); + +const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/gpio/scenes/gpio_scene_start.c b/applications/main/gpio/scenes/gpio_scene_start.c index 729922949..027267793 100644 --- a/applications/main/gpio/scenes/gpio_scene_start.c +++ b/applications/main/gpio/scenes/gpio_scene_start.c @@ -1,6 +1,6 @@ #include "../gpio_app_i.h" -#include "furi_hal_power.h" -#include "furi_hal_usb.h" +#include +#include #include enum GpioItem { diff --git a/applications/main/gpio/scenes/gpio_scene_test.c b/applications/main/gpio/scenes/gpio_scene_test.c index b015d8090..9940b95d4 100644 --- a/applications/main/gpio/scenes/gpio_scene_test.c +++ b/applications/main/gpio/scenes/gpio_scene_test.c @@ -12,8 +12,9 @@ void gpio_scene_test_ok_callback(InputType type, void* context) { } void gpio_scene_test_on_enter(void* context) { + furi_assert(context); GpioApp* app = context; - gpio_item_configure_all_pins(GpioModeOutputPushPull); + gpio_items_configure_all_pins(app->gpio_items, GpioModeOutputPushPull); gpio_test_set_ok_callback(app->gpio_test, gpio_scene_test_ok_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, GpioAppViewGpioTest); } @@ -25,6 +26,7 @@ bool gpio_scene_test_on_event(void* context, SceneManagerEvent event) { } void gpio_scene_test_on_exit(void* context) { - UNUSED(context); - gpio_item_configure_all_pins(GpioModeAnalog); + furi_assert(context); + GpioApp* app = context; + gpio_items_configure_all_pins(app->gpio_items, GpioModeAnalog); } diff --git a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c index 776343fb0..e2ab66264 100644 --- a/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c +++ b/applications/main/gpio/scenes/gpio_scene_usb_uart_config.c @@ -1,6 +1,6 @@ #include "../usb_uart_bridge.h" #include "../gpio_app_i.h" -#include "furi_hal.h" +#include typedef enum { UsbUartLineIndexVcp, diff --git a/applications/main/gpio/usb_uart_bridge.c b/applications/main/gpio/usb_uart_bridge.c index 1a82dbdc2..9bc759dc8 100644 --- a/applications/main/gpio/usb_uart_bridge.c +++ b/applications/main/gpio/usb_uart_bridge.c @@ -1,10 +1,10 @@ #include "usb_uart_bridge.h" -#include "furi_hal.h" -#include #include "usb_cdc.h" -#include "cli/cli_vcp.h" +#include +#include #include -#include "cli/cli.h" +#include +#include #define USB_CDC_PKT_LEN CDC_DATA_SZ #define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5) diff --git a/applications/main/gpio/views/gpio_test.c b/applications/main/gpio/views/gpio_test.c index 69dc0f67b..c154a7275 100644 --- a/applications/main/gpio/views/gpio_test.c +++ b/applications/main/gpio/views/gpio_test.c @@ -1,5 +1,5 @@ #include "gpio_test.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include @@ -11,6 +11,7 @@ struct GpioTest { typedef struct { uint8_t pin_idx; + GPIOItems* gpio_items; } GpioTestModel; static bool gpio_test_process_left(GpioTest* gpio_test); @@ -25,7 +26,12 @@ static void gpio_test_draw_callback(Canvas* canvas, void* _model) { elements_multiline_text_aligned( canvas, 64, 16, AlignCenter, AlignTop, "Press < or > to change pin"); elements_multiline_text_aligned( - canvas, 64, 32, AlignCenter, AlignTop, gpio_item_get_pin_name(model->pin_idx)); + canvas, + 64, + 32, + AlignCenter, + AlignTop, + gpio_items_get_pin_name(model->gpio_items, model->pin_idx)); } static bool gpio_test_input_callback(InputEvent* event, void* context) { @@ -64,7 +70,7 @@ static bool gpio_test_process_right(GpioTest* gpio_test) { gpio_test->view, GpioTestModel * model, { - if(model->pin_idx < GPIO_ITEM_COUNT) { + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { model->pin_idx++; } }, @@ -80,17 +86,17 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { GpioTestModel * model, { if(event->type == InputTypePress) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, true); + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, true); } else { - gpio_item_set_all_pins(true); + gpio_items_set_all_pins(model->gpio_items, true); } consumed = true; } else if(event->type == InputTypeRelease) { - if(model->pin_idx < GPIO_ITEM_COUNT) { - gpio_item_set_pin(model->pin_idx, false); + if(model->pin_idx < gpio_items_get_count(model->gpio_items)) { + gpio_items_set_pin(model->gpio_items, model->pin_idx, false); } else { - gpio_item_set_all_pins(false); + gpio_items_set_all_pins(model->gpio_items, false); } consumed = true; } @@ -101,11 +107,15 @@ static bool gpio_test_process_ok(GpioTest* gpio_test, InputEvent* event) { return consumed; } -GpioTest* gpio_test_alloc() { +GpioTest* gpio_test_alloc(GPIOItems* gpio_items) { GpioTest* gpio_test = malloc(sizeof(GpioTest)); gpio_test->view = view_alloc(); view_allocate_model(gpio_test->view, ViewModelTypeLocking, sizeof(GpioTestModel)); + + with_view_model( + gpio_test->view, GpioTestModel * model, { model->gpio_items = gpio_items; }, false); + view_set_context(gpio_test->view, gpio_test); view_set_draw_callback(gpio_test->view, gpio_test_draw_callback); view_set_input_callback(gpio_test->view, gpio_test_input_callback); diff --git a/applications/main/gpio/views/gpio_test.h b/applications/main/gpio/views/gpio_test.h index 5cbd11e82..38fcbc5fb 100644 --- a/applications/main/gpio/views/gpio_test.h +++ b/applications/main/gpio/views/gpio_test.h @@ -1,11 +1,13 @@ #pragma once +#include "../gpio_items.h" + #include typedef struct GpioTest GpioTest; typedef void (*GpioTestOkCallback)(InputType type, void* context); -GpioTest* gpio_test_alloc(); +GpioTest* gpio_test_alloc(GPIOItems* gpio_items); void gpio_test_free(GpioTest* gpio_test); diff --git a/applications/main/gpio/views/gpio_usb_uart.c b/applications/main/gpio/views/gpio_usb_uart.c index c7406d29b..837f2e3ec 100644 --- a/applications/main/gpio/views/gpio_usb_uart.c +++ b/applications/main/gpio/views/gpio_usb_uart.c @@ -1,6 +1,6 @@ #include "../usb_uart_bridge.h" #include "../gpio_app_i.h" -#include "furi_hal.h" +#include #include struct GpioUsbUart { diff --git a/applications/main/ibutton/application.fam b/applications/main/ibutton/application.fam index 77bb9a33c..06968bba4 100644 --- a/applications/main/ibutton/application.fam +++ b/applications/main/ibutton/application.fam @@ -2,6 +2,7 @@ App( appid="ibutton", name="iButton", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="ibutton_app", cdefines=["APP_IBUTTON"], requires=[ diff --git a/applications/main/infrared/application.fam b/applications/main/infrared/application.fam index 9c5eaf392..e5483e9ff 100644 --- a/applications/main/infrared/application.fam +++ b/applications/main/infrared/application.fam @@ -3,6 +3,7 @@ App( name="Infrared", apptype=FlipperAppType.APP, entry_point="infrared_app", + targets=["f7"], cdefines=["APP_INFRARED"], requires=[ "gui", diff --git a/applications/main/infrared/scenes/infrared_scene_rpc.c b/applications/main/infrared/scenes/infrared_scene_rpc.c index 8044e8318..04f17416d 100644 --- a/applications/main/infrared/scenes/infrared_scene_rpc.c +++ b/applications/main/infrared/scenes/infrared_scene_rpc.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "gui/canvas.h" +#include typedef enum { InfraredRpcStateIdle, diff --git a/applications/main/infrared/views/infrared_debug_view.c b/applications/main/infrared/views/infrared_debug_view.c index ab2c679c4..ec0896281 100644 --- a/applications/main/infrared/views/infrared_debug_view.c +++ b/applications/main/infrared/views/infrared_debug_view.c @@ -1,11 +1,11 @@ #include "infrared_debug_view.h" -#include -#include - #include #include +#include +#include + #define INFRARED_DEBUG_TEXT_LENGTH 64 struct InfraredDebugView { diff --git a/applications/main/infrared/views/infrared_progress_view.c b/applications/main/infrared/views/infrared_progress_view.c index 3c50f89e4..432da7ff1 100644 --- a/applications/main/infrared/views/infrared_progress_view.c +++ b/applications/main/infrared/views/infrared_progress_view.c @@ -1,13 +1,15 @@ -#include -#include "furi_hal_resources.h" -#include "assets_icons.h" -#include "gui/canvas.h" -#include "gui/view.h" -#include "input/input.h" -#include -#include #include "infrared_progress_view.h" -#include "gui/modules/button_panel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include #include struct InfraredProgressView { diff --git a/applications/main/lfrfid/application.fam b/applications/main/lfrfid/application.fam index 150a6f3db..8fe1bac4d 100644 --- a/applications/main/lfrfid/application.fam +++ b/applications/main/lfrfid/application.fam @@ -2,6 +2,7 @@ App( appid="lfrfid", name="125 kHz RFID", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="lfrfid_app", cdefines=["APP_LF_RFID"], requires=[ diff --git a/applications/main/nfc/application.fam b/applications/main/nfc/application.fam index ce21bfd27..f09127045 100644 --- a/applications/main/nfc/application.fam +++ b/applications/main/nfc/application.fam @@ -2,6 +2,7 @@ App( appid="nfc", name="NFC", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="nfc_app", cdefines=["APP_NFC"], requires=[ diff --git a/applications/main/nfc/nfc.c b/applications/main/nfc/nfc.c index 6e6dc4dcc..4540f5d9f 100644 --- a/applications/main/nfc/nfc.c +++ b/applications/main/nfc/nfc.c @@ -1,5 +1,5 @@ #include "nfc_i.h" -#include "furi_hal_nfc.h" +#include #include bool nfc_custom_event_callback(void* context, uint32_t event) { diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 7d31ecae5..6df4b1a8a 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -2,6 +2,7 @@ App( appid="subghz", name="Sub-GHz", apptype=FlipperAppType.APP, + targets=["f7"], entry_point="subghz_app", cdefines=["APP_SUBGHZ"], requires=[ diff --git a/applications/main/u2f/scenes/u2f_scene_main.c b/applications/main/u2f/scenes/u2f_scene_main.c index af7f1159b..251bc4d99 100644 --- a/applications/main/u2f/scenes/u2f_scene_main.c +++ b/applications/main/u2f/scenes/u2f_scene_main.c @@ -1,7 +1,7 @@ #include "../u2f_app_i.h" #include "../views/u2f_view.h" #include -#include "furi_hal.h" +#include #include "../u2f.h" #define U2F_REQUEST_TIMEOUT 500 diff --git a/applications/plugins/music_player/music_player_worker.c b/applications/plugins/music_player/music_player_worker.c index 60fd33a17..ee350ee80 100644 --- a/applications/plugins/music_player/music_player_worker.c +++ b/applications/plugins/music_player/music_player_worker.c @@ -6,6 +6,7 @@ #include #include +#include #include #define TAG "MusicPlayerWorker" diff --git a/applications/plugins/nfc_magic/application.fam b/applications/plugins/nfc_magic/application.fam index f09d65c90..bf42681ca 100644 --- a/applications/plugins/nfc_magic/application.fam +++ b/applications/plugins/nfc_magic/application.fam @@ -2,6 +2,7 @@ App( appid="nfc_magic", name="Nfc Magic", apptype=FlipperAppType.EXTERNAL, + targets=["f7"], entry_point="nfc_magic_app", requires=[ "storage", diff --git a/applications/plugins/picopass/application.fam b/applications/plugins/picopass/application.fam index bfbe5ed02..f2da6a9fa 100644 --- a/applications/plugins/picopass/application.fam +++ b/applications/plugins/picopass/application.fam @@ -2,6 +2,7 @@ App( appid="picopass", name="PicoPass Reader", apptype=FlipperAppType.EXTERNAL, + targets=["f7"], entry_point="picopass_app", requires=[ "storage", diff --git a/applications/plugins/signal_generator/signal_gen_app_i.h b/applications/plugins/signal_generator/signal_gen_app_i.h index 47c266475..60e4d7ed9 100644 --- a/applications/plugins/signal_generator/signal_gen_app_i.h +++ b/applications/plugins/signal_generator/signal_gen_app_i.h @@ -2,8 +2,8 @@ #include "scenes/signal_gen_scene.h" -#include "furi_hal_clock.h" -#include "furi_hal_pwm.h" +#include +#include #include #include diff --git a/applications/plugins/signal_generator/views/signal_gen_pwm.c b/applications/plugins/signal_generator/views/signal_gen_pwm.c index b6ba47ab0..d625ed5a9 100644 --- a/applications/plugins/signal_generator/views/signal_gen_pwm.c +++ b/applications/plugins/signal_generator/views/signal_gen_pwm.c @@ -1,5 +1,5 @@ #include "../signal_gen_app_i.h" -#include "furi_hal.h" +#include #include #include diff --git a/applications/plugins/weather_station/application.fam b/applications/plugins/weather_station/application.fam index 1074fc040..769b6dd27 100644 --- a/applications/plugins/weather_station/application.fam +++ b/applications/plugins/weather_station/application.fam @@ -2,6 +2,7 @@ App( appid="weather_station", name="Weather Station", apptype=FlipperAppType.PLUGIN, + targets=["f7"], entry_point="weather_station_app", cdefines=["APP_WEATHER_STATION"], requires=["gui"], diff --git a/applications/plugins/weather_station/protocols/ws_generic.h b/applications/plugins/weather_station/protocols/ws_generic.h index 8e6e061ad..47cfa74b3 100644 --- a/applications/plugins/weather_station/protocols/ws_generic.h +++ b/applications/plugins/weather_station/protocols/ws_generic.h @@ -6,7 +6,7 @@ #include #include "furi.h" -#include "furi_hal.h" +#include #include #include diff --git a/applications/services/cli/cli_command_gpio.c b/applications/services/cli/cli_command_gpio.c index f0d487bec..d02462734 100644 --- a/applications/services/cli/cli_command_gpio.c +++ b/applications/services/cli/cli_command_gpio.c @@ -5,28 +5,6 @@ #include #include -typedef struct { - const GpioPin* pin; - const char* name; - const bool debug; -} CliCommandGpio; - -const CliCommandGpio cli_command_gpio_pins[] = { - {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, - {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, - {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, - {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, - {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, - {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, - {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, - {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, - /* Dangerous pins, may damage hardware */ - {.pin = &gpio_infrared_rx, .name = "PA0", .debug = true}, - {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, - {.pin = &gpio_speaker, .name = "PB8", .debug = true}, - {.pin = &gpio_infrared_tx, .name = "PB9", .debug = true}, -}; - void cli_command_gpio_print_usage() { printf("Usage:\r\n"); printf("gpio \r\n"); @@ -38,9 +16,9 @@ void cli_command_gpio_print_usage() { static bool pin_name_to_int(FuriString* pin_name, size_t* result) { bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(furi_string_equal(pin_name, cli_command_gpio_pins[i].name)) { - if(!cli_command_gpio_pins[i].debug || is_debug_mode) { + for(size_t i = 0; i < gpio_pins_count; i++) { + if(furi_string_equal(pin_name, gpio_pins[i].name)) { + if(!gpio_pins[i].debug || is_debug_mode) { *result = i; return true; } @@ -53,9 +31,9 @@ static bool pin_name_to_int(FuriString* pin_name, size_t* result) { static void gpio_print_pins(void) { printf("Wrong pin name. Available pins: "); bool is_debug_mode = furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug); - for(size_t i = 0; i < COUNT_OF(cli_command_gpio_pins); i++) { - if(!cli_command_gpio_pins[i].debug || is_debug_mode) { - printf("%s ", cli_command_gpio_pins[i].name); + for(size_t i = 0; i < gpio_pins_count; i++) { + if(!gpio_pins[i].debug || is_debug_mode) { + printf("%s ", gpio_pins[i].name); } } } @@ -113,7 +91,7 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { return; } - if(cli_command_gpio_pins[num].debug) { //-V779 + if(gpio_pins[num].debug) { //-V779 printf( "Changing this pin mode may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); @@ -124,12 +102,12 @@ void cli_command_gpio_mode(Cli* cli, FuriString* args, void* context) { } if(value == 1) { // output - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, false); - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeOutputPushPull); - printf("Pin %s is now an output (low)", cli_command_gpio_pins[num].name); + furi_hal_gpio_write(gpio_pins[num].pin, false); + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeOutputPushPull); + printf("Pin %s is now an output (low)", gpio_pins[num].name); } else { // input - furi_hal_gpio_init_simple(cli_command_gpio_pins[num].pin, GpioModeInput); - printf("Pin %s is now an input", cli_command_gpio_pins[num].name); + furi_hal_gpio_init_simple(gpio_pins[num].pin, GpioModeInput); + printf("Pin %s is now an input", gpio_pins[num].name); } } @@ -144,15 +122,14 @@ void cli_command_gpio_read(Cli* cli, FuriString* args, void* context) { } if(LL_GPIO_MODE_INPUT != //-V779 - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an input.", cli_command_gpio_pins[num].name); + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an input.", gpio_pins[num].name); return; } - uint8_t val = !!furi_hal_gpio_read(cli_command_gpio_pins[num].pin); + uint8_t val = !!furi_hal_gpio_read(gpio_pins[num].pin); - printf("Pin %s <= %u", cli_command_gpio_pins[num].name, val); + printf("Pin %s <= %u", gpio_pins[num].name, val); } void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { @@ -174,14 +151,13 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { } if(LL_GPIO_MODE_OUTPUT != //-V779 - LL_GPIO_GetPinMode( - cli_command_gpio_pins[num].pin->port, cli_command_gpio_pins[num].pin->pin)) { - printf("Err: pin %s is not set as an output.", cli_command_gpio_pins[num].name); + LL_GPIO_GetPinMode(gpio_pins[num].pin->port, gpio_pins[num].pin->pin)) { + printf("Err: pin %s is not set as an output.", gpio_pins[num].name); return; } // Extra check if debug pins used - if(cli_command_gpio_pins[num].debug) { + if(gpio_pins[num].debug) { printf( "Setting this pin may damage hardware. Are you sure you want to continue? (y/n)?\r\n"); char c = cli_getc(cli); @@ -191,8 +167,8 @@ void cli_command_gpio_set(Cli* cli, FuriString* args, void* context) { } } - furi_hal_gpio_write(cli_command_gpio_pins[num].pin, !!value); - printf("Pin %s => %u", cli_command_gpio_pins[num].name, !!value); + furi_hal_gpio_write(gpio_pins[num].pin, !!value); + printf("Pin %s => %u", gpio_pins[num].name, !!value); } void cli_command_gpio(Cli* cli, FuriString* args, void* context) { diff --git a/applications/services/dialogs/dialogs_api.c b/applications/services/dialogs/dialogs_api.c index 5e2b0683e..ca2435b9b 100644 --- a/applications/services/dialogs/dialogs_api.c +++ b/applications/services/dialogs/dialogs_api.c @@ -1,4 +1,4 @@ -#include "dialogs/dialogs_message.h" +#include "dialogs_message.h" #include "dialogs_i.h" #include #include diff --git a/applications/services/dialogs/dialogs_module_file_browser.c b/applications/services/dialogs/dialogs_module_file_browser.c index 8d486dbaf..79d151c59 100644 --- a/applications/services/dialogs/dialogs_module_file_browser.c +++ b/applications/services/dialogs/dialogs_module_file_browser.c @@ -1,6 +1,7 @@ #include "dialogs_i.h" + +#include #include -#include "gui/modules/file_browser.h" typedef struct { FuriApiLock lock; diff --git a/applications/services/dolphin/dolphin.h b/applications/services/dolphin/dolphin.h index 41a6a6089..8757e2a37 100644 --- a/applications/services/dolphin/dolphin.h +++ b/applications/services/dolphin/dolphin.h @@ -1,8 +1,9 @@ #pragma once -#include -#include "gui/view.h" #include "helpers/dolphin_deed.h" + +#include +#include #include #ifdef __cplusplus diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index e22889bf9..54c36af76 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -1,16 +1,17 @@ #include "elements.h" -#include "m-core.h" +#include #include -#include "furi_hal_resources.h" +#include #include -#include "gui/canvas.h" +#include #include #include #include #include "canvas_i.h" +#include #include #include #include diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index b0903e021..af5cf862d 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,4 +1,3 @@ -#include "gui/canvas.h" #include "gui_i.h" #include diff --git a/applications/services/gui/modules/button_menu.c b/applications/services/gui/modules/button_menu.c index 427a3e1ae..6ac786c92 100644 --- a/applications/services/gui/modules/button_menu.c +++ b/applications/services/gui/modules/button_menu.c @@ -1,12 +1,15 @@ #include "button_menu.h" -#include "gui/canvas.h" -#include "gui/elements.h" -#include "input/input.h" -#include + +#include +#include +#include + #include -#include #include +#include +#include + #define ITEM_FIRST_OFFSET 17 #define ITEM_NEXT_OFFSET 4 #define ITEM_HEIGHT 14 diff --git a/applications/services/gui/modules/button_panel.c b/applications/services/gui/modules/button_panel.c index 8f29c6542..9b816eed0 100644 --- a/applications/services/gui/modules/button_panel.c +++ b/applications/services/gui/modules/button_panel.c @@ -1,12 +1,15 @@ #include "button_panel.h" -#include "furi_hal_resources.h" -#include "gui/canvas.h" + +#include +#include + +#include +#include +#include + #include #include #include -#include -#include -#include typedef struct { // uint16_t to support multi-screen, wide button panel diff --git a/applications/services/gui/modules/byte_input.c b/applications/services/gui/modules/byte_input.c index 82de129f5..b2d21f7ae 100644 --- a/applications/services/gui/modules/byte_input.c +++ b/applications/services/gui/modules/byte_input.c @@ -1,8 +1,9 @@ -#include -#include -#include #include "byte_input.h" +#include +#include +#include + struct ByteInput { View* view; }; diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index e03a032c1..d12a00ba5 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -1,14 +1,17 @@ #include "file_browser.h" -#include "assets_icons.h" #include "file_browser_worker.h" + +#include +#include +#include + +#include +#include + #include #include #include -#include "furi_hal_resources.h" #include -#include -#include -#include "toolbox/path.h" #define LIST_ITEMS 5u #define MAX_LEN_PX 110 diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index a97a4d71a..80c4f4c43 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -1,13 +1,16 @@ #include "file_browser_worker.h" + +#include +#include + +#include #include #include -#include "storage/filesystem_api_defines.h" +#include + #include #include -#include -#include #include -#include "toolbox/path.h" #define TAG "BrowserWorker" diff --git a/applications/services/gui/modules/loading.c b/applications/services/gui/modules/loading.c index 0fa2c1286..e64aeb78f 100644 --- a/applications/services/gui/modules/loading.c +++ b/applications/services/gui/modules/loading.c @@ -1,13 +1,14 @@ -#include -#include -#include +#include "loading.h" + #include #include #include #include #include -#include "loading.h" +#include +#include +#include struct Loading { View* view; diff --git a/applications/services/gui/modules/menu.c b/applications/services/gui/modules/menu.c index 6983e0108..3e3b6c2e4 100644 --- a/applications/services/gui/modules/menu.c +++ b/applications/services/gui/modules/menu.c @@ -1,9 +1,9 @@ #include "menu.h" -#include #include #include #include +#include struct Menu { View* view; diff --git a/applications/services/gui/modules/submenu.c b/applications/services/gui/modules/submenu.c index 72626c587..00e4d68b5 100644 --- a/applications/services/gui/modules/submenu.c +++ b/applications/services/gui/modules/submenu.c @@ -1,8 +1,8 @@ #include "submenu.h" -#include #include #include +#include struct Submenu { View* view; diff --git a/applications/services/gui/modules/text_box.c b/applications/services/gui/modules/text_box.c index 079a1294d..01ccdbf52 100644 --- a/applications/services/gui/modules/text_box.c +++ b/applications/services/gui/modules/text_box.c @@ -1,7 +1,7 @@ #include "text_box.h" -#include "gui/canvas.h" -#include +#include #include +#include #include struct TextBox { diff --git a/applications/services/gui/modules/validators.c b/applications/services/gui/modules/validators.c index 9c5d0be84..a18cb925f 100644 --- a/applications/services/gui/modules/validators.c +++ b/applications/services/gui/modules/validators.c @@ -1,6 +1,6 @@ -#include #include "validators.h" #include +#include struct ValidatorIsFile { char* app_path_folder; diff --git a/applications/services/gui/modules/validators.h b/applications/services/gui/modules/validators.h index d9200b6db..e509fd833 100644 --- a/applications/services/gui/modules/validators.h +++ b/applications/services/gui/modules/validators.h @@ -1,6 +1,7 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { diff --git a/applications/services/gui/modules/variable_item_list.c b/applications/services/gui/modules/variable_item_list.c index 5060985e1..cf7f64ca3 100644 --- a/applications/services/gui/modules/variable_item_list.c +++ b/applications/services/gui/modules/variable_item_list.c @@ -1,8 +1,8 @@ #include "variable_item_list.h" -#include "gui/canvas.h" -#include -#include #include +#include +#include +#include #include struct VariableItem { diff --git a/applications/services/gui/modules/widget.c b/applications/services/gui/modules/widget.c index 6153b425b..21b8e5616 100644 --- a/applications/services/gui/modules/widget.c +++ b/applications/services/gui/modules/widget.c @@ -1,7 +1,7 @@ -#include #include "widget.h" -#include #include "widget_elements/widget_element_i.h" +#include +#include ARRAY_DEF(ElementArray, WidgetElement*, M_PTR_OPLIST); diff --git a/applications/services/gui/view_stack.c b/applications/services/gui/view_stack.c index 3bb00fbf2..0a106f1ba 100644 --- a/applications/services/gui/view_stack.c +++ b/applications/services/gui/view_stack.c @@ -1,8 +1,9 @@ -#include "gui/view.h" -#include #include "view_stack.h" #include "view_i.h" +#include +#include + #define MAX_VIEWS 3 typedef struct { diff --git a/applications/services/notification/notification_app.c b/applications/services/notification/notification_app.c index b6579f547..2e170f547 100644 --- a/applications/services/notification/notification_app.c +++ b/applications/services/notification/notification_app.c @@ -1,4 +1,4 @@ -#include "furi_hal_light.h" +#include #include #include #include diff --git a/applications/services/notification/notification_messages.c b/applications/services/notification/notification_messages.c index d795c55d9..51f3533c3 100644 --- a/applications/services/notification/notification_messages.c +++ b/applications/services/notification/notification_messages.c @@ -1,4 +1,4 @@ -#include "furi_hal_resources.h" +#include #include "notification.h" #include "notification_messages_notes.h" #include diff --git a/applications/settings/about/about.c b/applications/settings/about/about.c index 560a683cf..61c724966 100644 --- a/applications/settings/about/about.c +++ b/applications/settings/about/about.c @@ -13,17 +13,29 @@ typedef DialogMessageButton (*AboutDialogScreen)(DialogsApp* dialogs, DialogMess static DialogMessageButton product_screen(DialogsApp* dialogs, DialogMessage* message) { DialogMessageButton result; - const char* screen_header = "Product: Flipper Zero\n" - "Model: FZ.1\n"; - const char* screen_text = "FCC ID: 2A2V6-FZ\n" - "IC: 27624-FZ"; + FuriString* screen_header = furi_string_alloc_printf( + "Product: %s\n" + "Model: %s", + furi_hal_version_get_model_name(), + furi_hal_version_get_model_code()); - dialog_message_set_header(message, screen_header, 0, 0, AlignLeft, AlignTop); - dialog_message_set_text(message, screen_text, 0, 26, AlignLeft, AlignTop); + FuriString* screen_text = furi_string_alloc_printf( + "FCC ID: %s\n" + "IC: %s", + furi_hal_version_get_fcc_id(), + furi_hal_version_get_ic_id()); + + dialog_message_set_header( + message, furi_string_get_cstr(screen_header), 0, 0, AlignLeft, AlignTop); + dialog_message_set_text( + message, furi_string_get_cstr(screen_text), 0, 26, AlignLeft, AlignTop); result = dialog_message_show(dialogs, message); dialog_message_set_header(message, NULL, 0, 0, AlignLeft, AlignTop); dialog_message_set_text(message, NULL, 0, 0, AlignLeft, AlignTop); + furi_string_free(screen_header); + furi_string_free(screen_text); + return result; } diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c index 964736b66..31921b9f3 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_confirm.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_scene_forget_dev_confirm_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index c0b0c80a9..481ba6d5c 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { BtSettingsApp* app = context; diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c index bcbc902b2..5db98e9de 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_start.c @@ -1,5 +1,5 @@ #include "../bt_settings_app.h" -#include "furi_hal_bt.h" +#include enum BtSetting { BtSettingOff, diff --git a/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c index 71ce5a7c3..08c9e2d7f 100644 --- a/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c +++ b/applications/system/storage_move_to_sd/scenes/storage_move_to_sd_scene_confirm.c @@ -1,7 +1,7 @@ #include "../storage_move_to_sd.h" -#include "gui/canvas.h" -#include "gui/modules/widget_elements/widget_element_i.h" -#include "storage/storage.h" +#include +#include +#include static void storage_move_to_sd_scene_confirm_widget_callback( GuiButtonType result, diff --git a/applications/system/storage_move_to_sd/storage_move_to_sd.h b/applications/system/storage_move_to_sd/storage_move_to_sd.h index a62d87c1f..135f3e9b0 100644 --- a/applications/system/storage_move_to_sd/storage_move_to_sd.h +++ b/applications/system/storage_move_to_sd/storage_move_to_sd.h @@ -1,17 +1,16 @@ #pragma once -#include "gui/modules/widget_elements/widget_element_i.h" -#include #include #include #include #include -#include - #include #include +#include +#include #include #include +#include #include "scenes/storage_move_to_sd_scene.h" diff --git a/applications/system/updater/cli/updater_cli.c b/applications/system/updater/cli/updater_cli.c index f8e21ca3e..2bf6dab26 100644 --- a/applications/system/updater/cli/updater_cli.c +++ b/applications/system/updater/cli/updater_cli.c @@ -76,8 +76,8 @@ static void updater_cli_ep(Cli* cli, FuriString* args, void* context) { for(size_t idx = 0; idx < COUNT_OF(update_cli_subcommands); ++idx) { const CliSubcommand* subcmd_def = &update_cli_subcommands[idx]; if(furi_string_cmp_str(subcommand, subcmd_def->command) == 0) { - furi_string_free(subcommand); subcmd_def->handler(args); + furi_string_free(subcommand); return; } } diff --git a/applications/system/updater/util/update_task.c b/applications/system/updater/util/update_task.c index b43a6df16..54fe27995 100644 --- a/applications/system/updater/util/update_task.c +++ b/applications/system/updater/util/update_task.c @@ -287,7 +287,9 @@ bool update_task_parse_manifest(UpdateTask* update_task) { } update_task_set_progress(update_task, UpdateTaskStageProgress, 50); - if(manifest->target != furi_hal_version_get_hw_target()) { + /* Check target only if it's set - skipped for pre-production samples */ + if(furi_hal_version_get_hw_target() && + (manifest->target != furi_hal_version_get_hw_target())) { break; } diff --git a/documentation/HardwareTargets.md b/documentation/HardwareTargets.md new file mode 100644 index 000000000..0c3474839 --- /dev/null +++ b/documentation/HardwareTargets.md @@ -0,0 +1,44 @@ +## What a Firmware Target is + +Flipper's firmware is modular and supports different hardware configurations in a common code base. It encapsulates hardware-specific differences in `furi_hal`, board initialization code, linker files, SDK data and other information in a _target definition_. + +Target-specific files are placed in a single sub-folder in `firmware/targets`. It must contain a target definition file, `target.json`, and may contain other files if they are referenced by current target's definition. By default, `fbt` gathers all source files in target folder, unless they are explicitly excluded. + +Targets can inherit most code parts from other targets, to reduce common code duplication. + + +## Target Definition File + +A target definition file, `target.json`, is a JSON file that can contain the following fields: + +* `include_paths`: list of strings, folder paths relative to current target folder to add to global C/C++ header path lookup list. +* `sdk_header_paths`: list of strings, folder paths relative to current target folder to gather headers from for including in SDK. +* `startup_script`: filename of a startup script, performing initial hardware initialization. +* `linker_script_flash`: filename of a linker script for creating the main firmware image. +* `linker_script_ram`: filename of a linker script to use in "updater" build configuration. +* `linker_script_app`: filename of a linker script to use for linking .fap files. +* `sdk_symbols`: filename of a .csv file containing current SDK configuration for this target. +* `linker_dependencies`: list of libraries to link the firmware with. Note that those not in the list won't be built by `fbt`. Also several link passes might be needed, in such case you may need to specify same library name twice. +* `inherit`: string, specifies hardware target to borrow main configuration from. Current configuration may specify additional values for parameters that are lists of strings, or override values that are not lists. +* `excluded_sources`: list of filenames from the inherited configuration(s) NOT to be built. +* `excluded_headers`: list of headers from the inherited configuration(s) NOT to be included in generated SDK. +* `excluded_modules`: list of strings specifying fbt library (module) names to exclude from being used to configure build environment. + + +## Applications & Hardware + +Not all applications are available on different hardware targets. + +* For applications built into the firmware, you have to specify a compatible application set using `FIRMWARE_APP_SET=...` fbt option. See [fbt docs](./fbt.md#firmware-application-set) for details on build configurations. + +* For applications built as external .faps, you have to explicitly specify compatible targets in application's manifest, `application.fam`. For example, to limit application to a single target, add `targets=["f7"],` to the manifest. It won't be built for other targets. + +For details on application manifests, check out [their docs page](./AppManifests.md). + + +## Building Firmware for a Specific Target + +You have to specify TARGET_HW (and, optionally, FIRMWARE_APP_SET) for `fbt` to build firmware for non-default target. For example, building and flashing debug firmware for f18 can be done with + + ./fbt TARGET_HW=18 flash_usb_full + diff --git a/firmware.scons b/firmware.scons index 3922c136e..92f2d1a91 100644 --- a/firmware.scons +++ b/firmware.scons @@ -16,6 +16,7 @@ env = ENV.Clone( "fwbin", "fbt_apps", "pvsstudio", + "fbt_hwtarget", ], COMPILATIONDB_USE_ABSPATH=False, BUILD_DIR=fw_build_meta["build_dir"], @@ -31,10 +32,6 @@ env = ENV.Clone( CPPPATH=[ "#/furi", *(f"#/{app_dir[0]}" for app_dir in ENV["APPDIRS"] if app_dir[1]), - "#/firmware/targets/f${TARGET_HW}/ble_glue", - "#/firmware/targets/f${TARGET_HW}/fatfs", - "#/firmware/targets/f${TARGET_HW}/furi_hal", - "#/firmware/targets/f${TARGET_HW}/Inc", "#/firmware/targets/furi_hal_include", ], # Specific flags for building libraries - always do optimized builds @@ -74,20 +71,6 @@ env = ENV.Clone( _APP_ICONS=None, ) - -def ApplyLibFlags(env): - flags_to_apply = env["FW_LIB_OPTS"].get( - env.get("FW_LIB_NAME"), - env["FW_LIB_OPTS"]["Default"], - ) - # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) - env.MergeFlags(flags_to_apply) - - -env.AddMethod(ApplyLibFlags) - -Export("env") - if env["IS_BASE_FIRMWARE"]: env.Append( FIRMWARE_BUILD_CFG="firmware", @@ -102,7 +85,11 @@ else: ], ) -# Invoke child SConscripts to populate global `env` + build their own part of the code +env.ConfigureForTarget(env.subst("${TARGET_HW}")) + +Export("env") + +# Invoke child SCopscripts to populate global `env` + build their own part of the code lib_targets = env.BuildModules( [ "lib", @@ -131,7 +118,7 @@ if extra_int_apps := GetOption("extra_int_apps"): fwenv.Append(APPS=extra_int_apps.split(",")) -for app_dir, _ in env["APPDIRS"]: +for app_dir, _ in fwenv["APPDIRS"]: app_dir_node = env.Dir("#").Dir(app_dir) for entry in app_dir_node.glob("*"): @@ -196,37 +183,11 @@ sources.extend( fwelf = fwenv["FW_ELF"] = fwenv.Program( "${FIRMWARE_BUILD_CFG}", sources, - LIBS=[ - "print", - "flipper${TARGET_HW}", - "furi", - "freertos", - "stm32cubewb", - "hwdrivers", - "fatfs", - "littlefs", - "subghz", - "flipperformat", - "toolbox", - "nfc", - "microtar", - "usb_stm32", - "st25rfal002", - "infrared", - "appframe", - "assets", - "misc", - "mbedtls", - "lfrfid", - "flipper_application", - # 2nd round - "flipperformat", - "toolbox", - ], + LIBS=fwenv["TARGET_CFG"].linker_dependencies, ) # Firmware depends on everything child builders returned -Depends(fwelf, lib_targets) +# Depends(fwelf, lib_targets) # Output extra details after building firmware AddPostAction(fwelf, fwenv["APPBUILD_DUMP"]) AddPostAction( diff --git a/firmware/SConscript b/firmware/SConscript index a16f14e65..fa96b0adf 100644 --- a/firmware/SConscript +++ b/firmware/SConscript @@ -2,15 +2,6 @@ Import("env") env.Append( LINT_SOURCES=[Dir(".")], - SDK_HEADERS=[ - *env.GlobRecursive("*.h", "targets/furi_hal_include", "*_i.h"), - *env.GlobRecursive("*.h", "targets/f${TARGET_HW}/furi_hal", "*_i.h"), - File("targets/f7/platform_specific/intrinsic_export.h"), - ], -) - -env.SetDefault( - SDK_DEFINITION=env.File("./targets/f${TARGET_HW}/api_symbols.csv").srcnode() ) libenv = env.Clone(FW_LIB_NAME="flipper${TARGET_HW}") @@ -22,9 +13,9 @@ libenv.Append( libenv.ApplyLibFlags() -sources = ["targets/f${TARGET_HW}/startup_stm32wb55xx_cm4.s"] -sources += libenv.GlobRecursive("*.c") - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +lib = libenv.StaticLibrary( + "${FW_LIB_NAME}", + env.get("TARGET_CFG").gatherSources(), +) libenv.Install("${LIB_DIST_DIR}", lib) Return("lib") diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv new file mode 100644 index 000000000..e4a7dfdd5 --- /dev/null +++ b/firmware/targets/f18/api_symbols.csv @@ -0,0 +1,2307 @@ +entry,status,name,type,params +Version,+,12.1,, +Header,+,applications/services/bt/bt_service/bt.h,, +Header,+,applications/services/cli/cli.h,, +Header,+,applications/services/cli/cli_vcp.h,, +Header,+,applications/services/dialogs/dialogs.h,, +Header,+,applications/services/dolphin/dolphin.h,, +Header,+,applications/services/gui/elements.h,, +Header,+,applications/services/gui/gui.h,, +Header,+,applications/services/gui/icon_i.h,, +Header,+,applications/services/gui/modules/button_menu.h,, +Header,+,applications/services/gui/modules/button_panel.h,, +Header,+,applications/services/gui/modules/byte_input.h,, +Header,+,applications/services/gui/modules/dialog_ex.h,, +Header,+,applications/services/gui/modules/empty_screen.h,, +Header,+,applications/services/gui/modules/file_browser.h,, +Header,+,applications/services/gui/modules/file_browser_worker.h,, +Header,+,applications/services/gui/modules/loading.h,, +Header,+,applications/services/gui/modules/menu.h,, +Header,+,applications/services/gui/modules/popup.h,, +Header,+,applications/services/gui/modules/submenu.h,, +Header,+,applications/services/gui/modules/text_box.h,, +Header,+,applications/services/gui/modules/text_input.h,, +Header,+,applications/services/gui/modules/validators.h,, +Header,+,applications/services/gui/modules/variable_item_list.h,, +Header,+,applications/services/gui/modules/widget.h,, +Header,+,applications/services/gui/modules/widget_elements/widget_element.h,, +Header,+,applications/services/gui/view_dispatcher.h,, +Header,+,applications/services/gui/view_stack.h,, +Header,+,applications/services/input/input.h,, +Header,+,applications/services/loader/loader.h,, +Header,+,applications/services/locale/locale.h,, +Header,+,applications/services/notification/notification.h,, +Header,+,applications/services/notification/notification_messages.h,, +Header,+,applications/services/power/power_service/power.h,, +Header,+,applications/services/rpc/rpc_app.h,, +Header,+,applications/services/storage/storage.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_resources.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_spi_config.h,, +Header,+,firmware/targets/f18/furi_hal/furi_hal_target_hw.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_clock.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_console.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, +Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt_hid.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_bt_serial.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_compress.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_i2c.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_info.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_light.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_memory.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_mpu.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_power.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_random.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_region.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_rtc.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_version.h,, +Header,+,firmware/targets/furi_hal_include/furi_hal_vibro.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_adc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_bus.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_comp.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_cortex.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_crc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_crs.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_dma.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_dmamux.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_exti.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_gpio.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_hsem.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_i2c.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_ipcc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_iwdg.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_lptim.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_lpuart.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_pka.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_pwr.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rcc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rng.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_rtc.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_spi.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_system.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_tim.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_usart.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_utils.h,, +Header,+,lib/STM32CubeWB/Drivers/STM32WBxx_HAL_Driver/Inc/stm32wbxx_ll_wwdg.h,, +Header,+,lib/flipper_application/flipper_application.h,, +Header,+,lib/flipper_format/flipper_format.h,, +Header,+,lib/flipper_format/flipper_format_i.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_button.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_consumer.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_desktop.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_device.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_game.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_keyboard.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_led.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_ordinal.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_power.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_simulation.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_sport.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_telephony.h,, +Header,+,lib/libusb_stm32/inc/hid_usage_vr.h,, +Header,-,lib/libusb_stm32/inc/stm32_compat.h,, +Header,+,lib/libusb_stm32/inc/usb.h,, +Header,+,lib/libusb_stm32/inc/usb_cdc.h,, +Header,+,lib/libusb_stm32/inc/usb_cdca.h,, +Header,+,lib/libusb_stm32/inc/usb_cdce.h,, +Header,+,lib/libusb_stm32/inc/usb_cdci.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcp.h,, +Header,+,lib/libusb_stm32/inc/usb_cdcw.h,, +Header,+,lib/libusb_stm32/inc/usb_dfu.h,, +Header,+,lib/libusb_stm32/inc/usb_hid.h,, +Header,+,lib/libusb_stm32/inc/usb_std.h,, +Header,+,lib/libusb_stm32/inc/usb_tmc.h,, +Header,+,lib/libusb_stm32/inc/usbd_core.h,, +Header,+,lib/mbedtls/include/mbedtls/des.h,, +Header,+,lib/mbedtls/include/mbedtls/sha1.h,, +Header,+,lib/micro-ecc/uECC.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, +Header,+,lib/print/wrappers.h,, +Header,+,lib/toolbox/args.h,, +Header,+,lib/toolbox/crc32_calc.h,, +Header,+,lib/toolbox/dir_walk.h,, +Header,+,lib/toolbox/float_tools.h,, +Header,+,lib/toolbox/hmac_sha256.h,, +Header,+,lib/toolbox/manchester_decoder.h,, +Header,+,lib/toolbox/manchester_encoder.h,, +Header,+,lib/toolbox/md5.h,, +Header,+,lib/toolbox/path.h,, +Header,+,lib/toolbox/protocols/protocol_dict.h,, +Header,+,lib/toolbox/random_name.h,, +Header,+,lib/toolbox/saved_struct.h,, +Header,+,lib/toolbox/stream/buffered_file_stream.h,, +Header,+,lib/toolbox/stream/file_stream.h,, +Header,+,lib/toolbox/stream/stream.h,, +Header,+,lib/toolbox/stream/string_stream.h,, +Header,+,lib/toolbox/tar/tar_archive.h,, +Header,+,lib/toolbox/value_index.h,, +Header,+,lib/toolbox/version.h,, +Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* +Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" +Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* +Function,-,LL_ADC_DeInit,ErrorStatus,ADC_TypeDef* +Function,-,LL_ADC_INJ_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_INJ_InitTypeDef*" +Function,-,LL_ADC_INJ_StructInit,void,LL_ADC_INJ_InitTypeDef* +Function,-,LL_ADC_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_InitTypeDef*" +Function,-,LL_ADC_REG_Init,ErrorStatus,"ADC_TypeDef*, LL_ADC_REG_InitTypeDef*" +Function,-,LL_ADC_REG_StructInit,void,LL_ADC_REG_InitTypeDef* +Function,-,LL_ADC_StructInit,void,LL_ADC_InitTypeDef* +Function,-,LL_COMP_DeInit,ErrorStatus,COMP_TypeDef* +Function,+,LL_COMP_Init,ErrorStatus,"COMP_TypeDef*, LL_COMP_InitTypeDef*" +Function,-,LL_COMP_StructInit,void,LL_COMP_InitTypeDef* +Function,-,LL_CRC_DeInit,ErrorStatus,CRC_TypeDef* +Function,-,LL_CRS_DeInit,ErrorStatus, +Function,+,LL_DMA_DeInit,ErrorStatus,"DMA_TypeDef*, uint32_t" +Function,+,LL_DMA_Init,ErrorStatus,"DMA_TypeDef*, uint32_t, LL_DMA_InitTypeDef*" +Function,-,LL_DMA_StructInit,void,LL_DMA_InitTypeDef* +Function,-,LL_EXTI_DeInit,ErrorStatus, +Function,-,LL_EXTI_Init,ErrorStatus,LL_EXTI_InitTypeDef* +Function,-,LL_EXTI_StructInit,void,LL_EXTI_InitTypeDef* +Function,-,LL_GPIO_DeInit,ErrorStatus,GPIO_TypeDef* +Function,+,LL_GPIO_Init,ErrorStatus,"GPIO_TypeDef*, LL_GPIO_InitTypeDef*" +Function,-,LL_GPIO_StructInit,void,LL_GPIO_InitTypeDef* +Function,-,LL_I2C_DeInit,ErrorStatus,I2C_TypeDef* +Function,+,LL_I2C_Init,ErrorStatus,"I2C_TypeDef*, LL_I2C_InitTypeDef*" +Function,-,LL_I2C_StructInit,void,LL_I2C_InitTypeDef* +Function,-,LL_Init1msTick,void,uint32_t +Function,+,LL_LPTIM_DeInit,ErrorStatus,LPTIM_TypeDef* +Function,-,LL_LPTIM_Disable,void,LPTIM_TypeDef* +Function,+,LL_LPTIM_Init,ErrorStatus,"LPTIM_TypeDef*, LL_LPTIM_InitTypeDef*" +Function,-,LL_LPTIM_StructInit,void,LL_LPTIM_InitTypeDef* +Function,-,LL_LPUART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_LPUART_Init,ErrorStatus,"USART_TypeDef*, LL_LPUART_InitTypeDef*" +Function,-,LL_LPUART_StructInit,void,LL_LPUART_InitTypeDef* +Function,-,LL_PKA_DeInit,ErrorStatus,PKA_TypeDef* +Function,-,LL_PKA_Init,ErrorStatus,"PKA_TypeDef*, LL_PKA_InitTypeDef*" +Function,-,LL_PKA_StructInit,void,LL_PKA_InitTypeDef* +Function,-,LL_PLL_ConfigSystemClock_HSE,ErrorStatus,"uint32_t, LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_HSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PLL_ConfigSystemClock_MSI,ErrorStatus,"LL_UTILS_PLLInitTypeDef*, LL_UTILS_ClkInitTypeDef*" +Function,-,LL_PWR_DeInit,ErrorStatus, +Function,-,LL_RCC_DeInit,ErrorStatus, +Function,-,LL_RCC_GetADCClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetCLK48ClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetI2CClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetLPTIMClockFreq,uint32_t,uint32_t +Function,+,LL_RCC_GetLPUARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRFWKPClockFreq,uint32_t, +Function,-,LL_RCC_GetRNGClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetRTCClockFreq,uint32_t, +Function,-,LL_RCC_GetSAIClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetSMPSClockFreq,uint32_t, +Function,-,LL_RCC_GetSystemClocksFreq,void,LL_RCC_ClocksTypeDef* +Function,+,LL_RCC_GetUSARTClockFreq,uint32_t,uint32_t +Function,-,LL_RCC_GetUSBClockFreq,uint32_t,uint32_t +Function,-,LL_RNG_DeInit,ErrorStatus,RNG_TypeDef* +Function,-,LL_RNG_Init,ErrorStatus,"RNG_TypeDef*, LL_RNG_InitTypeDef*" +Function,-,LL_RNG_StructInit,void,LL_RNG_InitTypeDef* +Function,-,LL_RTC_ALMA_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMA_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_ALMB_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_AlarmTypeDef*" +Function,-,LL_RTC_ALMB_StructInit,void,LL_RTC_AlarmTypeDef* +Function,-,LL_RTC_DATE_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_DateTypeDef*" +Function,-,LL_RTC_DATE_StructInit,void,LL_RTC_DateTypeDef* +Function,-,LL_RTC_DeInit,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_EnterInitMode,ErrorStatus,RTC_TypeDef* +Function,-,LL_RTC_ExitInitMode,ErrorStatus,RTC_TypeDef* +Function,+,LL_RTC_Init,ErrorStatus,"RTC_TypeDef*, LL_RTC_InitTypeDef*" +Function,-,LL_RTC_StructInit,void,LL_RTC_InitTypeDef* +Function,-,LL_RTC_TIME_Init,ErrorStatus,"RTC_TypeDef*, uint32_t, LL_RTC_TimeTypeDef*" +Function,-,LL_RTC_TIME_StructInit,void,LL_RTC_TimeTypeDef* +Function,-,LL_RTC_WaitForSynchro,ErrorStatus,RTC_TypeDef* +Function,-,LL_SPI_DeInit,ErrorStatus,SPI_TypeDef* +Function,+,LL_SPI_Init,ErrorStatus,"SPI_TypeDef*, LL_SPI_InitTypeDef*" +Function,-,LL_SPI_StructInit,void,LL_SPI_InitTypeDef* +Function,-,LL_SetFlashLatency,ErrorStatus,uint32_t +Function,+,LL_SetSystemCoreClock,void,uint32_t +Function,-,LL_TIM_BDTR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_BDTR_InitTypeDef*" +Function,-,LL_TIM_BDTR_StructInit,void,LL_TIM_BDTR_InitTypeDef* +Function,+,LL_TIM_DeInit,ErrorStatus,TIM_TypeDef* +Function,-,LL_TIM_ENCODER_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_ENCODER_InitTypeDef*" +Function,-,LL_TIM_ENCODER_StructInit,void,LL_TIM_ENCODER_InitTypeDef* +Function,-,LL_TIM_HALLSENSOR_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_HALLSENSOR_InitTypeDef*" +Function,-,LL_TIM_HALLSENSOR_StructInit,void,LL_TIM_HALLSENSOR_InitTypeDef* +Function,-,LL_TIM_IC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_IC_InitTypeDef*" +Function,-,LL_TIM_IC_StructInit,void,LL_TIM_IC_InitTypeDef* +Function,+,LL_TIM_Init,ErrorStatus,"TIM_TypeDef*, LL_TIM_InitTypeDef*" +Function,+,LL_TIM_OC_Init,ErrorStatus,"TIM_TypeDef*, uint32_t, LL_TIM_OC_InitTypeDef*" +Function,-,LL_TIM_OC_StructInit,void,LL_TIM_OC_InitTypeDef* +Function,-,LL_TIM_StructInit,void,LL_TIM_InitTypeDef* +Function,-,LL_USART_ClockInit,ErrorStatus,"USART_TypeDef*, LL_USART_ClockInitTypeDef*" +Function,-,LL_USART_ClockStructInit,void,LL_USART_ClockInitTypeDef* +Function,-,LL_USART_DeInit,ErrorStatus,USART_TypeDef* +Function,+,LL_USART_Init,ErrorStatus,"USART_TypeDef*, LL_USART_InitTypeDef*" +Function,-,LL_USART_StructInit,void,LL_USART_InitTypeDef* +Function,-,LL_mDelay,void,uint32_t +Function,-,SystemCoreClockUpdate,void, +Function,-,SystemInit,void, +Function,-,_Exit,void,int +Function,-,__assert,void,"const char*, int, const char*" +Function,+,__assert_func,void,"const char*, int, const char*, const char*" +Function,+,__clear_cache,void,"void*, void*" +Function,-,__eprintf,void,"const char*, const char*, unsigned int, const char*" +Function,+,__errno,int*, +Function,+,__furi_crash,void, +Function,+,__furi_halt,void, +Function,-,__getdelim,ssize_t,"char**, size_t*, int, FILE*" +Function,-,__getline,ssize_t,"char**, size_t*, FILE*" +Function,-,__itoa,char*,"int, char*, int" +Function,-,__locale_mb_cur_max,int, +Function,+,__retarget_lock_acquire,void,_LOCK_T +Function,+,__retarget_lock_acquire_recursive,void,_LOCK_T +Function,-,__retarget_lock_close,void,_LOCK_T +Function,+,__retarget_lock_close_recursive,void,_LOCK_T +Function,-,__retarget_lock_init,void,_LOCK_T* +Function,+,__retarget_lock_init_recursive,void,_LOCK_T* +Function,+,__retarget_lock_release,void,_LOCK_T +Function,+,__retarget_lock_release_recursive,void,_LOCK_T +Function,-,__retarget_lock_try_acquire,int,_LOCK_T +Function,-,__retarget_lock_try_acquire_recursive,int,_LOCK_T +Function,-,__srget_r,int,"_reent*, FILE*" +Function,-,__swbuf_r,int,"_reent*, int, FILE*" +Function,-,__utoa,char*,"unsigned, char*, int" +Function,+,__wrap___assert,void,"const char*, int, const char*" +Function,+,__wrap___assert_func,void,"const char*, int, const char*, const char*" +Function,+,__wrap_fflush,int,FILE* +Function,+,__wrap_printf,int,"const char*, ..." +Function,+,__wrap_putc,int,"int, FILE*" +Function,+,__wrap_putchar,int,int +Function,+,__wrap_puts,int,const char* +Function,+,__wrap_snprintf,int,"char*, size_t, const char*, ..." +Function,+,__wrap_vsnprintf,int,"char*, size_t, const char*, va_list" +Function,-,_asiprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_asniprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asnprintf_r,char*,"_reent*, char*, size_t*, const char*, ..." +Function,-,_asprintf_r,int,"_reent*, char**, const char*, ..." +Function,-,_atoi_r,int,"_reent*, const char*" +Function,-,_atol_r,long,"_reent*, const char*" +Function,-,_atoll_r,long long,"_reent*, const char*" +Function,-,_calloc_r,void*,"_reent*, size_t, size_t" +Function,-,_diprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_dprintf_r,int,"_reent*, int, const char*, ..." +Function,-,_drand48_r,double,_reent* +Function,-,_dtoa_r,char*,"_reent*, double, int, int, int*, int*, char**" +Function,-,_erand48_r,double,"_reent*, unsigned short[3]" +Function,-,_fclose_r,int,"_reent*, FILE*" +Function,-,_fcloseall_r,int,_reent* +Function,-,_fdopen_r,FILE*,"_reent*, int, const char*" +Function,-,_fflush_r,int,"_reent*, FILE*" +Function,-,_fgetc_r,int,"_reent*, FILE*" +Function,-,_fgetc_unlocked_r,int,"_reent*, FILE*" +Function,-,_fgetpos_r,int,"_reent*, FILE*, fpos_t*" +Function,-,_fgets_r,char*,"_reent*, char*, int, FILE*" +Function,-,_fgets_unlocked_r,char*,"_reent*, char*, int, FILE*" +Function,-,_findenv,char*,"const char*, int*" +Function,-,_findenv_r,char*,"_reent*, const char*, int*" +Function,-,_fiprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fiscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fmemopen_r,FILE*,"_reent*, void*, size_t, const char*" +Function,-,_fopen_r,FILE*,"_reent*, const char*, const char*" +Function,-,_fopencookie_r,FILE*,"_reent*, void*, const char*, cookie_io_functions_t" +Function,-,_fprintf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fpurge_r,int,"_reent*, FILE*" +Function,-,_fputc_r,int,"_reent*, int, FILE*" +Function,-,_fputc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_fputs_r,int,"_reent*, const char*, FILE*" +Function,-,_fputs_unlocked_r,int,"_reent*, const char*, FILE*" +Function,-,_fread_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_fread_unlocked_r,size_t,"_reent*, void*, size_t, size_t, FILE*" +Function,-,_free_r,void,"_reent*, void*" +Function,-,_freopen_r,FILE*,"_reent*, const char*, const char*, FILE*" +Function,-,_fscanf_r,int,"_reent*, FILE*, const char*, ..." +Function,-,_fseek_r,int,"_reent*, FILE*, long, int" +Function,-,_fseeko_r,int,"_reent*, FILE*, _off_t, int" +Function,-,_fsetpos_r,int,"_reent*, FILE*, const fpos_t*" +Function,-,_ftell_r,long,"_reent*, FILE*" +Function,-,_ftello_r,_off_t,"_reent*, FILE*" +Function,-,_funopen_r,FILE*,"_reent*, const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,-,_fwrite_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_fwrite_unlocked_r,size_t,"_reent*, const void*, size_t, size_t, FILE*" +Function,-,_getc_r,int,"_reent*, FILE*" +Function,-,_getc_unlocked_r,int,"_reent*, FILE*" +Function,-,_getchar_r,int,_reent* +Function,-,_getchar_unlocked_r,int,_reent* +Function,-,_getenv_r,char*,"_reent*, const char*" +Function,-,_gets_r,char*,"_reent*, char*" +Function,-,_iprintf_r,int,"_reent*, const char*, ..." +Function,-,_iscanf_r,int,"_reent*, const char*, ..." +Function,-,_jrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_l64a_r,char*,"_reent*, long" +Function,-,_lcong48_r,void,"_reent*, unsigned short[7]" +Function,-,_lrand48_r,long,_reent* +Function,-,_malloc_r,void*,"_reent*, size_t" +Function,-,_mblen_r,int,"_reent*, const char*, size_t, _mbstate_t*" +Function,-,_mbstowcs_r,size_t,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mbtowc_r,int,"_reent*, wchar_t*, const char*, size_t, _mbstate_t*" +Function,-,_mkdtemp_r,char*,"_reent*, char*" +Function,-,_mkostemp_r,int,"_reent*, char*, int" +Function,-,_mkostemps_r,int,"_reent*, char*, int, int" +Function,-,_mkstemp_r,int,"_reent*, char*" +Function,-,_mkstemps_r,int,"_reent*, char*, int" +Function,-,_mktemp_r,char*,"_reent*, char*" +Function,-,_mrand48_r,long,_reent* +Function,-,_mstats_r,void,"_reent*, char*" +Function,-,_nrand48_r,long,"_reent*, unsigned short[3]" +Function,-,_open_memstream_r,FILE*,"_reent*, char**, size_t*" +Function,-,_perror_r,void,"_reent*, const char*" +Function,-,_printf_r,int,"_reent*, const char*, ..." +Function,-,_putc_r,int,"_reent*, int, FILE*" +Function,-,_putc_unlocked_r,int,"_reent*, int, FILE*" +Function,-,_putchar,void,char +Function,-,_putchar_r,int,"_reent*, int" +Function,-,_putchar_unlocked_r,int,"_reent*, int" +Function,-,_putenv_r,int,"_reent*, char*" +Function,-,_puts_r,int,"_reent*, const char*" +Function,-,_realloc_r,void*,"_reent*, void*, size_t" +Function,-,_reallocf_r,void*,"_reent*, void*, size_t" +Function,-,_reclaim_reent,void,_reent* +Function,-,_remove_r,int,"_reent*, const char*" +Function,-,_rename_r,int,"_reent*, const char*, const char*" +Function,-,_rewind_r,void,"_reent*, FILE*" +Function,-,_scanf_r,int,"_reent*, const char*, ..." +Function,-,_seed48_r,unsigned short*,"_reent*, unsigned short[3]" +Function,-,_setenv_r,int,"_reent*, const char*, const char*, int" +Function,-,_siprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_siscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_sniprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_snprintf_r,int,"_reent*, char*, size_t, const char*, ..." +Function,-,_sprintf_r,int,"_reent*, char*, const char*, ..." +Function,-,_srand48_r,void,"_reent*, long" +Function,-,_sscanf_r,int,"_reent*, const char*, const char*, ..." +Function,-,_strdup_r,char*,"_reent*, const char*" +Function,-,_strerror_r,char*,"_reent*, int, int, int*" +Function,-,_strndup_r,char*,"_reent*, const char*, size_t" +Function,-,_strtod_r,double,"_reent*, const char*, char**" +Function,-,_strtol_r,long,"_reent*, const char*, char**, int" +Function,-,_strtold_r,long double,"_reent*, const char*, char**" +Function,-,_strtoll_r,long long,"_reent*, const char*, char**, int" +Function,-,_strtoul_r,unsigned long,"_reent*, const char*, char**, int" +Function,-,_strtoull_r,unsigned long long,"_reent*, const char*, char**, int" +Function,-,_system_r,int,"_reent*, const char*" +Function,-,_tempnam_r,char*,"_reent*, const char*, const char*" +Function,-,_tmpfile_r,FILE*,_reent* +Function,-,_tmpnam_r,char*,"_reent*, char*" +Function,-,_tzset_r,void,_reent* +Function,-,_ungetc_r,int,"_reent*, int, FILE*" +Function,-,_unsetenv_r,int,"_reent*, const char*" +Function,-,_vasiprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vasniprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasnprintf_r,char*,"_reent*, char*, size_t*, const char*, __gnuc_va_list" +Function,-,_vasprintf_r,int,"_reent*, char**, const char*, __gnuc_va_list" +Function,-,_vdiprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vdprintf_r,int,"_reent*, int, const char*, __gnuc_va_list" +Function,-,_vfiprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfiscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfprintf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_vfscanf_r,int,"_reent*, FILE*, const char*, __gnuc_va_list" +Function,-,_viprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_viscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vprintf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vscanf_r,int,"_reent*, const char*, __gnuc_va_list" +Function,-,_vsiprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsiscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_vsniprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsnprintf_r,int,"_reent*, char*, size_t, const char*, __gnuc_va_list" +Function,-,_vsprintf_r,int,"_reent*, char*, const char*, __gnuc_va_list" +Function,-,_vsscanf_r,int,"_reent*, const char*, const char*, __gnuc_va_list" +Function,-,_wcstombs_r,size_t,"_reent*, char*, const wchar_t*, size_t, _mbstate_t*" +Function,-,_wctomb_r,int,"_reent*, char*, wchar_t, _mbstate_t*" +Function,-,a64l,long,const char* +Function,+,abort,void, +Function,-,abs,int,int +Function,+,acquire_mutex,void*,"ValueMutex*, uint32_t" +Function,-,aligned_alloc,void*,"size_t, size_t" +Function,+,aligned_free,void,void* +Function,+,aligned_malloc,void*,"size_t, size_t" +Function,-,arc4random,__uint32_t, +Function,-,arc4random_buf,void,"void*, size_t" +Function,-,arc4random_uniform,__uint32_t,__uint32_t +Function,+,args_char_to_hex,_Bool,"char, char, uint8_t*" +Function,+,args_get_first_word_length,size_t,FuriString* +Function,+,args_length,size_t,FuriString* +Function,+,args_read_hex_bytes,_Bool,"FuriString*, uint8_t*, size_t" +Function,+,args_read_int_and_trim,_Bool,"FuriString*, int*" +Function,+,args_read_probably_quoted_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,+,args_read_string_and_trim,_Bool,"FuriString*, FuriString*" +Function,-,asctime,char*,const tm* +Function,-,asctime_r,char*,"const tm*, char*" +Function,-,asiprintf,int,"char**, const char*, ..." +Function,-,asniprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asnprintf,char*,"char*, size_t*, const char*, ..." +Function,-,asprintf,int,"char**, const char*, ..." +Function,-,at_quick_exit,int,void (*)() +Function,-,atexit,int,void (*)() +Function,-,atof,double,const char* +Function,-,atoff,float,const char* +Function,+,atoi,int,const char* +Function,-,atol,long,const char* +Function,-,atoll,long long,const char* +Function,-,basename,char*,const char* +Function,-,bcmp,int,"const void*, const void*, size_t" +Function,-,bcopy,void,"const void*, void*, size_t" +Function,+,ble_app_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,ble_app_init,_Bool, +Function,+,ble_app_thread_stop,void, +Function,+,ble_glue_force_c2_mode,BleGlueCommandResult,BleGlueC2Mode +Function,-,ble_glue_fus_get_status,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_delete,BleGlueCommandResult, +Function,-,ble_glue_fus_stack_install,BleGlueCommandResult,"uint32_t, uint32_t" +Function,-,ble_glue_fus_wait_operation,BleGlueCommandResult, +Function,+,ble_glue_get_c2_info,const BleGlueC2Info*, +Function,-,ble_glue_get_c2_status,BleGlueStatus, +Function,+,ble_glue_init,void, +Function,+,ble_glue_is_alive,_Bool, +Function,+,ble_glue_is_radio_stack_ready,_Bool, +Function,+,ble_glue_reinit_c2,_Bool, +Function,+,ble_glue_set_key_storage_changed_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,ble_glue_start,_Bool, +Function,+,ble_glue_thread_stop,void, +Function,+,ble_glue_wait_for_c2_start,_Bool,int32_t +Function,-,bsearch,void*,"const void*, const void*, size_t, size_t, __compar_fn_t" +Function,+,bt_disconnect,void,Bt* +Function,+,bt_forget_bonded_devices,void,Bt* +Function,+,bt_keys_storage_set_default_path,void,Bt* +Function,+,bt_keys_storage_set_storage_path,void,"Bt*, const char*" +Function,+,bt_set_profile,_Bool,"Bt*, BtProfile" +Function,+,bt_set_status_changed_callback,void,"Bt*, BtStatusChangedCallback, void*" +Function,+,buffered_file_stream_alloc,Stream*,Storage* +Function,+,buffered_file_stream_close,_Bool,Stream* +Function,+,buffered_file_stream_get_error,FS_Error,Stream* +Function,+,buffered_file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,buffered_file_stream_sync,_Bool,Stream* +Function,+,button_menu_add_item,ButtonMenuItem*,"ButtonMenu*, const char*, int32_t, ButtonMenuItemCallback, ButtonMenuItemType, void*" +Function,+,button_menu_alloc,ButtonMenu*, +Function,+,button_menu_free,void,ButtonMenu* +Function,+,button_menu_get_view,View*,ButtonMenu* +Function,+,button_menu_reset,void,ButtonMenu* +Function,+,button_menu_set_header,void,"ButtonMenu*, const char*" +Function,+,button_menu_set_selected_item,void,"ButtonMenu*, uint32_t" +Function,+,button_panel_add_item,void,"ButtonPanel*, uint32_t, uint16_t, uint16_t, uint16_t, uint16_t, const Icon*, const Icon*, ButtonItemCallback, void*" +Function,+,button_panel_add_label,void,"ButtonPanel*, uint16_t, uint16_t, Font, const char*" +Function,+,button_panel_alloc,ButtonPanel*, +Function,+,button_panel_free,void,ButtonPanel* +Function,+,button_panel_get_view,View*,ButtonPanel* +Function,+,button_panel_reserve,void,"ButtonPanel*, size_t, size_t" +Function,+,button_panel_reset,void,ButtonPanel* +Function,+,byte_input_alloc,ByteInput*, +Function,+,byte_input_free,void,ByteInput* +Function,+,byte_input_get_view,View*,ByteInput* +Function,+,byte_input_set_header_text,void,"ByteInput*, const char*" +Function,+,byte_input_set_result_callback,void,"ByteInput*, ByteInputCallback, ByteChangedCallback, void*, uint8_t*, uint8_t" +Function,-,bzero,void,"void*, size_t" +Function,-,calloc,void*,"size_t, size_t" +Function,+,canvas_clear,void,Canvas* +Function,+,canvas_commit,void,Canvas* +Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" +Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_disc,void,"Canvas*, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_dot,void,"Canvas*, uint8_t, uint8_t" +Function,+,canvas_draw_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_glyph,void,"Canvas*, uint8_t, uint8_t, uint16_t" +Function,+,canvas_draw_icon,void,"Canvas*, uint8_t, uint8_t, const Icon*" +Function,+,canvas_draw_icon_animation,void,"Canvas*, uint8_t, uint8_t, IconAnimation*" +Function,+,canvas_draw_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rbox,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_rframe,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" +Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" +Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" +Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_invert_color,void,Canvas* +Function,+,canvas_reset,void,Canvas* +Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" +Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_font,void,"Canvas*, Font" +Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" +Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" +Function,+,canvas_width,uint8_t,Canvas* +Function,-,cfree,void,void* +Function,-,clearerr,void,FILE* +Function,-,clearerr_unlocked,void,FILE* +Function,+,cli_add_command,void,"Cli*, const char*, CliCommandFlag, CliCallback, void*" +Function,+,cli_cmd_interrupt_received,_Bool,Cli* +Function,+,cli_delete_command,void,"Cli*, const char*" +Function,+,cli_getc,char,Cli* +Function,+,cli_is_connected,_Bool,Cli* +Function,+,cli_nl,void, +Function,+,cli_print_usage,void,"const char*, const char*, const char*" +Function,+,cli_read,size_t,"Cli*, uint8_t*, size_t" +Function,+,cli_read_timeout,size_t,"Cli*, uint8_t*, size_t, uint32_t" +Function,+,cli_session_close,void,Cli* +Function,+,cli_session_open,void,"Cli*, void*" +Function,+,cli_write,void,"Cli*, const uint8_t*, size_t" +Function,-,clock,clock_t, +Function,+,crc32_calc_buffer,uint32_t,"uint32_t, const void*, size_t" +Function,+,crc32_calc_file,uint32_t,"File*, const FileCrcProgressCb, void*" +Function,-,ctermid,char*,char* +Function,-,ctime,char*,const time_t* +Function,-,ctime_r,char*,"const time_t*, char*" +Function,-,cuserid,char*,char* +Function,+,delete_mutex,_Bool,ValueMutex* +Function,+,dialog_ex_alloc,DialogEx*, +Function,+,dialog_ex_disable_extended_events,void,DialogEx* +Function,+,dialog_ex_enable_extended_events,void,DialogEx* +Function,+,dialog_ex_free,void,DialogEx* +Function,+,dialog_ex_get_view,View*,DialogEx* +Function,+,dialog_ex_reset,void,DialogEx* +Function,+,dialog_ex_set_center_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_context,void,"DialogEx*, void*" +Function,+,dialog_ex_set_header,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_ex_set_icon,void,"DialogEx*, uint8_t, uint8_t, const Icon*" +Function,+,dialog_ex_set_left_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_result_callback,void,"DialogEx*, DialogExResultCallback" +Function,+,dialog_ex_set_right_button_text,void,"DialogEx*, const char*" +Function,+,dialog_ex_set_text,void,"DialogEx*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_file_browser_set_basic_options,void,"DialogsFileBrowserOptions*, const char*, const Icon*" +Function,+,dialog_file_browser_show,_Bool,"DialogsApp*, FuriString*, FuriString*, const DialogsFileBrowserOptions*" +Function,+,dialog_message_alloc,DialogMessage*, +Function,+,dialog_message_free,void,DialogMessage* +Function,+,dialog_message_set_buttons,void,"DialogMessage*, const char*, const char*, const char*" +Function,+,dialog_message_set_header,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_set_icon,void,"DialogMessage*, const Icon*, uint8_t, uint8_t" +Function,+,dialog_message_set_text,void,"DialogMessage*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,dialog_message_show,DialogMessageButton,"DialogsApp*, const DialogMessage*" +Function,+,dialog_message_show_storage_error,void,"DialogsApp*, const char*" +Function,-,difftime,double,"time_t, time_t" +Function,-,diprintf,int,"int, const char*, ..." +Function,+,dir_walk_alloc,DirWalk*,Storage* +Function,+,dir_walk_close,void,DirWalk* +Function,+,dir_walk_free,void,DirWalk* +Function,+,dir_walk_get_error,FS_Error,DirWalk* +Function,+,dir_walk_open,_Bool,"DirWalk*, const char*" +Function,+,dir_walk_read,DirWalkResult,"DirWalk*, FuriString*, FileInfo*" +Function,+,dir_walk_set_filter_cb,void,"DirWalk*, DirWalkFilterCb, void*" +Function,+,dir_walk_set_recursive,void,"DirWalk*, _Bool" +Function,-,div,div_t,"int, int" +Function,+,dolphin_deed,void,"Dolphin*, DolphinDeed" +Function,+,dolphin_deed_get_app,DolphinApp,DolphinDeed +Function,+,dolphin_deed_get_app_limit,uint8_t,DolphinApp +Function,+,dolphin_deed_get_weight,uint8_t,DolphinDeed +Function,+,dolphin_flush,void,Dolphin* +Function,+,dolphin_get_pubsub,FuriPubSub*,Dolphin* +Function,+,dolphin_stats,DolphinStats,Dolphin* +Function,+,dolphin_upgrade_level,void,Dolphin* +Function,-,dprintf,int,"int, const char*, ..." +Function,-,drand48,double, +Function,-,eTaskConfirmSleepModeStatus,eSleepModeStatus, +Function,-,eTaskGetState,eTaskState,TaskHandle_t +Function,+,elements_bold_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_bubble_str,void,"Canvas*, uint8_t, uint8_t, const char*, Align, Align" +Function,+,elements_button_center,void,"Canvas*, const char*" +Function,+,elements_button_left,void,"Canvas*, const char*" +Function,+,elements_button_right,void,"Canvas*, const char*" +Function,+,elements_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_multiline_text,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_multiline_text_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" +Function,+,elements_multiline_text_framed,void,"Canvas*, uint8_t, uint8_t, const char*" +Function,+,elements_progress_bar,void,"Canvas*, uint8_t, uint8_t, uint8_t, float" +Function,+,elements_progress_bar_with_text,void,"Canvas*, uint8_t, uint8_t, uint8_t, float, const char*" +Function,+,elements_scrollable_text_line,void,"Canvas*, uint8_t, uint8_t, uint8_t, FuriString*, size_t, _Bool" +Function,+,elements_scrollbar,void,"Canvas*, uint16_t, uint16_t" +Function,+,elements_scrollbar_pos,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t" +Function,+,elements_slightly_rounded_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_slightly_rounded_frame,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,elements_string_fit_width,void,"Canvas*, FuriString*, uint8_t" +Function,+,elements_text_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,empty_screen_alloc,EmptyScreen*, +Function,+,empty_screen_free,void,EmptyScreen* +Function,+,empty_screen_get_view,View*,EmptyScreen* +Function,-,erand48,double,unsigned short[3] +Function,-,exit,void,int +Function,-,explicit_bzero,void,"void*, size_t" +Function,-,fclose,int,FILE* +Function,-,fcloseall,int, +Function,-,fdopen,FILE*,"int, const char*" +Function,-,feof,int,FILE* +Function,-,feof_unlocked,int,FILE* +Function,-,ferror,int,FILE* +Function,-,ferror_unlocked,int,FILE* +Function,-,fflush,int,FILE* +Function,-,fflush_unlocked,int,FILE* +Function,-,ffs,int,int +Function,-,ffsl,int,long +Function,-,ffsll,int,long long +Function,-,fgetc,int,FILE* +Function,-,fgetc_unlocked,int,FILE* +Function,-,fgetpos,int,"FILE*, fpos_t*" +Function,-,fgets,char*,"char*, int, FILE*" +Function,-,fgets_unlocked,char*,"char*, int, FILE*" +Function,+,file_browser_alloc,FileBrowser*,FuriString* +Function,+,file_browser_configure,void,"FileBrowser*, const char*, const char*, _Bool, _Bool, const Icon*, _Bool" +Function,+,file_browser_free,void,FileBrowser* +Function,+,file_browser_get_view,View*,FileBrowser* +Function,+,file_browser_set_callback,void,"FileBrowser*, FileBrowserCallback, void*" +Function,+,file_browser_set_item_callback,void,"FileBrowser*, FileBrowserLoadItemCallback, void*" +Function,+,file_browser_start,void,"FileBrowser*, FuriString*" +Function,+,file_browser_stop,void,FileBrowser* +Function,+,file_browser_worker_alloc,BrowserWorker*,"FuriString*, const char*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_folder_enter,void,"BrowserWorker*, FuriString*, int32_t" +Function,+,file_browser_worker_folder_exit,void,BrowserWorker* +Function,+,file_browser_worker_folder_refresh,void,"BrowserWorker*, int32_t" +Function,+,file_browser_worker_free,void,BrowserWorker* +Function,+,file_browser_worker_is_in_start_folder,_Bool,BrowserWorker* +Function,+,file_browser_worker_load,void,"BrowserWorker*, uint32_t, uint32_t" +Function,+,file_browser_worker_set_callback_context,void,"BrowserWorker*, void*" +Function,+,file_browser_worker_set_config,void,"BrowserWorker*, FuriString*, const char*, _Bool, _Bool" +Function,+,file_browser_worker_set_folder_callback,void,"BrowserWorker*, BrowserWorkerFolderOpenCallback" +Function,+,file_browser_worker_set_item_callback,void,"BrowserWorker*, BrowserWorkerListItemCallback" +Function,+,file_browser_worker_set_list_callback,void,"BrowserWorker*, BrowserWorkerListLoadCallback" +Function,+,file_browser_worker_set_long_load_callback,void,"BrowserWorker*, BrowserWorkerLongLoadCallback" +Function,+,file_stream_alloc,Stream*,Storage* +Function,+,file_stream_close,_Bool,Stream* +Function,+,file_stream_get_error,FS_Error,Stream* +Function,+,file_stream_open,_Bool,"Stream*, const char*, FS_AccessMode, FS_OpenMode" +Function,-,fileno,int,FILE* +Function,-,fileno_unlocked,int,FILE* +Function,+,filesystem_api_error_get_desc,const char*,FS_Error +Function,-,fiprintf,int,"FILE*, const char*, ..." +Function,-,fiscanf,int,"FILE*, const char*, ..." +Function,+,flipper_application_alloc,FlipperApplication*,"Storage*, const ElfApiInterface*" +Function,+,flipper_application_free,void,FlipperApplication* +Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* +Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus +Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* +Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* +Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,+,flipper_application_preload_manifest,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" +Function,-,flipper_application_preload_status_to_string,const char*,FlipperApplicationPreloadStatus +Function,+,flipper_application_spawn,FuriThread*,"FlipperApplication*, void*" +Function,+,flipper_format_buffered_file_alloc,FlipperFormat*,Storage* +Function,+,flipper_format_buffered_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_buffered_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_delete_key,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_alloc,FlipperFormat*,Storage* +Function,+,flipper_format_file_close,_Bool,FlipperFormat* +Function,+,flipper_format_file_open_always,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_append,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_existing,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_file_open_new,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_free,void,FlipperFormat* +Function,+,flipper_format_get_raw_stream,Stream*,FlipperFormat* +Function,+,flipper_format_get_value_count,_Bool,"FlipperFormat*, const char*, uint32_t*" +Function,+,flipper_format_insert_or_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_insert_or_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_insert_or_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_insert_or_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_insert_or_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_insert_or_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_key_exist,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_read_bool,_Bool,"FlipperFormat*, const char*, _Bool*, const uint16_t" +Function,+,flipper_format_read_float,_Bool,"FlipperFormat*, const char*, float*, const uint16_t" +Function,+,flipper_format_read_header,_Bool,"FlipperFormat*, FuriString*, uint32_t*" +Function,+,flipper_format_read_hex,_Bool,"FlipperFormat*, const char*, uint8_t*, const uint16_t" +Function,+,flipper_format_read_hex_uint64,_Bool,"FlipperFormat*, const char*, uint64_t*, const uint16_t" +Function,+,flipper_format_read_int32,_Bool,"FlipperFormat*, const char*, int32_t*, const uint16_t" +Function,+,flipper_format_read_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_read_uint32,_Bool,"FlipperFormat*, const char*, uint32_t*, const uint16_t" +Function,+,flipper_format_rewind,_Bool,FlipperFormat* +Function,+,flipper_format_seek_to_end,_Bool,FlipperFormat* +Function,+,flipper_format_set_strict_mode,void,"FlipperFormat*, _Bool" +Function,+,flipper_format_string_alloc,FlipperFormat*, +Function,+,flipper_format_update_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_update_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_update_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_update_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_update_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_update_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_update_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,flipper_format_write_bool,_Bool,"FlipperFormat*, const char*, const _Bool*, const uint16_t" +Function,+,flipper_format_write_comment,_Bool,"FlipperFormat*, FuriString*" +Function,+,flipper_format_write_comment_cstr,_Bool,"FlipperFormat*, const char*" +Function,+,flipper_format_write_float,_Bool,"FlipperFormat*, const char*, const float*, const uint16_t" +Function,+,flipper_format_write_header,_Bool,"FlipperFormat*, FuriString*, const uint32_t" +Function,+,flipper_format_write_header_cstr,_Bool,"FlipperFormat*, const char*, const uint32_t" +Function,+,flipper_format_write_hex,_Bool,"FlipperFormat*, const char*, const uint8_t*, const uint16_t" +Function,+,flipper_format_write_hex_uint64,_Bool,"FlipperFormat*, const char*, const uint64_t*, const uint16_t" +Function,+,flipper_format_write_int32,_Bool,"FlipperFormat*, const char*, const int32_t*, const uint16_t" +Function,+,flipper_format_write_string,_Bool,"FlipperFormat*, const char*, FuriString*" +Function,+,flipper_format_write_string_cstr,_Bool,"FlipperFormat*, const char*, const char*" +Function,+,flipper_format_write_uint32,_Bool,"FlipperFormat*, const char*, const uint32_t*, const uint16_t" +Function,+,float_is_equal,_Bool,"float, float" +Function,-,flockfile,void,FILE* +Function,-,fls,int,int +Function,-,flsl,int,long +Function,-,flsll,int,long long +Function,-,fmemopen,FILE*,"void*, size_t, const char*" +Function,-,fopen,FILE*,"const char*, const char*" +Function,-,fopencookie,FILE*,"void*, const char*, cookie_io_functions_t" +Function,-,fprintf,int,"FILE*, const char*, ..." +Function,-,fpurge,int,FILE* +Function,-,fputc,int,"int, FILE*" +Function,-,fputc_unlocked,int,"int, FILE*" +Function,-,fputs,int,"const char*, FILE*" +Function,-,fputs_unlocked,int,"const char*, FILE*" +Function,-,fread,size_t,"void*, size_t, size_t, FILE*" +Function,-,fread_unlocked,size_t,"void*, size_t, size_t, FILE*" +Function,+,free,void,void* +Function,-,freopen,FILE*,"const char*, const char*, FILE*" +Function,-,fscanf,int,"FILE*, const char*, ..." +Function,-,fseek,int,"FILE*, long, int" +Function,-,fseeko,int,"FILE*, off_t, int" +Function,-,fsetpos,int,"FILE*, const fpos_t*" +Function,-,ftell,long,FILE* +Function,-,ftello,off_t,FILE* +Function,-,ftrylockfile,int,FILE* +Function,-,funlockfile,void,FILE* +Function,-,funopen,FILE*,"const void*, int (*)(void*, char*, int), int (*)(void*, const char*, int), fpos_t (*)(void*, fpos_t, int), int (*)(void*)" +Function,+,furi_delay_ms,void,uint32_t +Function,+,furi_delay_tick,void,uint32_t +Function,+,furi_delay_until_tick,FuriStatus,uint32_t +Function,+,furi_delay_us,void,uint32_t +Function,+,furi_event_flag_alloc,FuriEventFlag*, +Function,+,furi_event_flag_clear,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_free,void,FuriEventFlag* +Function,+,furi_event_flag_get,uint32_t,FuriEventFlag* +Function,+,furi_event_flag_set,uint32_t,"FuriEventFlag*, uint32_t" +Function,+,furi_event_flag_wait,uint32_t,"FuriEventFlag*, uint32_t, uint32_t, uint32_t" +Function,+,furi_get_tick,uint32_t, +Function,+,furi_hal_bt_change_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_clear_white_list,_Bool, +Function,+,furi_hal_bt_dump_state,void,FuriString* +Function,+,furi_hal_bt_ensure_c2_mode,_Bool,BleGlueC2Mode +Function,+,furi_hal_bt_get_key_storage_buff,void,"uint8_t**, uint16_t*" +Function,+,furi_hal_bt_get_radio_stack,FuriHalBtStack, +Function,+,furi_hal_bt_get_rssi,float, +Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, +Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_bt_hid_kb_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_bt_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_bt_hid_mouse_release_all,_Bool, +Function,+,furi_hal_bt_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_bt_hid_start,void, +Function,+,furi_hal_bt_hid_stop,void, +Function,-,furi_hal_bt_init,void, +Function,+,furi_hal_bt_is_active,_Bool, +Function,+,furi_hal_bt_is_alive,_Bool, +Function,+,furi_hal_bt_is_ble_gatt_gap_supported,_Bool, +Function,+,furi_hal_bt_is_testing_supported,_Bool, +Function,+,furi_hal_bt_lock_core2,void, +Function,+,furi_hal_bt_nvm_sram_sem_acquire,void, +Function,+,furi_hal_bt_nvm_sram_sem_release,void, +Function,+,furi_hal_bt_reinit,void, +Function,+,furi_hal_bt_serial_notify_buffer_is_empty,void, +Function,+,furi_hal_bt_serial_set_event_callback,void,"uint16_t, FuriHalBtSerialCallback, void*" +Function,+,furi_hal_bt_serial_set_rpc_status,void,FuriHalBtSerialRpcStatus +Function,+,furi_hal_bt_serial_start,void, +Function,+,furi_hal_bt_serial_stop,void, +Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" +Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" +Function,+,furi_hal_bt_start_advertising,void, +Function,+,furi_hal_bt_start_app,_Bool,"FuriHalBtProfile, GapEventCallback, void*" +Function,+,furi_hal_bt_start_packet_rx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_start_packet_tx,void,"uint8_t, uint8_t, uint8_t" +Function,+,furi_hal_bt_start_radio_stack,_Bool, +Function,+,furi_hal_bt_start_rx,void,uint8_t +Function,+,furi_hal_bt_start_tone_tx,void,"uint8_t, uint8_t" +Function,+,furi_hal_bt_stop_advertising,void, +Function,+,furi_hal_bt_stop_packet_test,uint16_t, +Function,+,furi_hal_bt_stop_rx,void, +Function,+,furi_hal_bt_stop_tone_tx,void, +Function,+,furi_hal_bt_unlock_core2,void, +Function,+,furi_hal_bt_update_battery_level,void,uint8_t +Function,+,furi_hal_bt_update_power_state,void, +Function,+,furi_hal_cdc_get_ctrl_line_state,uint8_t,uint8_t +Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t +Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" +Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" +Function,-,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_init,void, +Function,-,furi_hal_clock_init_early,void, +Function,+,furi_hal_clock_mco_disable,void, +Function,+,furi_hal_clock_mco_enable,void,"FuriHalClockMcoSourceId, FuriHalClockMcoDivisorId" +Function,-,furi_hal_clock_resume_tick,void, +Function,-,furi_hal_clock_suspend_tick,void, +Function,-,furi_hal_clock_switch_to_hsi,void, +Function,-,furi_hal_clock_switch_to_pll,void, +Function,-,furi_hal_compress_alloc,FuriHalCompress*,uint16_t +Function,-,furi_hal_compress_decode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,furi_hal_compress_encode,_Bool,"FuriHalCompress*, uint8_t*, size_t, uint8_t*, size_t, size_t*" +Function,-,furi_hal_compress_free,void,FuriHalCompress* +Function,-,furi_hal_compress_icon_decode,void,"const uint8_t*, uint8_t**" +Function,-,furi_hal_compress_icon_init,void, +Function,+,furi_hal_console_disable,void, +Function,+,furi_hal_console_enable,void, +Function,+,furi_hal_console_init,void, +Function,+,furi_hal_console_printf,void,"const char[], ..." +Function,+,furi_hal_console_puts,void,const char* +Function,+,furi_hal_console_set_tx_callback,void,"FuriHalConsoleTxCallback, void*" +Function,+,furi_hal_console_tx,void,"const uint8_t*, size_t" +Function,+,furi_hal_console_tx_with_new_line,void,"const uint8_t*, size_t" +Function,+,furi_hal_cortex_delay_us,void,uint32_t +Function,-,furi_hal_cortex_init_early,void, +Function,+,furi_hal_cortex_instructions_per_microsecond,uint32_t, +Function,+,furi_hal_cortex_timer_get,FuriHalCortexTimer,uint32_t +Function,+,furi_hal_cortex_timer_is_expired,_Bool,FuriHalCortexTimer +Function,+,furi_hal_cortex_timer_wait,void,FuriHalCortexTimer +Function,+,furi_hal_crypto_decrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,+,furi_hal_crypto_encrypt,_Bool,"const uint8_t*, uint8_t*, size_t" +Function,-,furi_hal_crypto_init,void, +Function,+,furi_hal_crypto_store_add_key,_Bool,"FuriHalCryptoKey*, uint8_t*" +Function,+,furi_hal_crypto_store_load_key,_Bool,"uint8_t, const uint8_t*" +Function,+,furi_hal_crypto_store_unload_key,_Bool,uint8_t +Function,+,furi_hal_crypto_verify_enclave,_Bool,"uint8_t*, uint8_t*" +Function,+,furi_hal_crypto_verify_key,_Bool,uint8_t +Function,+,furi_hal_debug_disable,void, +Function,+,furi_hal_debug_enable,void, +Function,-,furi_hal_deinit_early,void, +Function,-,furi_hal_flash_erase,void,uint8_t +Function,-,furi_hal_flash_get_base,size_t, +Function,-,furi_hal_flash_get_cycles_count,size_t, +Function,-,furi_hal_flash_get_free_end_address,const void*, +Function,-,furi_hal_flash_get_free_page_count,size_t, +Function,-,furi_hal_flash_get_free_page_start_address,size_t, +Function,-,furi_hal_flash_get_free_start_address,const void*, +Function,-,furi_hal_flash_get_page_number,int16_t,size_t +Function,-,furi_hal_flash_get_page_size,size_t, +Function,-,furi_hal_flash_get_read_block_size,size_t, +Function,-,furi_hal_flash_get_write_block_size,size_t, +Function,-,furi_hal_flash_init,void, +Function,-,furi_hal_flash_ob_apply,void, +Function,-,furi_hal_flash_ob_get_raw_ptr,const FuriHalFlashRawOptionByteData*, +Function,-,furi_hal_flash_ob_set_word,_Bool,"size_t, const uint32_t" +Function,-,furi_hal_flash_program_page,void,"const uint8_t, const uint8_t*, uint16_t" +Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" +Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" +Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" +Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" +Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" +Function,+,furi_hal_gpio_remove_int_callback,void,const GpioPin* +Function,+,furi_hal_hid_consumer_key_press,_Bool,uint16_t +Function,+,furi_hal_hid_consumer_key_release,_Bool,uint16_t +Function,+,furi_hal_hid_get_led_state,uint8_t, +Function,+,furi_hal_hid_is_connected,_Bool, +Function,+,furi_hal_hid_kb_press,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release,_Bool,uint16_t +Function,+,furi_hal_hid_kb_release_all,_Bool, +Function,+,furi_hal_hid_mouse_move,_Bool,"int8_t, int8_t" +Function,+,furi_hal_hid_mouse_press,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_release,_Bool,uint8_t +Function,+,furi_hal_hid_mouse_scroll,_Bool,int8_t +Function,+,furi_hal_hid_set_state_callback,void,"HidStateCallback, void*" +Function,+,furi_hal_hid_u2f_get_request,uint32_t,uint8_t* +Function,+,furi_hal_hid_u2f_is_connected,_Bool, +Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" +Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" +Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* +Function,-,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_init,void, +Function,-,furi_hal_i2c_init_early,void, +Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_read_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t*, uint32_t" +Function,+,furi_hal_i2c_read_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint32_t" +Function,+,furi_hal_i2c_release,void,FuriHalI2cBusHandle* +Function,+,furi_hal_i2c_rx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_trx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uint8_t*, const uint8_t, uint32_t" +Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" +Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" +Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_init,void, +Function,-,furi_hal_init_early,void, +Function,-,furi_hal_interrupt_init,void, +Function,+,furi_hal_interrupt_set_isr,void,"FuriHalInterruptId, FuriHalInterruptISR, void*" +Function,+,furi_hal_interrupt_set_isr_ex,void,"FuriHalInterruptId, uint16_t, FuriHalInterruptISR, void*" +Function,+,furi_hal_light_blink_set_color,void,Light +Function,+,furi_hal_light_blink_start,void,"Light, uint8_t, uint16_t, uint16_t" +Function,+,furi_hal_light_blink_stop,void, +Function,-,furi_hal_light_init,void, +Function,+,furi_hal_light_sequence,void,const char* +Function,+,furi_hal_light_set,void,"Light, uint8_t" +Function,+,furi_hal_memory_alloc,void*,size_t +Function,+,furi_hal_memory_get_free,size_t, +Function,+,furi_hal_memory_init,void, +Function,+,furi_hal_memory_max_pool_block,size_t, +Function,+,furi_hal_mpu_disable,void, +Function,+,furi_hal_mpu_enable,void, +Function,-,furi_hal_mpu_init,void, +Function,+,furi_hal_mpu_protect_disable,void,FuriHalMpuRegion +Function,+,furi_hal_mpu_protect_no_access,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" +Function,+,furi_hal_mpu_protect_read_only,void,"FuriHalMpuRegion, uint32_t, FuriHalMPURegionSize" +Function,-,furi_hal_os_init,void, +Function,+,furi_hal_os_tick,void, +Function,+,furi_hal_power_check_otg_status,void, +Function,+,furi_hal_power_debug_get,void,"PropertyValueCallback, void*" +Function,+,furi_hal_power_deep_sleep_available,_Bool, +Function,+,furi_hal_power_disable_external_3_3v,void, +Function,+,furi_hal_power_disable_otg,void, +Function,+,furi_hal_power_enable_external_3_3v,void, +Function,+,furi_hal_power_enable_otg,void, +Function,+,furi_hal_power_gauge_is_ok,_Bool, +Function,+,furi_hal_power_get_bat_health_pct,uint8_t, +Function,+,furi_hal_power_get_battery_charging_voltage,float, +Function,+,furi_hal_power_get_battery_current,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_design_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_full_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_remaining_capacity,uint32_t, +Function,+,furi_hal_power_get_battery_temperature,float,FuriHalPowerIC +Function,+,furi_hal_power_get_battery_voltage,float,FuriHalPowerIC +Function,+,furi_hal_power_get_pct,uint8_t, +Function,+,furi_hal_power_get_usb_voltage,float, +Function,+,furi_hal_power_info_get,void,"PropertyValueCallback, char, void*" +Function,-,furi_hal_power_init,void, +Function,+,furi_hal_power_insomnia_enter,void, +Function,+,furi_hal_power_insomnia_exit,void, +Function,-,furi_hal_power_insomnia_level,uint16_t, +Function,+,furi_hal_power_is_charging,_Bool, +Function,+,furi_hal_power_is_charging_done,_Bool, +Function,+,furi_hal_power_is_otg_enabled,_Bool, +Function,+,furi_hal_power_off,void, +Function,+,furi_hal_power_reset,void, +Function,+,furi_hal_power_set_battery_charging_voltage,void,float +Function,+,furi_hal_power_shutdown,void, +Function,+,furi_hal_power_sleep,void, +Function,+,furi_hal_power_sleep_available,_Bool, +Function,+,furi_hal_power_suppress_charge_enter,void, +Function,+,furi_hal_power_suppress_charge_exit,void, +Function,+,furi_hal_pwm_set_params,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_start,void,"FuriHalPwmOutputId, uint32_t, uint8_t" +Function,+,furi_hal_pwm_stop,void,FuriHalPwmOutputId +Function,+,furi_hal_random_fill_buf,void,"uint8_t*, uint32_t" +Function,+,furi_hal_random_get,uint32_t, +Function,+,furi_hal_region_get,const FuriHalRegion*, +Function,+,furi_hal_region_get_band,const FuriHalRegionBand*,uint32_t +Function,+,furi_hal_region_get_name,const char*, +Function,-,furi_hal_region_init,void, +Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t +Function,+,furi_hal_region_is_provisioned,_Bool, +Function,+,furi_hal_region_set,void,FuriHalRegion* +Function,-,furi_hal_resources_deinit_early,void, +Function,-,furi_hal_resources_init,void, +Function,-,furi_hal_resources_init_early,void, +Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* +Function,-,furi_hal_rtc_deinit_early,void, +Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, +Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_get_fault_data,uint32_t, +Function,+,furi_hal_rtc_get_heap_track_mode,FuriHalRtcHeapTrackMode, +Function,+,furi_hal_rtc_get_locale_dateformat,FuriHalRtcLocaleDateFormat, +Function,+,furi_hal_rtc_get_locale_timeformat,FuriHalRtcLocaleTimeFormat, +Function,+,furi_hal_rtc_get_locale_units,FuriHalRtcLocaleUnits, +Function,+,furi_hal_rtc_get_log_level,uint8_t, +Function,+,furi_hal_rtc_get_pin_fails,uint32_t, +Function,+,furi_hal_rtc_get_register,uint32_t,FuriHalRtcRegister +Function,+,furi_hal_rtc_get_timestamp,uint32_t, +Function,-,furi_hal_rtc_init,void, +Function,-,furi_hal_rtc_init_early,void, +Function,+,furi_hal_rtc_is_flag_set,_Bool,FuriHalRtcFlag +Function,+,furi_hal_rtc_reset_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_boot_mode,void,FuriHalRtcBootMode +Function,+,furi_hal_rtc_set_datetime,void,FuriHalRtcDateTime* +Function,+,furi_hal_rtc_set_fault_data,void,uint32_t +Function,+,furi_hal_rtc_set_flag,void,FuriHalRtcFlag +Function,+,furi_hal_rtc_set_heap_track_mode,void,FuriHalRtcHeapTrackMode +Function,+,furi_hal_rtc_set_locale_dateformat,void,FuriHalRtcLocaleDateFormat +Function,+,furi_hal_rtc_set_locale_timeformat,void,FuriHalRtcLocaleTimeFormat +Function,+,furi_hal_rtc_set_locale_units,void,FuriHalRtcLocaleUnits +Function,+,furi_hal_rtc_set_log_level,void,uint8_t +Function,+,furi_hal_rtc_set_pin_fails,void,uint32_t +Function,+,furi_hal_rtc_set_register,void,"FuriHalRtcRegister, uint32_t" +Function,+,furi_hal_rtc_validate_datetime,_Bool,FuriHalRtcDateTime* +Function,+,furi_hal_speaker_acquire,_Bool,uint32_t +Function,-,furi_hal_speaker_deinit,void, +Function,-,furi_hal_speaker_init,void, +Function,+,furi_hal_speaker_is_mine,_Bool, +Function,+,furi_hal_speaker_release,void, +Function,+,furi_hal_speaker_set_volume,void,float +Function,+,furi_hal_speaker_start,void,"float, float" +Function,+,furi_hal_speaker_stop,void, +Function,+,furi_hal_spi_acquire,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_deinit,void,FuriHalSpiBus* +Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* +Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* +Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,-,furi_hal_spi_config_deinit_early,void, +Function,-,furi_hal_spi_config_init,void, +Function,-,furi_hal_spi_config_init_early,void, +Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_switch,void,void* +Function,+,furi_hal_uart_deinit,void,FuriHalUartId +Function,+,furi_hal_uart_init,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_resume,void,FuriHalUartId +Function,+,furi_hal_uart_set_br,void,"FuriHalUartId, uint32_t" +Function,+,furi_hal_uart_set_irq_cb,void,"FuriHalUartId, void (*)(UartIrqEvent, uint8_t, void*), void*" +Function,+,furi_hal_uart_suspend,void,FuriHalUartId +Function,+,furi_hal_uart_tx,void,"FuriHalUartId, uint8_t*, size_t" +Function,+,furi_hal_usb_disable,void, +Function,+,furi_hal_usb_enable,void, +Function,+,furi_hal_usb_get_config,FuriHalUsbInterface*, +Function,-,furi_hal_usb_init,void, +Function,+,furi_hal_usb_is_locked,_Bool, +Function,+,furi_hal_usb_lock,void, +Function,+,furi_hal_usb_reinit,void, +Function,+,furi_hal_usb_set_config,_Bool,"FuriHalUsbInterface*, void*" +Function,-,furi_hal_usb_set_state_callback,void,"FuriHalUsbStateCallback, void*" +Function,+,furi_hal_usb_unlock,void, +Function,+,furi_hal_version_do_i_belong_here,_Bool, +Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, +Function,+,furi_hal_version_get_ble_mac,const uint8_t*, +Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, +Function,+,furi_hal_version_get_firmware_version,const Version*, +Function,+,furi_hal_version_get_hw_body,uint8_t, +Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, +Function,+,furi_hal_version_get_hw_connect,uint8_t, +Function,+,furi_hal_version_get_hw_display,FuriHalVersionDisplay, +Function,+,furi_hal_version_get_hw_region,FuriHalVersionRegion, +Function,+,furi_hal_version_get_hw_region_name,const char*, +Function,+,furi_hal_version_get_hw_target,uint8_t, +Function,+,furi_hal_version_get_hw_timestamp,uint32_t, +Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, +Function,+,furi_hal_version_get_model_name,const char*, +Function,+,furi_hal_version_get_name_ptr,const char*, +Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, +Function,-,furi_hal_version_init,void, +Function,+,furi_hal_version_uid,const uint8_t*, +Function,+,furi_hal_version_uid_size,size_t, +Function,-,furi_hal_vibro_init,void, +Function,+,furi_hal_vibro_on,void,_Bool +Function,-,furi_init,void, +Function,+,furi_kernel_get_tick_frequency,uint32_t, +Function,+,furi_kernel_is_irq_or_masked,_Bool, +Function,+,furi_kernel_lock,int32_t, +Function,+,furi_kernel_restore_lock,int32_t,int32_t +Function,+,furi_kernel_unlock,int32_t, +Function,+,furi_log_get_level,FuriLogLevel, +Function,-,furi_log_init,void, +Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_set_level,void,FuriLogLevel +Function,-,furi_log_set_puts,void,FuriLogPuts +Function,-,furi_log_set_timestamp,void,FuriLogTimestamp +Function,+,furi_message_queue_alloc,FuriMessageQueue*,"uint32_t, uint32_t" +Function,+,furi_message_queue_free,void,FuriMessageQueue* +Function,+,furi_message_queue_get,FuriStatus,"FuriMessageQueue*, void*, uint32_t" +Function,+,furi_message_queue_get_capacity,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_count,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_message_size,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_get_space,uint32_t,FuriMessageQueue* +Function,+,furi_message_queue_put,FuriStatus,"FuriMessageQueue*, const void*, uint32_t" +Function,+,furi_message_queue_reset,FuriStatus,FuriMessageQueue* +Function,+,furi_ms_to_ticks,uint32_t,uint32_t +Function,+,furi_mutex_acquire,FuriStatus,"FuriMutex*, uint32_t" +Function,+,furi_mutex_alloc,FuriMutex*,FuriMutexType +Function,+,furi_mutex_free,void,FuriMutex* +Function,+,furi_mutex_get_owner,FuriThreadId,FuriMutex* +Function,+,furi_mutex_release,FuriStatus,FuriMutex* +Function,+,furi_pubsub_alloc,FuriPubSub*, +Function,-,furi_pubsub_free,void,FuriPubSub* +Function,+,furi_pubsub_publish,void,"FuriPubSub*, void*" +Function,+,furi_pubsub_subscribe,FuriPubSubSubscription*,"FuriPubSub*, FuriPubSubCallback, void*" +Function,+,furi_pubsub_unsubscribe,void,"FuriPubSub*, FuriPubSubSubscription*" +Function,+,furi_record_close,void,const char* +Function,+,furi_record_create,void,"const char*, void*" +Function,-,furi_record_destroy,_Bool,const char* +Function,+,furi_record_exists,_Bool,const char* +Function,-,furi_record_init,void, +Function,+,furi_record_open,void*,const char* +Function,+,furi_run,void, +Function,+,furi_semaphore_acquire,FuriStatus,"FuriSemaphore*, uint32_t" +Function,+,furi_semaphore_alloc,FuriSemaphore*,"uint32_t, uint32_t" +Function,+,furi_semaphore_free,void,FuriSemaphore* +Function,+,furi_semaphore_get_count,uint32_t,FuriSemaphore* +Function,+,furi_semaphore_release,FuriStatus,FuriSemaphore* +Function,+,furi_stream_buffer_alloc,FuriStreamBuffer*,"size_t, size_t" +Function,+,furi_stream_buffer_bytes_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_buffer_free,void,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_empty,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_is_full,_Bool,FuriStreamBuffer* +Function,+,furi_stream_buffer_receive,size_t,"FuriStreamBuffer*, void*, size_t, uint32_t" +Function,+,furi_stream_buffer_reset,FuriStatus,FuriStreamBuffer* +Function,+,furi_stream_buffer_send,size_t,"FuriStreamBuffer*, const void*, size_t, uint32_t" +Function,+,furi_stream_buffer_spaces_available,size_t,FuriStreamBuffer* +Function,+,furi_stream_set_trigger_level,_Bool,"FuriStreamBuffer*, size_t" +Function,+,furi_string_alloc,FuriString*, +Function,+,furi_string_alloc_move,FuriString*,FuriString* +Function,+,furi_string_alloc_printf,FuriString*,"const char[], ..." +Function,+,furi_string_alloc_set,FuriString*,const FuriString* +Function,+,furi_string_alloc_set_str,FuriString*,const char[] +Function,+,furi_string_alloc_vprintf,FuriString*,"const char[], va_list" +Function,+,furi_string_cat,void,"FuriString*, const FuriString*" +Function,+,furi_string_cat_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_cat_str,void,"FuriString*, const char[]" +Function,+,furi_string_cat_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_string_cmp,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmp_str,int,"const FuriString*, const char[]" +Function,+,furi_string_cmpi,int,"const FuriString*, const FuriString*" +Function,+,furi_string_cmpi_str,int,"const FuriString*, const char[]" +Function,+,furi_string_empty,_Bool,const FuriString* +Function,+,furi_string_end_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_end_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_equal,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_equal_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_free,void,FuriString* +Function,+,furi_string_get_char,char,"const FuriString*, size_t" +Function,+,furi_string_get_cstr,const char*,const FuriString* +Function,+,furi_string_hash,size_t,const FuriString* +Function,+,furi_string_left,void,"FuriString*, size_t" +Function,+,furi_string_mid,void,"FuriString*, size_t, size_t" +Function,+,furi_string_move,void,"FuriString*, FuriString*" +Function,+,furi_string_printf,int,"FuriString*, const char[], ..." +Function,+,furi_string_push_back,void,"FuriString*, char" +Function,+,furi_string_replace,size_t,"FuriString*, FuriString*, FuriString*, size_t" +Function,+,furi_string_replace_all,void,"FuriString*, const FuriString*, const FuriString*" +Function,+,furi_string_replace_all_str,void,"FuriString*, const char[], const char[]" +Function,+,furi_string_replace_at,void,"FuriString*, size_t, size_t, const char[]" +Function,+,furi_string_replace_str,size_t,"FuriString*, const char[], const char[], size_t" +Function,+,furi_string_reserve,void,"FuriString*, size_t" +Function,+,furi_string_reset,void,FuriString* +Function,+,furi_string_right,void,"FuriString*, size_t" +Function,+,furi_string_search,size_t,"const FuriString*, const FuriString*, size_t" +Function,+,furi_string_search_char,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_rchar,size_t,"const FuriString*, char, size_t" +Function,+,furi_string_search_str,size_t,"const FuriString*, const char[], size_t" +Function,+,furi_string_set,void,"FuriString*, FuriString*" +Function,+,furi_string_set_char,void,"FuriString*, size_t, const char" +Function,+,furi_string_set_n,void,"FuriString*, const FuriString*, size_t, size_t" +Function,+,furi_string_set_str,void,"FuriString*, const char[]" +Function,+,furi_string_set_strn,void,"FuriString*, const char[], size_t" +Function,+,furi_string_size,size_t,const FuriString* +Function,+,furi_string_start_with,_Bool,"const FuriString*, const FuriString*" +Function,+,furi_string_start_with_str,_Bool,"const FuriString*, const char[]" +Function,+,furi_string_swap,void,"FuriString*, FuriString*" +Function,+,furi_string_trim,void,"FuriString*, const char[]" +Function,+,furi_string_utf8_decode,void,"char, FuriStringUTF8State*, FuriStringUnicodeValue*" +Function,+,furi_string_utf8_length,size_t,FuriString* +Function,+,furi_string_utf8_push,void,"FuriString*, FuriStringUnicodeValue" +Function,+,furi_string_vprintf,int,"FuriString*, const char[], va_list" +Function,+,furi_thread_alloc,FuriThread*, +Function,+,furi_thread_alloc_ex,FuriThread*,"const char*, uint32_t, FuriThreadCallback, void*" +Function,+,furi_thread_catch,void, +Function,-,furi_thread_disable_heap_trace,void,FuriThread* +Function,+,furi_thread_enable_heap_trace,void,FuriThread* +Function,+,furi_thread_enumerate,uint32_t,"FuriThreadId*, uint32_t" +Function,+,furi_thread_flags_clear,uint32_t,uint32_t +Function,+,furi_thread_flags_get,uint32_t, +Function,+,furi_thread_flags_set,uint32_t,"FuriThreadId, uint32_t" +Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" +Function,+,furi_thread_free,void,FuriThread* +Function,+,furi_thread_get_current,FuriThread*, +Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_heap_size,size_t,FuriThread* +Function,+,furi_thread_get_id,FuriThreadId,FuriThread* +Function,+,furi_thread_get_name,const char*,FuriThreadId +Function,+,furi_thread_get_return_code,int32_t,FuriThread* +Function,+,furi_thread_get_stack_space,uint32_t,FuriThreadId +Function,+,furi_thread_get_state,FuriThreadState,FuriThread* +Function,+,furi_thread_get_stdout_callback,FuriThreadStdoutWriteCallback, +Function,+,furi_thread_is_suspended,_Bool,FuriThreadId +Function,+,furi_thread_join,_Bool,FuriThread* +Function,+,furi_thread_mark_as_service,void,FuriThread* +Function,+,furi_thread_resume,void,FuriThreadId +Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" +Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_name,void,"FuriThread*, const char*" +Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" +Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" +Function,+,furi_thread_set_state_callback,void,"FuriThread*, FuriThreadStateCallback" +Function,+,furi_thread_set_state_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_stdout_callback,_Bool,FuriThreadStdoutWriteCallback +Function,+,furi_thread_start,void,FuriThread* +Function,+,furi_thread_stdout_flush,int32_t, +Function,+,furi_thread_stdout_write,size_t,"const char*, size_t" +Function,+,furi_thread_suspend,void,FuriThreadId +Function,+,furi_thread_yield,void, +Function,+,furi_timer_alloc,FuriTimer*,"FuriTimerCallback, FuriTimerType, void*" +Function,+,furi_timer_free,void,FuriTimer* +Function,+,furi_timer_is_running,uint32_t,FuriTimer* +Function,+,furi_timer_start,FuriStatus,"FuriTimer*, uint32_t" +Function,+,furi_timer_stop,FuriStatus,FuriTimer* +Function,-,fwrite,size_t,"const void*, size_t, size_t, FILE*" +Function,-,fwrite_unlocked,size_t,"const void*, size_t, size_t, FILE*" +Function,-,gap_get_state,GapState, +Function,-,gap_init,_Bool,"GapConfig*, GapEventCallback, void*" +Function,-,gap_start_advertising,void, +Function,-,gap_stop_advertising,void, +Function,-,gap_thread_stop,void, +Function,-,getc,int,FILE* +Function,-,getc_unlocked,int,FILE* +Function,-,getchar,int, +Function,-,getchar_unlocked,int, +Function,-,getenv,char*,const char* +Function,-,gets,char*,char* +Function,-,getsubopt,int,"char**, char**, char**" +Function,-,getw,int,FILE* +Function,-,gmtime,tm*,const time_t* +Function,-,gmtime_r,tm*,"const time_t*, tm*" +Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" +Function,+,gui_direct_draw_acquire,Canvas*,Gui* +Function,+,gui_direct_draw_release,void,Gui* +Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" +Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" +Function,+,gui_set_lockdown,void,"Gui*, _Bool" +Function,-,gui_view_port_send_to_back,void,"Gui*, ViewPort*" +Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" +Function,+,hal_sd_detect,_Bool, +Function,+,hal_sd_detect_init,void, +Function,+,hal_sd_detect_set_low,void, +Function,+,hmac_sha256_finish,void,"const hmac_sha256_context*, const uint8_t*, uint8_t*" +Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" +Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" +Function,+,icon_animation_alloc,IconAnimation*,const Icon* +Function,+,icon_animation_free,void,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,IconAnimation* +Function,+,icon_animation_get_width,uint8_t,IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" +Function,+,icon_animation_start,void,IconAnimation* +Function,+,icon_animation_stop,void,IconAnimation* +Function,+,icon_get_data,const uint8_t*,const Icon* +Function,+,icon_get_height,uint8_t,const Icon* +Function,+,icon_get_width,uint8_t,const Icon* +Function,-,index,char*,"const char*, int" +Function,+,init_mutex,_Bool,"ValueMutex*, void*, size_t" +Function,-,initstate,char*,"unsigned, char*, size_t" +Function,+,input_get_key_name,const char*,InputKey +Function,+,input_get_type_name,const char*,InputType +Function,-,iprintf,int,"const char*, ..." +Function,-,isalnum,int,int +Function,-,isalnum_l,int,"int, locale_t" +Function,-,isalpha,int,int +Function,-,isalpha_l,int,"int, locale_t" +Function,-,isascii,int,int +Function,-,isascii_l,int,"int, locale_t" +Function,-,isblank,int,int +Function,-,isblank_l,int,"int, locale_t" +Function,-,iscanf,int,"const char*, ..." +Function,-,iscntrl,int,int +Function,-,iscntrl_l,int,"int, locale_t" +Function,-,isdigit,int,int +Function,-,isdigit_l,int,"int, locale_t" +Function,-,isgraph,int,int +Function,-,isgraph_l,int,"int, locale_t" +Function,-,islower,int,int +Function,-,islower_l,int,"int, locale_t" +Function,-,isprint,int,int +Function,-,isprint_l,int,"int, locale_t" +Function,-,ispunct,int,int +Function,-,ispunct_l,int,"int, locale_t" +Function,-,isspace,int,int +Function,-,isspace_l,int,"int, locale_t" +Function,-,isupper,int,int +Function,-,isupper_l,int,"int, locale_t" +Function,-,isxdigit,int,int +Function,-,isxdigit_l,int,"int, locale_t" +Function,-,itoa,char*,"int, char*, int" +Function,-,jrand48,long,unsigned short[3] +Function,-,l64a,char*,long +Function,-,labs,long,long +Function,-,lcong48,void,unsigned short[7] +Function,-,ldiv,ldiv_t,"long, long" +Function,-,llabs,long long,long long +Function,-,lldiv,lldiv_t,"long long, long long" +Function,+,loader_get_pubsub,FuriPubSub*,Loader* +Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_lock,_Bool,Loader* +Function,+,loader_show_menu,void, +Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" +Function,+,loader_unlock,void,Loader* +Function,+,loader_update_menu,void, +Function,+,loading_alloc,Loading*, +Function,+,loading_free,void,Loading* +Function,+,loading_get_view,View*,Loading* +Function,+,locale_celsius_to_fahrenheit,float,float +Function,+,locale_fahrenheit_to_celsius,float,float +Function,+,locale_format_date,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleDateFormat, const char*" +Function,+,locale_format_time,void,"FuriString*, const FuriHalRtcDateTime*, const LocaleTimeFormat, const _Bool" +Function,+,locale_get_date_format,LocaleDateFormat, +Function,+,locale_get_measurement_unit,LocaleMeasurementUnits, +Function,+,locale_get_time_format,LocaleTimeFormat, +Function,+,locale_set_date_format,void,LocaleDateFormat +Function,+,locale_set_measurement_unit,void,LocaleMeasurementUnits +Function,+,locale_set_time_format,void,LocaleTimeFormat +Function,-,localtime,tm*,const time_t* +Function,-,localtime_r,tm*,"const time_t*, tm*" +Function,-,lrand48,long, +Function,+,malloc,void*,size_t +Function,+,manchester_advance,_Bool,"ManchesterState, ManchesterEvent, ManchesterState*, _Bool*" +Function,+,manchester_encoder_advance,_Bool,"ManchesterEncoderState*, const _Bool, ManchesterEncoderResult*" +Function,+,manchester_encoder_finish,ManchesterEncoderResult,ManchesterEncoderState* +Function,+,manchester_encoder_reset,void,ManchesterEncoderState* +Function,-,mbedtls_des3_crypt_cbc,int,"mbedtls_des3_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des3_crypt_ecb,int,"mbedtls_des3_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des3_free,void,mbedtls_des3_context* +Function,-,mbedtls_des3_init,void,mbedtls_des3_context* +Function,-,mbedtls_des3_set2key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set2key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 2]" +Function,-,mbedtls_des3_set3key_dec,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des3_set3key_enc,int,"mbedtls_des3_context*, const unsigned char[8 * 3]" +Function,-,mbedtls_des_crypt_cbc,int,"mbedtls_des_context*, int, size_t, unsigned char[8], const unsigned char*, unsigned char*" +Function,-,mbedtls_des_crypt_ecb,int,"mbedtls_des_context*, const unsigned char[8], unsigned char[8]" +Function,-,mbedtls_des_free,void,mbedtls_des_context* +Function,-,mbedtls_des_init,void,mbedtls_des_context* +Function,-,mbedtls_des_key_check_key_parity,int,const unsigned char[8] +Function,-,mbedtls_des_key_check_weak,int,const unsigned char[8] +Function,-,mbedtls_des_key_set_parity,void,unsigned char[8] +Function,-,mbedtls_des_self_test,int,int +Function,-,mbedtls_des_setkey,void,"uint32_t[32], const unsigned char[8]" +Function,-,mbedtls_des_setkey_dec,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_des_setkey_enc,int,"mbedtls_des_context*, const unsigned char[8]" +Function,-,mbedtls_internal_sha1_process,int,"mbedtls_sha1_context*, const unsigned char[64]" +Function,-,mbedtls_platform_gmtime_r,tm*,"const mbedtls_time_t*, tm*" +Function,-,mbedtls_platform_zeroize,void,"void*, size_t" +Function,-,mbedtls_sha1,int,"const unsigned char*, size_t, unsigned char[20]" +Function,-,mbedtls_sha1_clone,void,"mbedtls_sha1_context*, const mbedtls_sha1_context*" +Function,-,mbedtls_sha1_finish,int,"mbedtls_sha1_context*, unsigned char[20]" +Function,-,mbedtls_sha1_free,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_init,void,mbedtls_sha1_context* +Function,-,mbedtls_sha1_self_test,int,int +Function,-,mbedtls_sha1_starts,int,mbedtls_sha1_context* +Function,-,mbedtls_sha1_update,int,"mbedtls_sha1_context*, const unsigned char*, size_t" +Function,-,mblen,int,"const char*, size_t" +Function,-,mbstowcs,size_t,"wchar_t*, const char*, size_t" +Function,-,mbtowc,int,"wchar_t*, const char*, size_t" +Function,+,md5,void,"const unsigned char*, size_t, unsigned char[16]" +Function,+,md5_finish,void,"md5_context*, unsigned char[16]" +Function,+,md5_process,void,"md5_context*, const unsigned char[64]" +Function,+,md5_starts,void,md5_context* +Function,+,md5_update,void,"md5_context*, const unsigned char*, size_t" +Function,-,memccpy,void*,"void*, const void*, int, size_t" +Function,+,memchr,void*,"const void*, int, size_t" +Function,+,memcmp,int,"const void*, const void*, size_t" +Function,+,memcpy,void*,"void*, const void*, size_t" +Function,-,memmem,void*,"const void*, size_t, const void*, size_t" +Function,-,memmgr_alloc_from_pool,void*,size_t +Function,+,memmgr_get_free_heap,size_t, +Function,+,memmgr_get_minimum_free_heap,size_t, +Function,+,memmgr_get_total_heap,size_t, +Function,+,memmgr_heap_disable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_enable_thread_trace,void,FuriThreadId +Function,+,memmgr_heap_get_max_free_block,size_t, +Function,+,memmgr_heap_get_thread_memory,size_t,FuriThreadId +Function,+,memmgr_heap_printf_free_blocks,void, +Function,-,memmgr_pool_get_free,size_t, +Function,-,memmgr_pool_get_max_block,size_t, +Function,+,memmove,void*,"void*, const void*, size_t" +Function,-,mempcpy,void*,"void*, const void*, size_t" +Function,-,memrchr,void*,"const void*, int, size_t" +Function,+,memset,void*,"void*, int, size_t" +Function,+,menu_add_item,void,"Menu*, const char*, const Icon*, uint32_t, MenuItemCallback, void*" +Function,+,menu_alloc,Menu*, +Function,+,menu_free,void,Menu* +Function,+,menu_get_view,View*,Menu* +Function,+,menu_reset,void,Menu* +Function,+,menu_set_selected_item,void,"Menu*, uint32_t" +Function,-,mkdtemp,char*,char* +Function,-,mkostemp,int,"char*, int" +Function,-,mkostemps,int,"char*, int, int" +Function,-,mkstemp,int,char* +Function,-,mkstemps,int,"char*, int" +Function,-,mktemp,char*,char* +Function,-,mktime,time_t,tm* +Function,-,mrand48,long, +Function,+,notification_internal_message,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_internal_message_block,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_message,void,"NotificationApp*, const NotificationSequence*" +Function,+,notification_message_block,void,"NotificationApp*, const NotificationSequence*" +Function,-,nrand48,long,unsigned short[3] +Function,-,on_exit,int,"void (*)(int, void*), void*" +Function,-,open_memstream,FILE*,"char**, size_t*" +Function,+,path_append,void,"FuriString*, const char*" +Function,+,path_concat,void,"const char*, const char*, FuriString*" +Function,+,path_contains_only_ascii,_Bool,const char* +Function,+,path_extract_basename,void,"const char*, FuriString*" +Function,+,path_extract_dirname,void,"const char*, FuriString*" +Function,+,path_extract_extension,void,"FuriString*, char*, size_t" +Function,+,path_extract_filename,void,"FuriString*, FuriString*, _Bool" +Function,+,path_extract_filename_no_ext,void,"const char*, FuriString*" +Function,-,pcTaskGetName,char*,TaskHandle_t +Function,-,pcTimerGetName,const char*,TimerHandle_t +Function,-,pclose,int,FILE* +Function,-,perror,void,const char* +Function,-,popen,FILE*,"const char*, const char*" +Function,+,popup_alloc,Popup*, +Function,+,popup_disable_timeout,void,Popup* +Function,+,popup_enable_timeout,void,Popup* +Function,+,popup_free,void,Popup* +Function,+,popup_get_view,View*,Popup* +Function,+,popup_reset,void,Popup* +Function,+,popup_set_callback,void,"Popup*, PopupCallback" +Function,+,popup_set_context,void,"Popup*, void*" +Function,+,popup_set_header,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_icon,void,"Popup*, uint8_t, uint8_t, const Icon*" +Function,+,popup_set_text,void,"Popup*, const char*, uint8_t, uint8_t, Align, Align" +Function,+,popup_set_timeout,void,"Popup*, uint32_t" +Function,-,posix_memalign,int,"void**, size_t, size_t" +Function,+,power_enable_low_battery_level_notification,void,"Power*, _Bool" +Function,+,power_get_info,void,"Power*, PowerInfo*" +Function,+,power_get_pubsub,FuriPubSub*,Power* +Function,+,power_is_battery_healthy,_Bool,Power* +Function,+,power_off,void,Power* +Function,+,power_reboot,void,PowerBootMode +Function,-,printf,int,"const char*, ..." +Function,+,property_value_out,void,"PropertyValueContext*, const char*, unsigned int, ..." +Function,+,protocol_dict_alloc,ProtocolDict*,"const ProtocolBase**, size_t" +Function,+,protocol_dict_decoders_feed,ProtocolId,"ProtocolDict*, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_feature,ProtocolId,"ProtocolDict*, uint32_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_feed_by_id,ProtocolId,"ProtocolDict*, size_t, _Bool, uint32_t" +Function,+,protocol_dict_decoders_start,void,ProtocolDict* +Function,+,protocol_dict_encoder_start,_Bool,"ProtocolDict*, size_t" +Function,+,protocol_dict_encoder_yield,LevelDuration,"ProtocolDict*, size_t" +Function,+,protocol_dict_free,void,ProtocolDict* +Function,+,protocol_dict_get_data,void,"ProtocolDict*, size_t, uint8_t*, size_t" +Function,+,protocol_dict_get_data_size,size_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_features,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_manufacturer,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_max_data_size,size_t,ProtocolDict* +Function,+,protocol_dict_get_name,const char*,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_protocol_by_name,ProtocolId,"ProtocolDict*, const char*" +Function,+,protocol_dict_get_validate_count,uint32_t,"ProtocolDict*, size_t" +Function,+,protocol_dict_get_write_data,_Bool,"ProtocolDict*, size_t, void*" +Function,+,protocol_dict_render_brief_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_render_data,void,"ProtocolDict*, FuriString*, size_t" +Function,+,protocol_dict_set_data,void,"ProtocolDict*, size_t, const uint8_t*, size_t" +Function,-,pselect,int,"int, fd_set*, fd_set*, fd_set*, const timespec*, const sigset_t*" +Function,-,putc,int,"int, FILE*" +Function,-,putc_unlocked,int,"int, FILE*" +Function,-,putchar,int,int +Function,-,putchar_unlocked,int,int +Function,-,putenv,int,char* +Function,-,puts,int,const char* +Function,-,putw,int,"int, FILE*" +Function,-,pvPortCalloc,void*,"size_t, size_t" +Function,-,pvPortMalloc,void*,size_t +Function,-,pvTaskGetThreadLocalStoragePointer,void*,"TaskHandle_t, BaseType_t" +Function,-,pvTaskIncrementMutexHeldCount,TaskHandle_t, +Function,-,pvTimerGetTimerID,void*,const TimerHandle_t +Function,-,pxPortInitialiseStack,StackType_t*,"StackType_t*, TaskFunction_t, void*" +Function,-,qsort,void,"void*, size_t, size_t, __compar_fn_t" +Function,-,qsort_r,void,"void*, size_t, size_t, int (*)(const void*, const void*, void*), void*" +Function,-,quick_exit,void,int +Function,+,rand,int, +Function,-,rand_r,int,unsigned* +Function,+,random,long, +Function,-,rawmemchr,void*,"const void*, int" +Function,-,read_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" +Function,+,realloc,void*,"void*, size_t" +Function,-,reallocarray,void*,"void*, size_t, size_t" +Function,-,reallocf,void*,"void*, size_t" +Function,-,realpath,char*,"const char*, char*" +Function,+,release_mutex,_Bool,"ValueMutex*, const void*" +Function,-,remove,int,const char* +Function,-,rename,int,"const char*, const char*" +Function,-,renameat,int,"int, const char*, int, const char*" +Function,-,rewind,void,FILE* +Function,-,rindex,char*,"const char*, int" +Function,+,rpc_session_close,void,RpcSession* +Function,+,rpc_session_feed,size_t,"RpcSession*, uint8_t*, size_t, TickType_t" +Function,+,rpc_session_get_available_size,size_t,RpcSession* +Function,+,rpc_session_open,RpcSession*,Rpc* +Function,+,rpc_session_set_buffer_is_empty_callback,void,"RpcSession*, RpcBufferIsEmptyCallback" +Function,+,rpc_session_set_close_callback,void,"RpcSession*, RpcSessionClosedCallback" +Function,+,rpc_session_set_context,void,"RpcSession*, void*" +Function,+,rpc_session_set_send_bytes_callback,void,"RpcSession*, RpcSendBytesCallback" +Function,+,rpc_session_set_terminated_callback,void,"RpcSession*, RpcSessionTerminatedCallback" +Function,+,rpc_system_app_confirm,void,"RpcAppSystem*, RpcAppSystemEvent, _Bool" +Function,+,rpc_system_app_error_reset,void,RpcAppSystem* +Function,+,rpc_system_app_exchange_data,void,"RpcAppSystem*, const uint8_t*, size_t" +Function,+,rpc_system_app_get_data,const char*,RpcAppSystem* +Function,+,rpc_system_app_send_exited,void,RpcAppSystem* +Function,+,rpc_system_app_send_started,void,RpcAppSystem* +Function,+,rpc_system_app_set_callback,void,"RpcAppSystem*, RpcAppSystemCallback, void*" +Function,+,rpc_system_app_set_data_exchange_callback,void,"RpcAppSystem*, RpcAppSystemDataExchangeCallback, void*" +Function,+,rpc_system_app_set_error_code,void,"RpcAppSystem*, uint32_t" +Function,+,rpc_system_app_set_error_text,void,"RpcAppSystem*, const char*" +Function,-,rpmatch,int,const char* +Function,+,saved_struct_get_payload_size,_Bool,"const char*, uint8_t, uint8_t, size_t*" +Function,+,saved_struct_load,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t" +Function,-,scanf,int,"const char*, ..." +Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" +Function,+,scene_manager_free,void,SceneManager* +Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_handle_back_event,_Bool,SceneManager* +Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_handle_tick_event,void,SceneManager* +Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" +Function,+,scene_manager_previous_scene,_Bool,SceneManager* +Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_search_and_switch_to_previous_scene_one_of,_Bool,"SceneManager*, const uint32_t*, size_t" +Function,+,scene_manager_set_scene_state,void,"SceneManager*, uint32_t, uint32_t" +Function,+,scene_manager_stop,void,SceneManager* +Function,+,sd_api_get_fs_type_text,const char*,SDFsType +Function,-,secure_getenv,char*,const char* +Function,-,seed48,unsigned short*,unsigned short[3] +Function,-,select,int,"int, fd_set*, fd_set*, fd_set*, timeval*" +Function,-,serial_svc_is_started,_Bool, +Function,-,serial_svc_notify_buffer_is_empty,void, +Function,-,serial_svc_set_callbacks,void,"uint16_t, SerialServiceEventCallback, void*" +Function,-,serial_svc_set_rpc_status,void,SerialServiceRpcStatus +Function,-,serial_svc_start,void, +Function,-,serial_svc_stop,void, +Function,-,serial_svc_update_tx,_Bool,"uint8_t*, uint16_t" +Function,+,set_random_name,void,"char*, uint8_t" +Function,-,setbuf,void,"FILE*, char*" +Function,-,setbuffer,void,"FILE*, char*, int" +Function,-,setenv,int,"const char*, const char*, int" +Function,-,setkey,void,const char* +Function,-,setlinebuf,int,FILE* +Function,-,setstate,char*,char* +Function,-,setvbuf,int,"FILE*, char*, int, size_t" +Function,+,sha256,void,"const unsigned char*, unsigned int, unsigned char[32]" +Function,+,sha256_finish,void,"sha256_context*, unsigned char[32]" +Function,+,sha256_process,void,sha256_context* +Function,+,sha256_start,void,sha256_context* +Function,+,sha256_update,void,"sha256_context*, const unsigned char*, unsigned int" +Function,-,siprintf,int,"char*, const char*, ..." +Function,-,siscanf,int,"const char*, const char*, ..." +Function,-,sniprintf,int,"char*, size_t, const char*, ..." +Function,+,snprintf,int,"char*, size_t, const char*, ..." +Function,-,sprintf,int,"char*, const char*, ..." +Function,+,srand,void,unsigned +Function,-,srand48,void,long +Function,-,srandom,void,unsigned +Function,+,sscanf,int,"const char*, const char*, ..." +Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" +Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_mkdir,FS_Error,"Storage*, const char*" +Function,+,storage_common_remove,FS_Error,"Storage*, const char*" +Function,+,storage_common_rename,FS_Error,"Storage*, const char*, const char*" +Function,+,storage_common_stat,FS_Error,"Storage*, const char*, FileInfo*" +Function,+,storage_common_timestamp,FS_Error,"Storage*, const char*, uint32_t*" +Function,+,storage_dir_close,_Bool,File* +Function,+,storage_dir_open,_Bool,"File*, const char*" +Function,+,storage_dir_read,_Bool,"File*, FileInfo*, char*, uint16_t" +Function,-,storage_dir_rewind,_Bool,File* +Function,+,storage_error_get_desc,const char*,FS_Error +Function,+,storage_file_alloc,File*,Storage* +Function,+,storage_file_close,_Bool,File* +Function,+,storage_file_eof,_Bool,File* +Function,+,storage_file_exists,_Bool,"Storage*, const char*" +Function,+,storage_file_free,void,File* +Function,+,storage_file_get_error,FS_Error,File* +Function,+,storage_file_get_error_desc,const char*,File* +Function,-,storage_file_get_internal_error,int32_t,File* +Function,+,storage_file_is_dir,_Bool,File* +Function,+,storage_file_is_open,_Bool,File* +Function,+,storage_file_open,_Bool,"File*, const char*, FS_AccessMode, FS_OpenMode" +Function,+,storage_file_read,uint16_t,"File*, void*, uint16_t" +Function,+,storage_file_seek,_Bool,"File*, uint32_t, _Bool" +Function,+,storage_file_size,uint64_t,File* +Function,-,storage_file_sync,_Bool,File* +Function,+,storage_file_tell,uint64_t,File* +Function,+,storage_file_truncate,_Bool,File* +Function,+,storage_file_write,uint16_t,"File*, const void*, uint16_t" +Function,+,storage_get_next_filename,void,"Storage*, const char*, const char*, const char*, FuriString*, uint8_t" +Function,+,storage_get_pubsub,FuriPubSub*,Storage* +Function,+,storage_int_backup,FS_Error,"Storage*, const char*" +Function,+,storage_int_restore,FS_Error,"Storage*, const char*, Storage_name_converter" +Function,+,storage_sd_format,FS_Error,Storage* +Function,+,storage_sd_info,FS_Error,"Storage*, SDInfo*" +Function,+,storage_sd_status,FS_Error,Storage* +Function,+,storage_sd_unmount,FS_Error,Storage* +Function,+,storage_simply_mkdir,_Bool,"Storage*, const char*" +Function,+,storage_simply_remove,_Bool,"Storage*, const char*" +Function,+,storage_simply_remove_recursive,_Bool,"Storage*, const char*" +Function,-,stpcpy,char*,"char*, const char*" +Function,-,stpncpy,char*,"char*, const char*, size_t" +Function,-,strcasecmp,int,"const char*, const char*" +Function,-,strcasecmp_l,int,"const char*, const char*, locale_t" +Function,+,strcasestr,char*,"const char*, const char*" +Function,-,strcat,char*,"char*, const char*" +Function,+,strchr,char*,"const char*, int" +Function,-,strchrnul,char*,"const char*, int" +Function,+,strcmp,int,"const char*, const char*" +Function,-,strcoll,int,"const char*, const char*" +Function,-,strcoll_l,int,"const char*, const char*, locale_t" +Function,+,strcpy,char*,"char*, const char*" +Function,+,strcspn,size_t,"const char*, const char*" +Function,+,strdup,char*,const char* +Function,+,stream_clean,void,Stream* +Function,+,stream_copy,size_t,"Stream*, Stream*, size_t" +Function,+,stream_copy_full,size_t,"Stream*, Stream*" +Function,+,stream_delete,_Bool,"Stream*, size_t" +Function,+,stream_delete_and_insert,_Bool,"Stream*, size_t, StreamWriteCB, const void*" +Function,+,stream_delete_and_insert_char,_Bool,"Stream*, size_t, char" +Function,+,stream_delete_and_insert_cstring,_Bool,"Stream*, size_t, const char*" +Function,+,stream_delete_and_insert_format,_Bool,"Stream*, size_t, const char*, ..." +Function,+,stream_delete_and_insert_string,_Bool,"Stream*, size_t, FuriString*" +Function,+,stream_delete_and_insert_vaformat,_Bool,"Stream*, size_t, const char*, va_list" +Function,+,stream_dump_data,void,Stream* +Function,+,stream_eof,_Bool,Stream* +Function,+,stream_free,void,Stream* +Function,+,stream_insert,_Bool,"Stream*, const uint8_t*, size_t" +Function,+,stream_insert_char,_Bool,"Stream*, char" +Function,+,stream_insert_cstring,_Bool,"Stream*, const char*" +Function,+,stream_insert_format,_Bool,"Stream*, const char*, ..." +Function,+,stream_insert_string,_Bool,"Stream*, FuriString*" +Function,+,stream_insert_vaformat,_Bool,"Stream*, const char*, va_list" +Function,+,stream_load_from_file,size_t,"Stream*, Storage*, const char*" +Function,+,stream_read,size_t,"Stream*, uint8_t*, size_t" +Function,+,stream_read_line,_Bool,"Stream*, FuriString*" +Function,+,stream_rewind,_Bool,Stream* +Function,+,stream_save_to_file,size_t,"Stream*, Storage*, const char*, FS_OpenMode" +Function,+,stream_seek,_Bool,"Stream*, int32_t, StreamOffset" +Function,+,stream_seek_to_char,_Bool,"Stream*, char, StreamDirection" +Function,+,stream_size,size_t,Stream* +Function,+,stream_split,_Bool,"Stream*, Stream*, Stream*" +Function,+,stream_tell,size_t,Stream* +Function,+,stream_write,size_t,"Stream*, const uint8_t*, size_t" +Function,+,stream_write_char,size_t,"Stream*, char" +Function,+,stream_write_cstring,size_t,"Stream*, const char*" +Function,+,stream_write_format,size_t,"Stream*, const char*, ..." +Function,+,stream_write_string,size_t,"Stream*, FuriString*" +Function,+,stream_write_vaformat,size_t,"Stream*, const char*, va_list" +Function,-,strerror,char*,int +Function,-,strerror_l,char*,"int, locale_t" +Function,-,strerror_r,char*,"int, char*, size_t" +Function,-,strftime,size_t,"char*, size_t, const char*, const tm*" +Function,-,strftime_l,size_t,"char*, size_t, const char*, const tm*, locale_t" +Function,+,string_stream_alloc,Stream*, +Function,-,strlcat,size_t,"char*, const char*, size_t" +Function,+,strlcpy,size_t,"char*, const char*, size_t" +Function,+,strlen,size_t,const char* +Function,-,strlwr,char*,char* +Function,+,strncasecmp,int,"const char*, const char*, size_t" +Function,-,strncasecmp_l,int,"const char*, const char*, size_t, locale_t" +Function,-,strncat,char*,"char*, const char*, size_t" +Function,+,strncmp,int,"const char*, const char*, size_t" +Function,+,strncpy,char*,"char*, const char*, size_t" +Function,-,strndup,char*,"const char*, size_t" +Function,-,strnlen,size_t,"const char*, size_t" +Function,-,strnstr,char*,"const char*, const char*, size_t" +Function,-,strpbrk,char*,"const char*, const char*" +Function,-,strptime,char*,"const char*, const char*, tm*" +Function,-,strptime_l,char*,"const char*, const char*, tm*, locale_t" +Function,+,strrchr,char*,"const char*, int" +Function,-,strsep,char*,"char**, const char*" +Function,-,strsignal,char*,int +Function,+,strspn,size_t,"const char*, const char*" +Function,+,strstr,char*,"const char*, const char*" +Function,-,strtod,double,"const char*, char**" +Function,-,strtod_l,double,"const char*, char**, locale_t" +Function,+,strtof,float,"const char*, char**" +Function,-,strtof_l,float,"const char*, char**, locale_t" +Function,-,strtok,char*,"char*, const char*" +Function,-,strtok_r,char*,"char*, const char*, char**" +Function,+,strtol,long,"const char*, char**, int" +Function,-,strtol_l,long,"const char*, char**, int, locale_t" +Function,-,strtold,long double,"const char*, char**" +Function,-,strtold_l,long double,"const char*, char**, locale_t" +Function,-,strtoll,long long,"const char*, char**, int" +Function,-,strtoll_l,long long,"const char*, char**, int, locale_t" +Function,+,strtoul,unsigned long,"const char*, char**, int" +Function,-,strtoul_l,unsigned long,"const char*, char**, int, locale_t" +Function,+,strtoull,unsigned long long,"const char*, char**, int" +Function,-,strtoull_l,unsigned long long,"const char*, char**, int, locale_t" +Function,-,strupr,char*,char* +Function,-,strverscmp,int,"const char*, const char*" +Function,-,strxfrm,size_t,"char*, const char*, size_t" +Function,-,strxfrm_l,size_t,"char*, const char*, size_t, locale_t" +Function,+,submenu_add_item,void,"Submenu*, const char*, uint32_t, SubmenuItemCallback, void*" +Function,+,submenu_alloc,Submenu*, +Function,+,submenu_free,void,Submenu* +Function,+,submenu_get_view,View*,Submenu* +Function,+,submenu_reset,void,Submenu* +Function,+,submenu_set_header,void,"Submenu*, const char*" +Function,+,submenu_set_selected_item,void,"Submenu*, uint32_t" +Function,-,system,int,const char* +Function,+,tar_archive_add_dir,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_add_file,_Bool,"TarArchive*, const char*, const char*, const int32_t" +Function,+,tar_archive_alloc,TarArchive*,Storage* +Function,+,tar_archive_dir_add_element,_Bool,"TarArchive*, const char*" +Function,+,tar_archive_file_add_data_block,_Bool,"TarArchive*, const uint8_t*, const int32_t" +Function,+,tar_archive_file_add_header,_Bool,"TarArchive*, const char*, const int32_t" +Function,+,tar_archive_file_finalize,_Bool,TarArchive* +Function,+,tar_archive_finalize,_Bool,TarArchive* +Function,+,tar_archive_free,void,TarArchive* +Function,+,tar_archive_get_entries_count,int32_t,TarArchive* +Function,+,tar_archive_open,_Bool,"TarArchive*, const char*, TarOpenMode" +Function,+,tar_archive_set_file_callback,void,"TarArchive*, tar_unpack_file_cb, void*" +Function,+,tar_archive_store_data,_Bool,"TarArchive*, const char*, const uint8_t*, const int32_t" +Function,+,tar_archive_unpack_file,_Bool,"TarArchive*, const char*, const char*" +Function,+,tar_archive_unpack_to,_Bool,"TarArchive*, const char*, Storage_name_converter" +Function,-,tempnam,char*,"const char*, const char*" +Function,+,text_box_alloc,TextBox*, +Function,+,text_box_free,void,TextBox* +Function,+,text_box_get_view,View*,TextBox* +Function,+,text_box_reset,void,TextBox* +Function,+,text_box_set_focus,void,"TextBox*, TextBoxFocus" +Function,+,text_box_set_font,void,"TextBox*, TextBoxFont" +Function,+,text_box_set_text,void,"TextBox*, const char*" +Function,+,text_input_alloc,TextInput*, +Function,+,text_input_free,void,TextInput* +Function,+,text_input_get_validator_callback,TextInputValidatorCallback,TextInput* +Function,+,text_input_get_validator_callback_context,void*,TextInput* +Function,+,text_input_get_view,View*,TextInput* +Function,+,text_input_reset,void,TextInput* +Function,+,text_input_set_header_text,void,"TextInput*, const char*" +Function,+,text_input_set_result_callback,void,"TextInput*, TextInputCallback, void*, char*, size_t, _Bool" +Function,+,text_input_set_validator,void,"TextInput*, TextInputValidatorCallback, void*" +Function,-,time,time_t,time_t* +Function,-,timingsafe_bcmp,int,"const void*, const void*, size_t" +Function,-,timingsafe_memcmp,int,"const void*, const void*, size_t" +Function,-,tmpfile,FILE*, +Function,-,tmpnam,char*,char* +Function,-,toascii,int,int +Function,-,toascii_l,int,"int, locale_t" +Function,-,tolower,int,int +Function,-,tolower_l,int,"int, locale_t" +Function,-,toupper,int,int +Function,-,toupper_l,int,"int, locale_t" +Function,-,tzset,void, +Function,-,uECC_compress,void,"const uint8_t*, uint8_t*, uECC_Curve" +Function,+,uECC_compute_public_key,int,"const uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_curve_private_key_size,int,uECC_Curve +Function,-,uECC_curve_public_key_size,int,uECC_Curve +Function,-,uECC_decompress,void,"const uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_get_rng,uECC_RNG_Function, +Function,-,uECC_make_key,int,"uint8_t*, uint8_t*, uECC_Curve" +Function,-,uECC_secp160r1,uECC_Curve, +Function,-,uECC_secp192r1,uECC_Curve, +Function,-,uECC_secp224r1,uECC_Curve, +Function,-,uECC_secp256k1,uECC_Curve, +Function,+,uECC_secp256r1,uECC_Curve, +Function,+,uECC_set_rng,void,uECC_RNG_Function +Function,-,uECC_shared_secret,int,"const uint8_t*, const uint8_t*, uint8_t*, uECC_Curve" +Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uECC_Curve" +Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" +Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" +Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" +Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" +Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ungetc,int,"int, FILE*" +Function,-,unsetenv,int,const char* +Function,-,usbd_poll,void,usbd_device* +Function,-,utoa,char*,"unsigned, char*, int" +Function,-,uxListRemove,UBaseType_t,ListItem_t* +Function,-,uxTaskGetNumberOfTasks,UBaseType_t, +Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t +Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t +Function,-,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t +Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t +Function,-,uxTaskResetEventItemValue,TickType_t, +Function,-,uxTimerGetReloadMode,UBaseType_t,TimerHandle_t +Function,-,uxTimerGetTimerNumber,UBaseType_t,TimerHandle_t +Function,-,vApplicationGetIdleTaskMemory,void,"StaticTask_t**, StackType_t**, uint32_t*" +Function,-,vApplicationGetTimerTaskMemory,void,"StaticTask_t**, StackType_t**, uint32_t*" +Function,-,vListInitialise,void,List_t* +Function,-,vListInitialiseItem,void,ListItem_t* +Function,-,vListInsert,void,"List_t*, ListItem_t*" +Function,-,vListInsertEnd,void,"List_t*, ListItem_t*" +Function,-,vPortDefineHeapRegions,void,const HeapRegion_t* +Function,-,vPortEndScheduler,void, +Function,+,vPortEnterCritical,void, +Function,+,vPortExitCritical,void, +Function,-,vPortFree,void,void* +Function,-,vPortGetHeapStats,void,HeapStats_t* +Function,-,vPortInitialiseBlocks,void, +Function,-,vPortSuppressTicksAndSleep,void,TickType_t +Function,-,vTaskAllocateMPURegions,void,"TaskHandle_t, const MemoryRegion_t*" +Function,-,vTaskDelay,void,const TickType_t +Function,-,vTaskDelete,void,TaskHandle_t +Function,-,vTaskEndScheduler,void, +Function,-,vTaskGenericNotifyGiveFromISR,void,"TaskHandle_t, UBaseType_t, BaseType_t*" +Function,-,vTaskGetInfo,void,"TaskHandle_t, TaskStatus_t*, BaseType_t, eTaskState" +Function,-,vTaskGetRunTimeStats,void,char* +Function,-,vTaskInternalSetTimeOutState,void,TimeOut_t* +Function,-,vTaskList,void,char* +Function,-,vTaskMissedYield,void, +Function,-,vTaskPlaceOnEventList,void,"List_t*, const TickType_t" +Function,-,vTaskPlaceOnEventListRestricted,void,"List_t*, TickType_t, const BaseType_t" +Function,-,vTaskPlaceOnUnorderedEventList,void,"List_t*, const TickType_t, const TickType_t" +Function,-,vTaskPriorityDisinheritAfterTimeout,void,"const TaskHandle_t, UBaseType_t" +Function,+,vTaskPrioritySet,void,"TaskHandle_t, UBaseType_t" +Function,-,vTaskRemoveFromUnorderedEventList,void,"ListItem_t*, const TickType_t" +Function,-,vTaskResume,void,TaskHandle_t +Function,-,vTaskSetTaskNumber,void,"TaskHandle_t, const UBaseType_t" +Function,-,vTaskSetThreadLocalStoragePointer,void,"TaskHandle_t, BaseType_t, void*" +Function,-,vTaskSetTimeOutState,void,TimeOut_t* +Function,-,vTaskStartScheduler,void, +Function,-,vTaskStepTick,void,TickType_t +Function,-,vTaskSuspend,void,TaskHandle_t +Function,-,vTaskSuspendAll,void, +Function,-,vTaskSwitchContext,void, +Function,-,vTimerSetReloadMode,void,"TimerHandle_t, const BaseType_t" +Function,-,vTimerSetTimerID,void,"TimerHandle_t, void*" +Function,-,vTimerSetTimerNumber,void,"TimerHandle_t, UBaseType_t" +Function,+,validator_is_file_alloc_init,ValidatorIsFile*,"const char*, const char*, const char*" +Function,+,validator_is_file_callback,_Bool,"const char*, FuriString*, void*" +Function,+,validator_is_file_free,void,ValidatorIsFile* +Function,+,value_index_bool,uint8_t,"const _Bool, const _Bool[], uint8_t" +Function,+,value_index_float,uint8_t,"const float, const float[], uint8_t" +Function,+,value_index_uint32,uint8_t,"const uint32_t, const uint32_t[], uint8_t" +Function,+,variable_item_get_context,void*,VariableItem* +Function,+,variable_item_get_current_value_index,uint8_t,VariableItem* +Function,+,variable_item_list_add,VariableItem*,"VariableItemList*, const char*, uint8_t, VariableItemChangeCallback, void*" +Function,+,variable_item_list_alloc,VariableItemList*, +Function,+,variable_item_list_free,void,VariableItemList* +Function,+,variable_item_list_get_selected_item_index,uint8_t,VariableItemList* +Function,+,variable_item_list_get_view,View*,VariableItemList* +Function,+,variable_item_list_reset,void,VariableItemList* +Function,+,variable_item_list_set_enter_callback,void,"VariableItemList*, VariableItemListEnterCallback, void*" +Function,+,variable_item_list_set_selected_item,void,"VariableItemList*, uint8_t" +Function,+,variable_item_set_current_value_index,void,"VariableItem*, uint8_t" +Function,+,variable_item_set_current_value_text,void,"VariableItem*, const char*" +Function,+,variable_item_set_values_count,void,"VariableItem*, uint8_t" +Function,-,vasiprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vasniprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasnprintf,char*,"char*, size_t*, const char*, __gnuc_va_list" +Function,-,vasprintf,int,"char**, const char*, __gnuc_va_list" +Function,-,vdiprintf,int,"int, const char*, __gnuc_va_list" +Function,-,vdprintf,int,"int, const char*, __gnuc_va_list" +Function,+,version_get,const Version*, +Function,+,version_get_builddate,const char*,const Version* +Function,+,version_get_dirty_flag,_Bool,const Version* +Function,+,version_get_gitbranch,const char*,const Version* +Function,+,version_get_gitbranchnum,const char*,const Version* +Function,+,version_get_githash,const char*,const Version* +Function,+,version_get_target,uint8_t,const Version* +Function,+,version_get_version,const char*,const Version* +Function,-,vfiprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfiscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfprintf,int,"FILE*, const char*, __gnuc_va_list" +Function,-,vfscanf,int,"FILE*, const char*, __gnuc_va_list" +Function,+,view_alloc,View*, +Function,+,view_allocate_model,void,"View*, ViewModelType, size_t" +Function,+,view_commit_model,void,"View*, _Bool" +Function,+,view_dispatcher_add_view,void,"ViewDispatcher*, uint32_t, View*" +Function,+,view_dispatcher_alloc,ViewDispatcher*, +Function,+,view_dispatcher_attach_to_gui,void,"ViewDispatcher*, Gui*, ViewDispatcherType" +Function,+,view_dispatcher_enable_queue,void,ViewDispatcher* +Function,+,view_dispatcher_free,void,ViewDispatcher* +Function,+,view_dispatcher_remove_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_run,void,ViewDispatcher* +Function,+,view_dispatcher_send_custom_event,void,"ViewDispatcher*, uint32_t" +Function,+,view_dispatcher_send_to_back,void,ViewDispatcher* +Function,+,view_dispatcher_send_to_front,void,ViewDispatcher* +Function,+,view_dispatcher_set_custom_event_callback,void,"ViewDispatcher*, ViewDispatcherCustomEventCallback" +Function,+,view_dispatcher_set_event_callback_context,void,"ViewDispatcher*, void*" +Function,+,view_dispatcher_set_navigation_event_callback,void,"ViewDispatcher*, ViewDispatcherNavigationEventCallback" +Function,+,view_dispatcher_set_tick_event_callback,void,"ViewDispatcher*, ViewDispatcherTickEventCallback, uint32_t" +Function,+,view_dispatcher_stop,void,ViewDispatcher* +Function,+,view_dispatcher_switch_to_view,void,"ViewDispatcher*, uint32_t" +Function,+,view_free,void,View* +Function,+,view_free_model,void,View* +Function,+,view_get_model,void*,View* +Function,+,view_port_alloc,ViewPort*, +Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" +Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" +Function,+,view_port_free,void,ViewPort* +Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* +Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" +Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_set_height,void,"ViewPort*, uint8_t" +Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" +Function,+,view_port_set_width,void,"ViewPort*, uint8_t" +Function,+,view_port_update,void,ViewPort* +Function,+,view_set_context,void,"View*, void*" +Function,+,view_set_custom_callback,void,"View*, ViewCustomCallback" +Function,+,view_set_draw_callback,void,"View*, ViewDrawCallback" +Function,+,view_set_enter_callback,void,"View*, ViewCallback" +Function,+,view_set_exit_callback,void,"View*, ViewCallback" +Function,+,view_set_input_callback,void,"View*, ViewInputCallback" +Function,+,view_set_orientation,void,"View*, ViewOrientation" +Function,+,view_set_previous_callback,void,"View*, ViewNavigationCallback" +Function,+,view_set_update_callback,void,"View*, ViewUpdateCallback" +Function,+,view_set_update_callback_context,void,"View*, void*" +Function,+,view_stack_add_view,void,"ViewStack*, View*" +Function,+,view_stack_alloc,ViewStack*, +Function,+,view_stack_free,void,ViewStack* +Function,+,view_stack_get_view,View*,ViewStack* +Function,+,view_stack_remove_view,void,"ViewStack*, View*" +Function,+,view_tie_icon_animation,void,"View*, IconAnimation*" +Function,-,viprintf,int,"const char*, __gnuc_va_list" +Function,-,viscanf,int,"const char*, __gnuc_va_list" +Function,-,vprintf,int,"const char*, __gnuc_va_list" +Function,-,vscanf,int,"const char*, __gnuc_va_list" +Function,-,vsiprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsiscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,vsniprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsnprintf,int,"char*, size_t, const char*, __gnuc_va_list" +Function,-,vsprintf,int,"char*, const char*, __gnuc_va_list" +Function,-,vsscanf,int,"const char*, const char*, __gnuc_va_list" +Function,-,wcstombs,size_t,"char*, const wchar_t*, size_t" +Function,-,wctomb,int,"char*, wchar_t" +Function,+,widget_add_button_element,void,"Widget*, GuiButtonType, const char*, ButtonCallback, void*" +Function,+,widget_add_frame_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, uint8_t" +Function,+,widget_add_icon_element,void,"Widget*, uint8_t, uint8_t, const Icon*" +Function,+,widget_add_string_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_string_multiline_element,void,"Widget*, uint8_t, uint8_t, Align, Align, Font, const char*" +Function,+,widget_add_text_box_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, Align, Align, const char*, _Bool" +Function,+,widget_add_text_scroll_element,void,"Widget*, uint8_t, uint8_t, uint8_t, uint8_t, const char*" +Function,+,widget_alloc,Widget*, +Function,+,widget_free,void,Widget* +Function,+,widget_get_view,View*,Widget* +Function,+,widget_reset,void,Widget* +Function,-,write_mutex,_Bool,"ValueMutex*, void*, size_t, uint32_t" +Function,-,xPortGetFreeHeapSize,size_t, +Function,-,xPortGetMinimumEverFreeHeapSize,size_t, +Function,-,xPortStartScheduler,BaseType_t, +Function,-,xTaskAbortDelay,BaseType_t,TaskHandle_t +Function,-,xTaskCallApplicationTaskHook,BaseType_t,"TaskHandle_t, void*" +Function,-,xTaskCatchUpTicks,BaseType_t,TickType_t +Function,-,xTaskCheckForTimeOut,BaseType_t,"TimeOut_t*, TickType_t*" +Function,-,xTaskCreate,BaseType_t,"TaskFunction_t, const char*, const uint16_t, void*, UBaseType_t, TaskHandle_t*" +Function,-,xTaskCreateStatic,TaskHandle_t,"TaskFunction_t, const char*, const uint32_t, void*, UBaseType_t, StackType_t*, StaticTask_t*" +Function,-,xTaskDelayUntil,BaseType_t,"TickType_t*, const TickType_t" +Function,-,xTaskGenericNotify,BaseType_t,"TaskHandle_t, UBaseType_t, uint32_t, eNotifyAction, uint32_t*" +Function,-,xTaskGenericNotifyFromISR,BaseType_t,"TaskHandle_t, UBaseType_t, uint32_t, eNotifyAction, uint32_t*, BaseType_t*" +Function,-,xTaskGenericNotifyStateClear,BaseType_t,"TaskHandle_t, UBaseType_t" +Function,-,xTaskGenericNotifyWait,BaseType_t,"UBaseType_t, uint32_t, uint32_t, uint32_t*, TickType_t" +Function,-,xTaskGetCurrentTaskHandle,TaskHandle_t, +Function,+,xTaskGetHandle,TaskHandle_t,const char* +Function,-,xTaskGetIdleTaskHandle,TaskHandle_t, +Function,+,xTaskGetSchedulerState,BaseType_t, +Function,+,xTaskGetTickCount,TickType_t, +Function,-,xTaskGetTickCountFromISR,TickType_t, +Function,-,xTaskIncrementTick,BaseType_t, +Function,-,xTaskPriorityDisinherit,BaseType_t,const TaskHandle_t +Function,-,xTaskPriorityInherit,BaseType_t,const TaskHandle_t +Function,-,xTaskRemoveFromEventList,BaseType_t,const List_t* +Function,-,xTaskResumeAll,BaseType_t, +Function,-,xTaskResumeFromISR,BaseType_t,TaskHandle_t +Function,-,xTimerCreate,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t" +Function,-,xTimerCreateStatic,TimerHandle_t,"const char*, const TickType_t, const BaseType_t, void*, TimerCallbackFunction_t, StaticTimer_t*" +Function,-,xTimerCreateTimerTask,BaseType_t, +Function,-,xTimerGenericCommand,BaseType_t,"TimerHandle_t, const BaseType_t, const TickType_t, BaseType_t*, const TickType_t" +Function,-,xTimerGetExpiryTime,TickType_t,TimerHandle_t +Function,-,xTimerGetPeriod,TickType_t,TimerHandle_t +Function,-,xTimerGetReloadMode,BaseType_t,TimerHandle_t +Function,-,xTimerGetTimerDaemonTaskHandle,TaskHandle_t, +Function,-,xTimerIsTimerActive,BaseType_t,TimerHandle_t +Function,-,xTimerPendFunctionCall,BaseType_t,"PendedFunction_t, void*, uint32_t, TickType_t" +Function,-,xTimerPendFunctionCallFromISR,BaseType_t,"PendedFunction_t, void*, uint32_t, BaseType_t*" +Variable,-,AHBPrescTable,const uint32_t[16], +Variable,-,APBPrescTable,const uint32_t[8], +Variable,-,ITM_RxBuffer,volatile int32_t, +Variable,-,MSIRangeTable,const uint32_t[16], +Variable,-,SmpsPrescalerTable,const uint32_t[4][6], +Variable,+,SystemCoreClock,uint32_t, +Variable,+,_ctype_,const char[], +Variable,-,_daylight,int, +Variable,+,_global_impure_ptr,_reent*, +Variable,+,_impure_ptr,_reent*, +Variable,-,_sys_errlist,const char*[], +Variable,-,_sys_nerr,int, +Variable,-,_timezone,long, +Variable,-,_tzname,char*[2], +Variable,+,cli_vcp,CliSession, +Variable,+,furi_hal_i2c_bus_external,FuriHalI2cBus, +Variable,+,furi_hal_i2c_bus_power,FuriHalI2cBus, +Variable,+,furi_hal_i2c_handle_external,FuriHalI2cBusHandle, +Variable,+,furi_hal_i2c_handle_power,FuriHalI2cBusHandle, +Variable,+,furi_hal_sd_spi_handle,FuriHalSpiBusHandle*, +Variable,+,furi_hal_spi_bus_d,FuriHalSpiBus, +Variable,+,furi_hal_spi_bus_handle_display,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_external,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, +Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,gpio_button_back,const GpioPin, +Variable,+,gpio_button_down,const GpioPin, +Variable,+,gpio_button_left,const GpioPin, +Variable,+,gpio_button_ok,const GpioPin, +Variable,+,gpio_button_right,const GpioPin, +Variable,+,gpio_button_up,const GpioPin, +Variable,+,gpio_display_cs,const GpioPin, +Variable,+,gpio_display_di,const GpioPin, +Variable,+,gpio_display_rst_n,const GpioPin, +Variable,+,gpio_ext_pa0,const GpioPin, +Variable,+,gpio_ext_pa1,const GpioPin, +Variable,+,gpio_ext_pa15,const GpioPin, +Variable,+,gpio_ext_pa2,const GpioPin, +Variable,+,gpio_ext_pa4,const GpioPin, +Variable,+,gpio_ext_pa5,const GpioPin, +Variable,+,gpio_ext_pa6,const GpioPin, +Variable,+,gpio_ext_pa7,const GpioPin, +Variable,+,gpio_ext_pb13,const GpioPin, +Variable,+,gpio_ext_pb2,const GpioPin, +Variable,+,gpio_ext_pb3,const GpioPin, +Variable,+,gpio_ext_pb4,const GpioPin, +Variable,+,gpio_ext_pb5,const GpioPin, +Variable,+,gpio_ext_pb9,const GpioPin, +Variable,+,gpio_ext_pc0,const GpioPin, +Variable,+,gpio_ext_pc1,const GpioPin, +Variable,+,gpio_ext_pc3,const GpioPin, +Variable,+,gpio_ext_pc4,const GpioPin, +Variable,+,gpio_ext_pc5,const GpioPin, +Variable,+,gpio_ext_pd0,const GpioPin, +Variable,+,gpio_ext_pe4,const GpioPin, +Variable,+,gpio_i2c_power_scl,const GpioPin, +Variable,+,gpio_i2c_power_sda,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, +Variable,+,gpio_sdcard_cd,const GpioPin, +Variable,+,gpio_sdcard_cs,const GpioPin, +Variable,+,gpio_speaker,const GpioPin, +Variable,+,gpio_spi_d_miso,const GpioPin, +Variable,+,gpio_spi_d_mosi,const GpioPin, +Variable,+,gpio_spi_d_sck,const GpioPin, +Variable,+,gpio_usart_rx,const GpioPin, +Variable,+,gpio_usart_tx,const GpioPin, +Variable,+,gpio_usb_dm,const GpioPin, +Variable,+,gpio_usb_dp,const GpioPin, +Variable,+,ibutton_gpio,const GpioPin, +Variable,+,input_pins,const InputPin[], +Variable,+,input_pins_count,const size_t, +Variable,+,message_blink_set_color_blue,const NotificationMessage, +Variable,+,message_blink_set_color_cyan,const NotificationMessage, +Variable,+,message_blink_set_color_green,const NotificationMessage, +Variable,+,message_blink_set_color_magenta,const NotificationMessage, +Variable,+,message_blink_set_color_red,const NotificationMessage, +Variable,+,message_blink_set_color_white,const NotificationMessage, +Variable,+,message_blink_set_color_yellow,const NotificationMessage, +Variable,+,message_blink_start_10,const NotificationMessage, +Variable,+,message_blink_start_100,const NotificationMessage, +Variable,+,message_blink_stop,const NotificationMessage, +Variable,+,message_blue_0,const NotificationMessage, +Variable,+,message_blue_255,const NotificationMessage, +Variable,+,message_click,const NotificationMessage, +Variable,+,message_delay_1,const NotificationMessage, +Variable,+,message_delay_10,const NotificationMessage, +Variable,+,message_delay_100,const NotificationMessage, +Variable,+,message_delay_1000,const NotificationMessage, +Variable,+,message_delay_25,const NotificationMessage, +Variable,+,message_delay_250,const NotificationMessage, +Variable,+,message_delay_50,const NotificationMessage, +Variable,+,message_delay_500,const NotificationMessage, +Variable,+,message_display_backlight_enforce_auto,const NotificationMessage, +Variable,+,message_display_backlight_enforce_on,const NotificationMessage, +Variable,+,message_display_backlight_off,const NotificationMessage, +Variable,+,message_display_backlight_on,const NotificationMessage, +Variable,+,message_do_not_reset,const NotificationMessage, +Variable,+,message_force_display_brightness_setting_1f,const NotificationMessage, +Variable,+,message_force_speaker_volume_setting_1f,const NotificationMessage, +Variable,+,message_force_vibro_setting_off,const NotificationMessage, +Variable,+,message_force_vibro_setting_on,const NotificationMessage, +Variable,+,message_green_0,const NotificationMessage, +Variable,+,message_green_255,const NotificationMessage, +Variable,+,message_note_a0,const NotificationMessage, +Variable,+,message_note_a1,const NotificationMessage, +Variable,+,message_note_a2,const NotificationMessage, +Variable,+,message_note_a3,const NotificationMessage, +Variable,+,message_note_a4,const NotificationMessage, +Variable,+,message_note_a5,const NotificationMessage, +Variable,+,message_note_a6,const NotificationMessage, +Variable,+,message_note_a7,const NotificationMessage, +Variable,+,message_note_a8,const NotificationMessage, +Variable,+,message_note_as0,const NotificationMessage, +Variable,+,message_note_as1,const NotificationMessage, +Variable,+,message_note_as2,const NotificationMessage, +Variable,+,message_note_as3,const NotificationMessage, +Variable,+,message_note_as4,const NotificationMessage, +Variable,+,message_note_as5,const NotificationMessage, +Variable,+,message_note_as6,const NotificationMessage, +Variable,+,message_note_as7,const NotificationMessage, +Variable,+,message_note_as8,const NotificationMessage, +Variable,+,message_note_b0,const NotificationMessage, +Variable,+,message_note_b1,const NotificationMessage, +Variable,+,message_note_b2,const NotificationMessage, +Variable,+,message_note_b3,const NotificationMessage, +Variable,+,message_note_b4,const NotificationMessage, +Variable,+,message_note_b5,const NotificationMessage, +Variable,+,message_note_b6,const NotificationMessage, +Variable,+,message_note_b7,const NotificationMessage, +Variable,+,message_note_b8,const NotificationMessage, +Variable,+,message_note_c0,const NotificationMessage, +Variable,+,message_note_c1,const NotificationMessage, +Variable,+,message_note_c2,const NotificationMessage, +Variable,+,message_note_c3,const NotificationMessage, +Variable,+,message_note_c4,const NotificationMessage, +Variable,+,message_note_c5,const NotificationMessage, +Variable,+,message_note_c6,const NotificationMessage, +Variable,+,message_note_c7,const NotificationMessage, +Variable,+,message_note_c8,const NotificationMessage, +Variable,+,message_note_cs0,const NotificationMessage, +Variable,+,message_note_cs1,const NotificationMessage, +Variable,+,message_note_cs2,const NotificationMessage, +Variable,+,message_note_cs3,const NotificationMessage, +Variable,+,message_note_cs4,const NotificationMessage, +Variable,+,message_note_cs5,const NotificationMessage, +Variable,+,message_note_cs6,const NotificationMessage, +Variable,+,message_note_cs7,const NotificationMessage, +Variable,+,message_note_cs8,const NotificationMessage, +Variable,+,message_note_d0,const NotificationMessage, +Variable,+,message_note_d1,const NotificationMessage, +Variable,+,message_note_d2,const NotificationMessage, +Variable,+,message_note_d3,const NotificationMessage, +Variable,+,message_note_d4,const NotificationMessage, +Variable,+,message_note_d5,const NotificationMessage, +Variable,+,message_note_d6,const NotificationMessage, +Variable,+,message_note_d7,const NotificationMessage, +Variable,+,message_note_d8,const NotificationMessage, +Variable,+,message_note_ds0,const NotificationMessage, +Variable,+,message_note_ds1,const NotificationMessage, +Variable,+,message_note_ds2,const NotificationMessage, +Variable,+,message_note_ds3,const NotificationMessage, +Variable,+,message_note_ds4,const NotificationMessage, +Variable,+,message_note_ds5,const NotificationMessage, +Variable,+,message_note_ds6,const NotificationMessage, +Variable,+,message_note_ds7,const NotificationMessage, +Variable,+,message_note_ds8,const NotificationMessage, +Variable,+,message_note_e0,const NotificationMessage, +Variable,+,message_note_e1,const NotificationMessage, +Variable,+,message_note_e2,const NotificationMessage, +Variable,+,message_note_e3,const NotificationMessage, +Variable,+,message_note_e4,const NotificationMessage, +Variable,+,message_note_e5,const NotificationMessage, +Variable,+,message_note_e6,const NotificationMessage, +Variable,+,message_note_e7,const NotificationMessage, +Variable,+,message_note_e8,const NotificationMessage, +Variable,+,message_note_f0,const NotificationMessage, +Variable,+,message_note_f1,const NotificationMessage, +Variable,+,message_note_f2,const NotificationMessage, +Variable,+,message_note_f3,const NotificationMessage, +Variable,+,message_note_f4,const NotificationMessage, +Variable,+,message_note_f5,const NotificationMessage, +Variable,+,message_note_f6,const NotificationMessage, +Variable,+,message_note_f7,const NotificationMessage, +Variable,+,message_note_f8,const NotificationMessage, +Variable,+,message_note_fs0,const NotificationMessage, +Variable,+,message_note_fs1,const NotificationMessage, +Variable,+,message_note_fs2,const NotificationMessage, +Variable,+,message_note_fs3,const NotificationMessage, +Variable,+,message_note_fs4,const NotificationMessage, +Variable,+,message_note_fs5,const NotificationMessage, +Variable,+,message_note_fs6,const NotificationMessage, +Variable,+,message_note_fs7,const NotificationMessage, +Variable,+,message_note_fs8,const NotificationMessage, +Variable,+,message_note_g0,const NotificationMessage, +Variable,+,message_note_g1,const NotificationMessage, +Variable,+,message_note_g2,const NotificationMessage, +Variable,+,message_note_g3,const NotificationMessage, +Variable,+,message_note_g4,const NotificationMessage, +Variable,+,message_note_g5,const NotificationMessage, +Variable,+,message_note_g6,const NotificationMessage, +Variable,+,message_note_g7,const NotificationMessage, +Variable,+,message_note_g8,const NotificationMessage, +Variable,+,message_note_gs0,const NotificationMessage, +Variable,+,message_note_gs1,const NotificationMessage, +Variable,+,message_note_gs2,const NotificationMessage, +Variable,+,message_note_gs3,const NotificationMessage, +Variable,+,message_note_gs4,const NotificationMessage, +Variable,+,message_note_gs5,const NotificationMessage, +Variable,+,message_note_gs6,const NotificationMessage, +Variable,+,message_note_gs7,const NotificationMessage, +Variable,+,message_note_gs8,const NotificationMessage, +Variable,+,message_red_0,const NotificationMessage, +Variable,+,message_red_255,const NotificationMessage, +Variable,+,message_sound_off,const NotificationMessage, +Variable,+,message_vibro_off,const NotificationMessage, +Variable,+,message_vibro_on,const NotificationMessage, +Variable,+,periph_power,const GpioPin, +Variable,+,sequence_audiovisual_alert,const NotificationSequence, +Variable,+,sequence_blink_blue_10,const NotificationSequence, +Variable,+,sequence_blink_blue_100,const NotificationSequence, +Variable,+,sequence_blink_cyan_10,const NotificationSequence, +Variable,+,sequence_blink_cyan_100,const NotificationSequence, +Variable,+,sequence_blink_green_10,const NotificationSequence, +Variable,+,sequence_blink_green_100,const NotificationSequence, +Variable,+,sequence_blink_magenta_10,const NotificationSequence, +Variable,+,sequence_blink_magenta_100,const NotificationSequence, +Variable,+,sequence_blink_red_10,const NotificationSequence, +Variable,+,sequence_blink_red_100,const NotificationSequence, +Variable,+,sequence_blink_start_blue,const NotificationSequence, +Variable,+,sequence_blink_start_cyan,const NotificationSequence, +Variable,+,sequence_blink_start_green,const NotificationSequence, +Variable,+,sequence_blink_start_magenta,const NotificationSequence, +Variable,+,sequence_blink_start_red,const NotificationSequence, +Variable,+,sequence_blink_start_yellow,const NotificationSequence, +Variable,+,sequence_blink_stop,const NotificationSequence, +Variable,+,sequence_blink_white_100,const NotificationSequence, +Variable,+,sequence_blink_yellow_10,const NotificationSequence, +Variable,+,sequence_blink_yellow_100,const NotificationSequence, +Variable,+,sequence_charged,const NotificationSequence, +Variable,+,sequence_charging,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_auto,const NotificationSequence, +Variable,+,sequence_display_backlight_enforce_on,const NotificationSequence, +Variable,+,sequence_display_backlight_off,const NotificationSequence, +Variable,+,sequence_display_backlight_off_delay_1000,const NotificationSequence, +Variable,+,sequence_display_backlight_on,const NotificationSequence, +Variable,+,sequence_double_vibro,const NotificationSequence, +Variable,+,sequence_error,const NotificationSequence, +Variable,+,sequence_not_charging,const NotificationSequence, +Variable,+,sequence_reset_blue,const NotificationSequence, +Variable,+,sequence_reset_display,const NotificationSequence, +Variable,+,sequence_reset_green,const NotificationSequence, +Variable,+,sequence_reset_red,const NotificationSequence, +Variable,+,sequence_reset_rgb,const NotificationSequence, +Variable,+,sequence_reset_sound,const NotificationSequence, +Variable,+,sequence_reset_vibro,const NotificationSequence, +Variable,+,sequence_set_blue_255,const NotificationSequence, +Variable,+,sequence_set_green_255,const NotificationSequence, +Variable,+,sequence_set_only_blue_255,const NotificationSequence, +Variable,+,sequence_set_only_green_255,const NotificationSequence, +Variable,+,sequence_set_only_red_255,const NotificationSequence, +Variable,+,sequence_set_red_255,const NotificationSequence, +Variable,+,sequence_set_vibro_on,const NotificationSequence, +Variable,+,sequence_single_vibro,const NotificationSequence, +Variable,+,sequence_solid_yellow,const NotificationSequence, +Variable,+,sequence_success,const NotificationSequence, +Variable,-,suboptarg,char*, +Variable,+,usb_cdc_dual,FuriHalUsbInterface, +Variable,+,usb_cdc_single,FuriHalUsbInterface, +Variable,+,usb_hid,FuriHalUsbInterface, +Variable,+,usb_hid_u2f,FuriHalUsbInterface, +Variable,+,usbd_devfs,const usbd_driver, +Variable,+,vibro_gpio,const GpioPin, diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c new file mode 100644 index 000000000..ad35a0074 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -0,0 +1,91 @@ +#include +#include + +#include + +#include + +#define TAG "FuriHal" + +void furi_hal_init_early() { + furi_hal_cortex_init_early(); + + furi_hal_clock_init_early(); + + furi_hal_resources_init_early(); + + furi_hal_os_init(); + + furi_hal_spi_config_init_early(); + + furi_hal_i2c_init_early(); + furi_hal_light_init(); + + furi_hal_rtc_init_early(); +} + +void furi_hal_deinit_early() { + furi_hal_rtc_deinit_early(); + + furi_hal_i2c_deinit_early(); + furi_hal_spi_config_deinit_early(); + + furi_hal_resources_deinit_early(); + + furi_hal_clock_deinit_early(); +} + +void furi_hal_init() { + furi_hal_mpu_init(); + furi_hal_clock_init(); + furi_hal_console_init(); + furi_hal_rtc_init(); + + furi_hal_interrupt_init(); + + furi_hal_flash_init(); + + furi_hal_resources_init(); + FURI_LOG_I(TAG, "GPIO OK"); + + furi_hal_version_init(); + + furi_hal_spi_config_init(); + + furi_hal_speaker_init(); + FURI_LOG_I(TAG, "Speaker OK"); + + furi_hal_crypto_init(); + + // USB +#ifndef FURI_RAM_EXEC + furi_hal_usb_init(); + FURI_LOG_I(TAG, "USB OK"); +#endif + + furi_hal_i2c_init(); + + // High Level + furi_hal_power_init(); + furi_hal_light_init(); +#ifndef FURI_RAM_EXEC + furi_hal_vibro_init(); +#endif + furi_hal_bt_init(); + furi_hal_compress_icon_init(); + + // FatFS driver initialization + MX_FATFS_Init(); + FURI_LOG_I(TAG, "FATFS OK"); +} + +void furi_hal_switch(void* address) { + __set_BASEPRI(0); + asm volatile("ldr r3, [%0] \n" + "msr msp, r3 \n" + "ldr r3, [%1] \n" + "mov pc, r3 \n" + : + : "r"(address), "r"(address + 0x4) + : "r3"); +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c new file mode 100644 index 000000000..dafeefdd0 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -0,0 +1,201 @@ +#include +#include + +#include +#include + +const GpioPin vibro_gpio = {.port = GPIOA, .pin = LL_GPIO_PIN_8}; +const GpioPin ibutton_gpio = {.port = GPIOB, .pin = LL_GPIO_PIN_14}; + +const GpioPin gpio_display_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_display_rst_n = {.port = GPIOB, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_display_di = {.port = GPIOB, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_sdcard_cs = {.port = GPIOC, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_sdcard_cd = {.port = GPIOC, .pin = LL_GPIO_PIN_10}; + +const GpioPin gpio_button_up = {.port = GPIOB, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_button_down = {.port = GPIOC, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_button_right = {.port = GPIOB, .pin = LL_GPIO_PIN_12}; +const GpioPin gpio_button_left = {.port = GPIOB, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_button_ok = {.port = GPIOH, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_button_back = {.port = GPIOC, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_spi_d_miso = {.port = GPIOC, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_spi_d_mosi = {.port = GPIOB, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_spi_d_sck = {.port = GPIOD, .pin = LL_GPIO_PIN_1}; + +const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; +const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_ext_pc5 = {.port = GPIOC, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pc4 = {.port = GPIOC, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa5 = {.port = GPIOA, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pb9 = {.port = GPIOB, .pin = LL_GPIO_PIN_9}; +const GpioPin gpio_ext_pa0 = {.port = GPIOA, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pa1 = {.port = GPIOA, .pin = LL_GPIO_PIN_1}; +const GpioPin gpio_ext_pa15 = {.port = GPIOA, .pin = LL_GPIO_PIN_15}; +const GpioPin gpio_ext_pe4 = {.port = GPIOE, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pa2 = {.port = GPIOA, .pin = LL_GPIO_PIN_2}; +const GpioPin gpio_ext_pb4 = {.port = GPIOB, .pin = LL_GPIO_PIN_4}; +const GpioPin gpio_ext_pb5 = {.port = GPIOB, .pin = LL_GPIO_PIN_5}; +const GpioPin gpio_ext_pd0 = {.port = GPIOD, .pin = LL_GPIO_PIN_0}; +const GpioPin gpio_ext_pb13 = {.port = GPIOB, .pin = LL_GPIO_PIN_13}; + +const GpioPin gpio_usart_tx = {.port = GPIOB, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_usart_rx = {.port = GPIOB, .pin = LL_GPIO_PIN_7}; + +const GpioPin gpio_i2c_power_sda = {.port = GPIOA, .pin = LL_GPIO_PIN_10}; +const GpioPin gpio_i2c_power_scl = {.port = GPIOA, .pin = LL_GPIO_PIN_9}; + +const GpioPin gpio_speaker = {.port = GPIOB, .pin = LL_GPIO_PIN_8}; + +const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; + +const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; +const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; + +const GpioPinRecord gpio_pins[] = { + {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, + {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, + + {.pin = &gpio_ext_pc5, .name = "PC5", .debug = false}, + {.pin = &gpio_ext_pc4, .name = "PC4", .debug = false}, + {.pin = &gpio_ext_pa5, .name = "PA5", .debug = false}, + {.pin = &gpio_ext_pb9, .name = "PB9", .debug = false}, + {.pin = &gpio_ext_pa0, .name = "PA0", .debug = false}, + {.pin = &gpio_ext_pa1, .name = "PA1", .debug = false}, + {.pin = &gpio_ext_pa15, .name = "PA15", .debug = false}, + {.pin = &gpio_ext_pe4, .name = "PE4", .debug = false}, + {.pin = &gpio_ext_pa2, .name = "PA2", .debug = false}, + {.pin = &gpio_ext_pb4, .name = "PB4", .debug = false}, + {.pin = &gpio_ext_pb5, .name = "PB5", .debug = false}, + {.pin = &gpio_ext_pd0, .name = "PD0", .debug = false}, + {.pin = &gpio_ext_pb13, .name = "PB13", .debug = false}, + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, + {.pin = &gpio_speaker, .name = "PB8", .debug = true}, +}; + +const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); + +const InputPin input_pins[] = { + {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, + {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, + {.gpio = &gpio_button_right, .key = InputKeyRight, .inverted = true, .name = "Right"}, + {.gpio = &gpio_button_left, .key = InputKeyLeft, .inverted = true, .name = "Left"}, + {.gpio = &gpio_button_ok, .key = InputKeyOk, .inverted = false, .name = "OK"}, + {.gpio = &gpio_button_back, .key = InputKeyBack, .inverted = true, .name = "Back"}, +}; + +const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); + +static void furi_hal_resources_init_input_pins(GpioMode mode) { + for(size_t i = 0; i < input_pins_count; i++) { + furi_hal_gpio_init( + input_pins[i].gpio, + mode, + (input_pins[i].inverted) ? GpioPullUp : GpioPullDown, + GpioSpeedLow); + } +} + +void furi_hal_resources_init_early() { + furi_hal_resources_init_input_pins(GpioModeInput); + + // SD Card stepdown control + furi_hal_gpio_write(&periph_power, 1); + furi_hal_gpio_init(&periph_power, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); + + // Display pins + furi_hal_gpio_write(&gpio_display_rst_n, 1); + furi_hal_gpio_init_simple(&gpio_display_rst_n, GpioModeOutputPushPull); + furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + + // Pullup display reset pin for shutdown + SET_BIT(PWR->PUCRB, gpio_display_rst_n.pin); + CLEAR_BIT(PWR->PDCRB, gpio_display_rst_n.pin); + SET_BIT(PWR->CR3, PWR_CR3_APC); + + // Hard reset USB + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeOutputOpenDrain); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + furi_delay_us(5); // Device Driven disconnect: 2.5us + extra to compensate cables + furi_hal_gpio_write(&gpio_usb_dm, 1); + furi_hal_gpio_write(&gpio_usb_dp, 1); + furi_hal_gpio_init_simple(&gpio_usb_dm, GpioModeAnalog); + furi_hal_gpio_init_simple(&gpio_usb_dp, GpioModeAnalog); + furi_hal_gpio_write(&gpio_usb_dm, 0); + furi_hal_gpio_write(&gpio_usb_dp, 0); + + // External header pins + furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pc3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pb3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +void furi_hal_resources_deinit_early() { + furi_hal_resources_init_input_pins(GpioModeAnalog); +} + +void furi_hal_resources_init() { + // Button pins + furi_hal_resources_init_input_pins(GpioModeInterruptRiseFall); + + // Display pins + furi_hal_gpio_init(&gpio_display_rst_n, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_display_rst_n, 0); + + furi_hal_gpio_init(&gpio_display_di, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_display_di, 0); + + // SD pins + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); + + furi_hal_gpio_init(&vibro_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI0_IRQn); + + NVIC_SetPriority(EXTI1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI1_IRQn); + + NVIC_SetPriority(EXTI2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI2_IRQn); + + NVIC_SetPriority(EXTI3_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI3_IRQn); + + NVIC_SetPriority(EXTI4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI4_IRQn); + + NVIC_SetPriority(EXTI9_5_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI9_5_IRQn); + + NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); + NVIC_EnableIRQ(EXTI15_10_IRQn); +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h new file mode 100644 index 000000000..ef2cdae7f --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -0,0 +1,116 @@ +#pragma once + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Input Related Constants */ +#define INPUT_DEBOUNCE_TICKS 4 + +/* Input Keys */ +typedef enum { + InputKeyUp, + InputKeyDown, + InputKeyRight, + InputKeyLeft, + InputKeyOk, + InputKeyBack, + InputKeyMAX, /**< Special value */ +} InputKey; + +/* Light */ +typedef enum { + LightRed = (1 << 0), + LightGreen = (1 << 1), + LightBlue = (1 << 2), + LightBacklight = (1 << 3), +} Light; + +typedef struct { + const GpioPin* gpio; + const InputKey key; + const bool inverted; + const char* name; +} InputPin; + +typedef struct { + const GpioPin* pin; + const char* name; + const bool debug; +} GpioPinRecord; + +extern const InputPin input_pins[]; +extern const size_t input_pins_count; + +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + +extern const GpioPin vibro_gpio; +extern const GpioPin ibutton_gpio; + +extern const GpioPin gpio_display_cs; +extern const GpioPin gpio_display_rst_n; +extern const GpioPin gpio_display_di; +extern const GpioPin gpio_sdcard_cs; +extern const GpioPin gpio_sdcard_cd; + +extern const GpioPin gpio_button_up; +extern const GpioPin gpio_button_down; +extern const GpioPin gpio_button_right; +extern const GpioPin gpio_button_left; +extern const GpioPin gpio_button_ok; +extern const GpioPin gpio_button_back; + +extern const GpioPin gpio_spi_d_miso; +extern const GpioPin gpio_spi_d_mosi; +extern const GpioPin gpio_spi_d_sck; + +extern const GpioPin gpio_ext_pc0; +extern const GpioPin gpio_ext_pc1; +extern const GpioPin gpio_ext_pc3; +extern const GpioPin gpio_ext_pb2; +extern const GpioPin gpio_ext_pb3; +extern const GpioPin gpio_ext_pa4; +extern const GpioPin gpio_ext_pa6; +extern const GpioPin gpio_ext_pa7; + +extern const GpioPin gpio_ext_pc5; +extern const GpioPin gpio_ext_pc4; +extern const GpioPin gpio_ext_pa5; +extern const GpioPin gpio_ext_pb9; +extern const GpioPin gpio_ext_pa0; +extern const GpioPin gpio_ext_pa1; +extern const GpioPin gpio_ext_pa15; +extern const GpioPin gpio_ext_pe4; +extern const GpioPin gpio_ext_pa2; +extern const GpioPin gpio_ext_pb4; +extern const GpioPin gpio_ext_pb5; +extern const GpioPin gpio_ext_pd0; +extern const GpioPin gpio_ext_pb13; + +extern const GpioPin gpio_usart_tx; +extern const GpioPin gpio_usart_rx; +extern const GpioPin gpio_i2c_power_sda; +extern const GpioPin gpio_i2c_power_scl; + +extern const GpioPin gpio_speaker; + +extern const GpioPin periph_power; + +extern const GpioPin gpio_usb_dm; +extern const GpioPin gpio_usb_dp; + +void furi_hal_resources_init_early(); + +void furi_hal_resources_deinit_early(); + +void furi_hal_resources_init(); + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f18/furi_hal/furi_hal_spi_config.c b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c new file mode 100644 index 000000000..0fbe55e2a --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_spi_config.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include + +#define TAG "FuriHalSpiConfig" + +/* SPI Presets */ + +const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_2EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV8, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV16, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV2, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { + .Mode = LL_SPI_MODE_MASTER, + .TransferDirection = LL_SPI_FULL_DUPLEX, + .DataWidth = LL_SPI_DATAWIDTH_8BIT, + .ClockPolarity = LL_SPI_POLARITY_LOW, + .ClockPhase = LL_SPI_PHASE_1EDGE, + .NSS = LL_SPI_NSS_SOFT, + .BaudRate = LL_SPI_BAUDRATEPRESCALER_DIV32, + .BitOrder = LL_SPI_MSB_FIRST, + .CRCCalculation = LL_SPI_CRCCALCULATION_DISABLE, + .CRCPoly = 7, +}; + +/* SPI Buses */ + +FuriMutex* furi_hal_spi_bus_r_mutex = NULL; + +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + +static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_r_mutex); + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_r_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_r_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ReleaseReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventDeactivate) { + FURI_CRITICAL_ENTER(); + LL_APB2_GRP1_ForceReset(LL_APB2_GRP1_PERIPH_SPI1); + FURI_CRITICAL_EXIT(); + } +} + +FuriHalSpiBus furi_hal_spi_bus_r = { + .spi = SPI1, + .callback = furi_hal_spi_bus_r_event_callback, +}; + +FuriMutex* furi_hal_spi_bus_d_mutex = NULL; + +static void furi_hal_spi_bus_d_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { + if(event == FuriHalSpiBusEventInit) { + furi_hal_spi_bus_d_mutex = furi_mutex_alloc(FuriMutexTypeNormal); + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + bus->current_handle = NULL; + } else if(event == FuriHalSpiBusEventDeinit) { + furi_mutex_free(furi_hal_spi_bus_d_mutex); + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventLock) { + furi_check(furi_mutex_acquire(furi_hal_spi_bus_d_mutex, FuriWaitForever) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventUnlock) { + furi_check(furi_mutex_release(furi_hal_spi_bus_d_mutex) == FuriStatusOk); + } else if(event == FuriHalSpiBusEventActivate) { + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ReleaseReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } else if(event == FuriHalSpiBusEventDeactivate) { + FURI_CRITICAL_ENTER(); + LL_APB1_GRP1_ForceReset(LL_APB1_GRP1_PERIPH_SPI2); + FURI_CRITICAL_EXIT(); + } +} + +FuriHalSpiBus furi_hal_spi_bus_d = { + .spi = SPI2, + .callback = furi_hal_spi_bus_d_event_callback, +}; + +/* SPI Bus Handles */ + +inline static void furi_hal_spi_bus_r_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +inline static void furi_hal_spi_bus_nfc_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + // Configure GPIOs in normal SPI mode + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventDeinit) { + // Configure GPIOs for st25r3916 Transparent mode + furi_hal_gpio_init(handle->sck, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->miso, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_init(handle->cs, GpioModeInput, GpioPullUp, GpioSpeedLow); + furi_hal_gpio_write(handle->mosi, false); + furi_hal_gpio_init(handle->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI1); + + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_init(handle->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(handle->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_external_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_external = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_external_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + +inline static void furi_hal_spi_bus_d_handle_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event, + const LL_SPI_InitTypeDef* preset) { + if(event == FuriHalSpiBusHandleEventInit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeOutputPushPull, GpioPullUp, GpioSpeedVeryHigh); + + furi_hal_gpio_init_ex( + handle->miso, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + handle->sck, + GpioModeAltFunctionPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + + } else if(event == FuriHalSpiBusHandleEventDeinit) { + furi_hal_gpio_write(handle->cs, true); + furi_hal_gpio_init(handle->cs, GpioModeAnalog, GpioPullUp, GpioSpeedLow); + } else if(event == FuriHalSpiBusHandleEventActivate) { + LL_SPI_Init(handle->bus->spi, (LL_SPI_InitTypeDef*)preset); + LL_SPI_SetRxFIFOThreshold(handle->bus->spi, LL_SPI_RX_FIFO_TH_QUARTER); + LL_SPI_Enable(handle->bus->spi); + furi_hal_gpio_write(handle->cs, false); + } else if(event == FuriHalSpiBusHandleEventDeactivate) { + furi_hal_gpio_write(handle->cs, true); + LL_SPI_Disable(handle->bus->spi); + } +} + +static void furi_hal_spi_bus_handle_display_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_4m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_display = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_display_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_display_cs, +}; + +static void furi_hal_spi_bus_handle_sd_fast_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_16m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_fast_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; + +static void furi_hal_spi_bus_handle_sd_slow_event_callback( + FuriHalSpiBusHandle* handle, + FuriHalSpiBusHandleEvent event) { + furi_hal_spi_bus_d_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_2m); +} + +FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow = { + .bus = &furi_hal_spi_bus_d, + .callback = furi_hal_spi_bus_handle_sd_slow_event_callback, + .miso = &gpio_spi_d_miso, + .mosi = &gpio_spi_d_mosi, + .sck = &gpio_spi_d_sck, + .cs = &gpio_sdcard_cs, +}; diff --git a/firmware/targets/f18/furi_hal/furi_hal_spi_config.h b/firmware/targets/f18/furi_hal/furi_hal_spi_config.h new file mode 100644 index 000000000..da39fbfa6 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_spi_config.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** Preset for ST25R916 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_2edge_low_8m; + +/** Preset for CC1101 */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_8m; + +/** Preset for ST7567 (Display) */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_4m; + +/** Preset for SdCard in fast mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_16m; + +/** Preset for SdCard in slow mode */ +extern const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m; + +/** Furi Hal Spi Bus R (External) */ +extern FuriHalSpiBus furi_hal_spi_bus_r; + +/** Furi Hal Spi Bus D (Display, SdCard) */ +extern FuriHalSpiBus furi_hal_spi_bus_d; + +/** External on `furi_hal_spi_bus_r` + * Preset: `furi_hal_spi_preset_1edge_low_2m` + * + * miso: pa6 + * mosi: pa7 + * sck: pb3 + * cs: pa4 (software controlled) + * + * @warning not initialized by default, call `furi_hal_spi_bus_handle_init` to initialize + * Bus pins are floating on inactive state, CS high after initialization + * + */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_external; + +/** ST7567(Display) on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_display; + +/** SdCard in fast mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_fast; + +/** SdCard in slow mode on `furi_hal_spi_bus_d` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_sd_slow; + +#ifdef __cplusplus +} +#endif diff --git a/firmware/targets/f18/furi_hal/furi_hal_target_hw.h b/firmware/targets/f18/furi_hal/furi_hal_target_hw.h new file mode 100644 index 000000000..6f70f09be --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_target_hw.h @@ -0,0 +1 @@ +#pragma once diff --git a/firmware/targets/f18/furi_hal/furi_hal_version_device.c b/firmware/targets/f18/furi_hal/furi_hal_version_device.c new file mode 100644 index 000000000..1b5090b93 --- /dev/null +++ b/firmware/targets/f18/furi_hal/furi_hal_version_device.c @@ -0,0 +1,21 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 18) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Komi"; +} + +const char* furi_hal_version_get_model_code() { + return "N/A"; +} + +const char* furi_hal_version_get_fcc_id() { + return "N/A"; +} + +const char* furi_hal_version_get_ic_id() { + return "N/A"; +} diff --git a/firmware/targets/f18/target.json b/firmware/targets/f18/target.json new file mode 100644 index 000000000..2c3b27ab1 --- /dev/null +++ b/firmware/targets/f18/target.json @@ -0,0 +1,55 @@ +{ + "inherit": "7", + "include_paths": [ + "furi_hal" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper18", + "furi", + "freertos", + "stm32cubewb", + "hwdrivers", + "fatfs", + "littlefs", + "flipperformat", + "toolbox", + "microtar", + "usb_stm32", + "appframe", + "assets", + "misc", + "flipper_application", + "flipperformat", + "toolbox", + "flipper18" + ], + "excluded_sources": [ + "furi_hal_infrared.c", + "furi_hal_nfc.c", + "furi_hal_rfid.c", + "furi_hal_subghz.c" + ], + "excluded_headers": [ + "furi_hal_infrared.h", + "furi_hal_nfc.h", + "furi_hal_rfid.h", + "furi_hal_subghz.h", + "furi_hal_ibutton.h", + "furi_hal_subghz_configs.h" + ], + "excluded_modules": [ + "one_wire", + "nfc", + "lfrfid", + "subghz", + "infrared", + "st25rfal002" + ] +} \ No newline at end of file diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 14036ef1a..c1a979859 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,11.10,, +Version,+,12.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -41,14 +41,19 @@ Header,+,firmware/targets/f7/furi_hal/furi_hal_flash.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_gpio.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_i2c_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_ibutton.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_idle_timer.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_interrupt.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_nfc.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_os.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_pwm.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_resources.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_rfid.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_config.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_spi_types.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h,, +Header,+,firmware/targets/f7/furi_hal/furi_hal_target_hw.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_uart.h,, Header,+,firmware/targets/f7/furi_hal/furi_hal_usb_cdc.h,, Header,+,firmware/targets/f7/platform_specific/intrinsic_export.h,, @@ -61,22 +66,18 @@ Header,+,firmware/targets/furi_hal_include/furi_hal_cortex.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_crypto.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_debug.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_i2c.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_ibutton.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_info.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_infrared.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_light.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_memory.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_mpu.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_nfc.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_power.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_random.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_region.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_rfid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_rtc.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_sd.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_speaker.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_spi.h,, -Header,+,firmware/targets/furi_hal_include/furi_hal_subghz.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid.h,, Header,+,firmware/targets/furi_hal_include/furi_hal_usb_hid_u2f.h,, @@ -150,6 +151,16 @@ Header,+,lib/libusb_stm32/inc/usbd_core.h,, Header,+,lib/mbedtls/include/mbedtls/des.h,, Header,+,lib/mbedtls/include/mbedtls/sha1.h,, Header,+,lib/micro-ecc/uECC.h,, +Header,+,lib/mlib/m-algo.h,, +Header,+,lib/mlib/m-array.h,, +Header,+,lib/mlib/m-bptree.h,, +Header,+,lib/mlib/m-core.h,, +Header,+,lib/mlib/m-deque.h,, +Header,+,lib/mlib/m-dict.h,, +Header,+,lib/mlib/m-list.h,, +Header,+,lib/mlib/m-rbtree.h,, +Header,+,lib/mlib/m-tuple.h,, +Header,+,lib/mlib/m-variant.h,, Header,+,lib/nfc/nfc_device.h,, Header,+,lib/one_wire/ibutton/ibutton_worker.h,, Header,+,lib/one_wire/maxim_crc.h,, @@ -852,6 +863,7 @@ Function,+,flipper_application_free,void,FlipperApplication* Function,+,flipper_application_get_manifest,const FlipperApplicationManifest*,FlipperApplication* Function,+,flipper_application_load_status_to_string,const char*,FlipperApplicationLoadStatus Function,+,flipper_application_manifest_is_compatible,_Bool,"const FlipperApplicationManifest*, const ElfApiInterface*" +Function,+,flipper_application_manifest_is_target_compatible,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_manifest_is_valid,_Bool,const FlipperApplicationManifest* Function,+,flipper_application_map_to_memory,FlipperApplicationLoadStatus,FlipperApplication* Function,+,flipper_application_preload,FlipperApplicationPreloadStatus,"FlipperApplication*, const char*" @@ -1022,7 +1034,7 @@ Function,+,furi_hal_cdc_get_port_settings,usb_cdc_line_coding*,uint8_t Function,+,furi_hal_cdc_receive,int32_t,"uint8_t, uint8_t*, uint16_t" Function,+,furi_hal_cdc_send,void,"uint8_t, uint8_t*, uint16_t" Function,+,furi_hal_cdc_set_callbacks,void,"uint8_t, CdcCallbacks*, void*" -Function,+,furi_hal_clock_deinit_early,void, +Function,-,furi_hal_clock_deinit_early,void, Function,-,furi_hal_clock_init,void, Function,-,furi_hal_clock_init_early,void, Function,+,furi_hal_clock_mco_disable,void, @@ -1103,7 +1115,7 @@ Function,+,furi_hal_hid_u2f_is_connected,_Bool, Function,+,furi_hal_hid_u2f_send_response,void,"uint8_t*, uint8_t" Function,+,furi_hal_hid_u2f_set_callback,void,"HidU2fCallback, void*" Function,+,furi_hal_i2c_acquire,void,FuriHalI2cBusHandle* -Function,+,furi_hal_i2c_deinit_early,void, +Function,-,furi_hal_i2c_deinit_early,void, Function,-,furi_hal_i2c_init,void, Function,-,furi_hal_i2c_init_early,void, Function,+,furi_hal_i2c_is_device_ready,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint32_t" @@ -1241,7 +1253,7 @@ Function,-,furi_hal_region_init,void, Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* -Function,+,furi_hal_resources_deinit_early,void, +Function,-,furi_hal_resources_deinit_early,void, Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1270,7 +1282,7 @@ Function,+,furi_hal_rfid_tim_read_start,void, Function,+,furi_hal_rfid_tim_read_stop,void, Function,+,furi_hal_rfid_tim_reset,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* -Function,+,furi_hal_rtc_deinit_early,void, +Function,-,furi_hal_rtc_deinit_early,void, Function,+,furi_hal_rtc_get_boot_mode,FuriHalRtcBootMode, Function,+,furi_hal_rtc_get_datetime,void,FuriHalRtcDateTime* Function,+,furi_hal_rtc_get_fault_data,uint32_t, @@ -1314,9 +1326,9 @@ Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_deinit_early,void, -Function,-,furi_hal_spi_init,void, -Function,+,furi_hal_spi_init_early,void, +Function,-,furi_hal_spi_config_deinit_early,void, +Function,-,furi_hal_spi_config_init,void, +Function,-,furi_hal_spi_config_init_early,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1370,6 +1382,7 @@ Function,+,furi_hal_version_do_i_belong_here,_Bool, Function,+,furi_hal_version_get_ble_local_device_name_ptr,const char*, Function,+,furi_hal_version_get_ble_mac,const uint8_t*, Function,+,furi_hal_version_get_device_name_ptr,const char*, +Function,+,furi_hal_version_get_fcc_id,const char*, Function,+,furi_hal_version_get_firmware_version,const Version*, Function,+,furi_hal_version_get_hw_body,uint8_t, Function,+,furi_hal_version_get_hw_color,FuriHalVersionColor, @@ -1380,6 +1393,8 @@ Function,+,furi_hal_version_get_hw_region_name,const char*, Function,+,furi_hal_version_get_hw_target,uint8_t, Function,+,furi_hal_version_get_hw_timestamp,uint32_t, Function,+,furi_hal_version_get_hw_version,uint8_t, +Function,+,furi_hal_version_get_ic_id,const char*, +Function,+,furi_hal_version_get_model_code,const char*, Function,+,furi_hal_version_get_model_name,const char*, Function,+,furi_hal_version_get_name_ptr,const char*, Function,+,furi_hal_version_get_otp_version,FuriHalVersionOtpVersion, @@ -3026,6 +3041,8 @@ Variable,+,gpio_infrared_rx,const GpioPin, Variable,+,gpio_infrared_tx,const GpioPin, Variable,+,gpio_nfc_cs,const GpioPin, Variable,+,gpio_nfc_irq_rfid_pull,const GpioPin, +Variable,+,gpio_pins,const GpioPinRecord[], +Variable,+,gpio_pins_count,const size_t, Variable,+,gpio_rf_sw_0,const GpioPin, Variable,+,gpio_rfid_carrier,const GpioPin, Variable,+,gpio_rfid_carrier_out,const GpioPin, diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index ec82c377a..7f2f5759f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -17,7 +17,7 @@ void furi_hal_init_early() { furi_hal_os_init(); - furi_hal_spi_init_early(); + furi_hal_spi_config_init_early(); furi_hal_i2c_init_early(); furi_hal_light_init(); @@ -29,7 +29,7 @@ void furi_hal_deinit_early() { furi_hal_rtc_deinit_early(); furi_hal_i2c_deinit_early(); - furi_hal_spi_deinit_early(); + furi_hal_spi_config_deinit_early(); furi_hal_resources_deinit_early(); @@ -52,7 +52,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_region_init(); - furi_hal_spi_init(); + furi_hal_spi_config_init(); furi_hal_ibutton_init(); FURI_LOG_I(TAG, "iButton OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index ab3855f42..8259be2f6 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -1,5 +1,5 @@ -#include "furi_hal_bt_hid.h" -#include "furi_hal_usb_hid.h" +#include +#include #include "usb_hid.h" #include "dev_info_service.h" #include "battery_service.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c index aa09dde52..2539e6bd0 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_serial.c @@ -1,4 +1,4 @@ -#include "furi_hal_bt_serial.h" +#include #include "dev_info_service.h" #include "battery_service.h" #include "serial_service.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_cortex.c b/firmware/targets/f7/furi_hal/furi_hal_cortex.c index 192b83ee4..d0bce5038 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_cortex.c +++ b/firmware/targets/f7/furi_hal/furi_hal_cortex.c @@ -1,4 +1,4 @@ -#include "furi_hal_cortex.h" +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c index 678eb2965..afc4fdf52 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_i2c_config.c @@ -1,4 +1,4 @@ -#include "furi_hal_i2c_config.h" +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_ibutton.h b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h similarity index 98% rename from firmware/targets/furi_hal_include/furi_hal_ibutton.h rename to firmware/targets/f7/furi_hal/furi_hal_ibutton.h index 84ef0cd6a..fb57d628b 100644 --- a/firmware/targets/furi_hal_include/furi_hal_ibutton.h +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h @@ -7,7 +7,7 @@ #include #include -#include "furi_hal_gpio.h" +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index 442ae715d..dea1112ab 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -1,4 +1,4 @@ -#include "furi_hal_infrared.h" +#include #include #include "stm32wbxx_ll_dma.h" #include "sys/_stdint.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index 038ae9489..1b1132d0c 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -1,5 +1,5 @@ -#include "furi_hal_interrupt.h" -#include "furi_hal_os.h" +#include +#include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_light.c b/firmware/targets/f7/furi_hal/furi_hal_light.c index e6b3ab7d9..83e1603b7 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_light.c +++ b/firmware/targets/f7/furi_hal/furi_hal_light.c @@ -1,5 +1,5 @@ #include -#include "furi_hal_resources.h" +#include #include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_nfc.c b/firmware/targets/f7/furi_hal/furi_hal_nfc.c index 6381d1a91..ce81fd058 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_nfc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_nfc.c @@ -1,5 +1,5 @@ #include -#include "furi_hal_nfc.h" +#include #include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_nfc.h b/firmware/targets/f7/furi_hal/furi_hal_nfc.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_nfc.h rename to firmware/targets/f7/furi_hal/furi_hal_nfc.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_pwm.c b/firmware/targets/f7/furi_hal/furi_hal_pwm.c index e47f752ab..8f84b5fd8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_pwm.c +++ b/firmware/targets/f7/furi_hal/furi_hal_pwm.c @@ -1,4 +1,4 @@ -#include "furi_hal_pwm.h" +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_random.c b/firmware/targets/f7/furi_hal/furi_hal_random.c index cd019c0d9..f36407cc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_random.c +++ b/firmware/targets/f7/furi_hal/furi_hal_random.c @@ -1,4 +1,4 @@ -#include "furi_hal_random.h" +#include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 4a32d7087..03cc6e714 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -62,6 +62,23 @@ const GpioPin periph_power = {.port = GPIOA, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_usb_dm = {.port = GPIOA, .pin = LL_GPIO_PIN_11}; const GpioPin gpio_usb_dp = {.port = GPIOA, .pin = LL_GPIO_PIN_12}; +const GpioPinRecord gpio_pins[] = { + {.pin = &gpio_ext_pa7, .name = "PA7", .debug = false}, + {.pin = &gpio_ext_pa6, .name = "PA6", .debug = false}, + {.pin = &gpio_ext_pa4, .name = "PA4", .debug = false}, + {.pin = &gpio_ext_pb3, .name = "PB3", .debug = false}, + {.pin = &gpio_ext_pb2, .name = "PB2", .debug = false}, + {.pin = &gpio_ext_pc3, .name = "PC3", .debug = false}, + {.pin = &gpio_ext_pc1, .name = "PC1", .debug = false}, + {.pin = &gpio_ext_pc0, .name = "PC0", .debug = false}, + + /* Dangerous pins, may damage hardware */ + {.pin = &gpio_usart_rx, .name = "PB7", .debug = true}, + {.pin = &gpio_speaker, .name = "PB8", .debug = true}, +}; + +const size_t gpio_pins_count = sizeof(gpio_pins) / sizeof(GpioPinRecord); + const InputPin input_pins[] = { {.gpio = &gpio_button_up, .key = InputKeyUp, .inverted = true, .name = "Up"}, {.gpio = &gpio_button_down, .key = InputKeyDown, .inverted = true, .name = "Down"}, diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 64e5e19f9..51ea7bcc1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -38,9 +38,18 @@ typedef struct { const char* name; } InputPin; +typedef struct { + const GpioPin* pin; + const char* name; + const bool debug; +} GpioPinRecord; + extern const InputPin input_pins[]; extern const size_t input_pins_count; +extern const GpioPinRecord gpio_pins[]; +extern const size_t gpio_pins_count; + extern const GpioPin vibro_gpio; extern const GpioPin ibutton_gpio; diff --git a/firmware/targets/furi_hal_include/furi_hal_rfid.h b/firmware/targets/f7/furi_hal/furi_hal_rfid.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_rfid.h rename to firmware/targets/f7/furi_hal/furi_hal_rfid.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_sd.c b/firmware/targets/f7/furi_hal/furi_hal_sd.c index 688a4e61b..1b0de5628 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_sd.c +++ b/firmware/targets/f7/furi_hal/furi_hal_sd.c @@ -1,24 +1,21 @@ -#include "furi_hal_sd.h" +#include #include #include #include void hal_sd_detect_init(void) { // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_INPUT); - LL_GPIO_SetPinSpeed(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_SPEED_FREQ_LOW); - LL_GPIO_SetPinPull(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_PULL_UP); + furi_hal_gpio_init(&gpio_sdcard_cd, GpioModeInput, GpioPullUp, GpioSpeedLow); } void hal_sd_detect_set_low(void) { // low speed input with pullup - LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_OUTPUT); - LL_GPIO_SetPinOutputType(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_OUTPUT_OPENDRAIN); - LL_GPIO_ResetOutputPin(SD_CD_GPIO_Port, SD_CD_Pin); + furi_hal_gpio_init_simple(&gpio_sdcard_cd, GpioModeOutputOpenDrain); + furi_hal_gpio_write(&gpio_sdcard_cd, 0); } bool hal_sd_detect(void) { - bool result = !(LL_GPIO_IsInputPinSet(SD_CD_GPIO_Port, SD_CD_Pin)); + bool result = !furi_hal_gpio_read(&gpio_sdcard_cd); return result; } diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2d54278d6..2f9d87080 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,38 +1,14 @@ -#include "furi_hal_spi.h" -#include "furi_hal_resources.h" +#include +#include #include #include #include -#include #include #include #include -#define TAG "FuriHalSpi" - -void furi_hal_spi_init_early() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_d); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); -} - -void furi_hal_spi_deinit_early() { - furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); - furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); -} - -void furi_hal_spi_init() { - furi_hal_spi_bus_init(&furi_hal_spi_bus_r); - - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); - - FURI_LOG_I(TAG, "Init OK"); -} - void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 56f67bbf8..9cf332dac 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -1,5 +1,9 @@ #include #include +#include +#include + +#define TAG "FuriHalSpiConfig" /* SPI Presets */ @@ -72,6 +76,27 @@ const LL_SPI_InitTypeDef furi_hal_spi_preset_1edge_low_2m = { FuriMutex* furi_hal_spi_bus_r_mutex = NULL; +void furi_hal_spi_config_init_early() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_d); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_display); +} + +void furi_hal_spi_config_deinit_early() { + furi_hal_spi_bus_handle_deinit(&furi_hal_spi_bus_handle_display); + furi_hal_spi_bus_deinit(&furi_hal_spi_bus_d); +} + +void furi_hal_spi_config_init() { + furi_hal_spi_bus_init(&furi_hal_spi_bus_r); + + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); + + FURI_LOG_I(TAG, "Init OK"); +} + static void furi_hal_spi_bus_r_event_callback(FuriHalSpiBus* bus, FuriHalSpiBusEvent event) { if(event == FuriHalSpiBusEventInit) { furi_hal_spi_bus_r_mutex = furi_mutex_alloc(FuriMutexTypeNormal); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 3441fd965..9bde15c33 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,5 +1,5 @@ -#include "furi_hal_subghz.h" -#include "furi_hal_subghz_configs.h" +#include +#include #include #include diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h similarity index 100% rename from firmware/targets/furi_hal_include/furi_hal_subghz.h rename to firmware/targets/f7/furi_hal/furi_hal_subghz.h diff --git a/firmware/targets/f7/furi_hal/furi_hal_target_hw.h b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h new file mode 100644 index 000000000..128122f84 --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_target_hw.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include +#include +#include diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb.c b/firmware/targets/f7/furi_hal/furi_hal_usb.c index e740155f5..0038bd348 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb.c @@ -1,6 +1,6 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" +#include +#include +#include #include #include #include diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c index 88deafdcc..4c4f727ba 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_cdc.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_cdc.h" +#include +#include +#include +#include #include #include "usb.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c index fc1ce024c..a3bc84227 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_hid.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" +#include +#include +#include +#include #include #include "usb.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c index c099aec8a..fe711512a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c +++ b/firmware/targets/f7/furi_hal/furi_hal_usb_u2f.c @@ -1,7 +1,7 @@ -#include "furi_hal_version.h" -#include "furi_hal_usb_i.h" -#include "furi_hal_usb_hid_u2f.h" -#include "furi_hal_usb.h" +#include +#include +#include +#include #include #include "usb.h" #include "usb_hid.h" diff --git a/firmware/targets/f7/furi_hal/furi_hal_version.c b/firmware/targets/f7/furi_hal/furi_hal_version.c index b7827ac7f..859a8c39f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_version.c +++ b/firmware/targets/f7/furi_hal/furi_hal_version.c @@ -195,14 +195,6 @@ void furi_hal_version_init() { FURI_LOG_I(TAG, "Init OK"); } -bool furi_hal_version_do_i_belong_here() { - return furi_hal_version_get_hw_target() == 7; -} - -const char* furi_hal_version_get_model_name() { - return "Flipper Zero"; -} - FuriHalVersionOtpVersion furi_hal_version_get_otp_version() { if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) { return FuriHalVersionOtpVersionEmpty; diff --git a/firmware/targets/f7/furi_hal/furi_hal_version_device.c b/firmware/targets/f7/furi_hal/furi_hal_version_device.c new file mode 100644 index 000000000..c059c2cbe --- /dev/null +++ b/firmware/targets/f7/furi_hal/furi_hal_version_device.c @@ -0,0 +1,21 @@ +#include + +bool furi_hal_version_do_i_belong_here() { + return (furi_hal_version_get_hw_target() == 7) || (furi_hal_version_get_hw_target() == 0); +} + +const char* furi_hal_version_get_model_name() { + return "Flipper Zero"; +} + +const char* furi_hal_version_get_model_code() { + return "FZ.1"; +} + +const char* furi_hal_version_get_fcc_id() { + return "2A2V6-FZ"; +} + +const char* furi_hal_version_get_ic_id() { + return "27624-FZ"; +} diff --git a/firmware/targets/f7/Inc/FreeRTOSConfig.h b/firmware/targets/f7/inc/FreeRTOSConfig.h similarity index 100% rename from firmware/targets/f7/Inc/FreeRTOSConfig.h rename to firmware/targets/f7/inc/FreeRTOSConfig.h diff --git a/firmware/targets/f7/Inc/alt_boot.h b/firmware/targets/f7/inc/alt_boot.h similarity index 100% rename from firmware/targets/f7/Inc/alt_boot.h rename to firmware/targets/f7/inc/alt_boot.h diff --git a/firmware/targets/f7/Inc/stm32.h b/firmware/targets/f7/inc/stm32.h similarity index 100% rename from firmware/targets/f7/Inc/stm32.h rename to firmware/targets/f7/inc/stm32.h diff --git a/firmware/targets/f7/Inc/stm32_assert.h b/firmware/targets/f7/inc/stm32_assert.h similarity index 100% rename from firmware/targets/f7/Inc/stm32_assert.h rename to firmware/targets/f7/inc/stm32_assert.h diff --git a/firmware/targets/f7/Src/dfu.c b/firmware/targets/f7/src/dfu.c similarity index 100% rename from firmware/targets/f7/Src/dfu.c rename to firmware/targets/f7/src/dfu.c diff --git a/firmware/targets/f7/Src/main.c b/firmware/targets/f7/src/main.c similarity index 100% rename from firmware/targets/f7/Src/main.c rename to firmware/targets/f7/src/main.c diff --git a/firmware/targets/f7/Src/recovery.c b/firmware/targets/f7/src/recovery.c similarity index 99% rename from firmware/targets/f7/Src/recovery.c rename to firmware/targets/f7/src/recovery.c index fa57482ca..d56be4fe0 100644 --- a/firmware/targets/f7/Src/recovery.c +++ b/firmware/targets/f7/src/recovery.c @@ -51,4 +51,4 @@ void flipper_boot_recovery_exec() { furi_hal_rtc_set_pin_fails(0); furi_hal_rtc_reset_flag(FuriHalRtcFlagLock); } -} \ No newline at end of file +} diff --git a/firmware/targets/f7/Src/system_stm32wbxx.c b/firmware/targets/f7/src/system_stm32wbxx.c similarity index 100% rename from firmware/targets/f7/Src/system_stm32wbxx.c rename to firmware/targets/f7/src/system_stm32wbxx.c diff --git a/firmware/targets/f7/Src/update.c b/firmware/targets/f7/src/update.c similarity index 99% rename from firmware/targets/f7/Src/update.c rename to firmware/targets/f7/src/update.c index a68a8b7a7..b223a5dc3 100644 --- a/firmware/targets/f7/Src/update.c +++ b/firmware/targets/f7/src/update.c @@ -42,7 +42,7 @@ static bool flipper_update_init() { furi_hal_rtc_init(); furi_hal_interrupt_init(); - furi_hal_spi_init(); + furi_hal_spi_config_init(); MX_FATFS_Init(); if(!hal_sd_detect()) { diff --git a/firmware/targets/f7/stm32wb55xx_flash.ld b/firmware/targets/f7/stm32wb55xx_flash.ld index e1fb98b9f..df4c5b726 100644 --- a/firmware/targets/f7/stm32wb55xx_flash.ld +++ b/firmware/targets/f7/stm32wb55xx_flash.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_flash.ld ** ** Abstract : System Workbench Minimal System calls file ** diff --git a/firmware/targets/f7/stm32wb55xx_ram_fw.ld b/firmware/targets/f7/stm32wb55xx_ram_fw.ld index db9e407c9..0ac9be4df 100644 --- a/firmware/targets/f7/stm32wb55xx_ram_fw.ld +++ b/firmware/targets/f7/stm32wb55xx_ram_fw.ld @@ -1,7 +1,7 @@ /** ***************************************************************************** ** -** File : stm32wb55xx_flash_cm4.ld +** File : stm32wb55xx_ram_fw.ld ** ** Abstract : System Workbench Minimal System calls file ** diff --git a/firmware/targets/f7/target.json b/firmware/targets/f7/target.json new file mode 100644 index 000000000..49aa109bd --- /dev/null +++ b/firmware/targets/f7/target.json @@ -0,0 +1,45 @@ +{ + "include_paths": [ + "ble_glue", + "fatfs", + "furi_hal", + "inc" + ], + "sdk_header_paths": [ + "../furi_hal_include", + "furi_hal", + "platform_specific" + ], + "startup_script": "startup_stm32wb55xx_cm4.s", + "linker_script_flash": "stm32wb55xx_flash.ld", + "linker_script_ram": "stm32wb55xx_ram_fw.ld", + "linker_script_app": "application_ext.ld", + "sdk_symbols": "api_symbols.csv", + "linker_dependencies": [ + "print", + "flipper7", + "furi", + "freertos", + "stm32cubewb", + "hwdrivers", + "fatfs", + "littlefs", + "subghz", + "flipperformat", + "toolbox", + "nfc", + "microtar", + "usb_stm32", + "st25rfal002", + "infrared", + "appframe", + "assets", + "one_wire", + "misc", + "mbedtls", + "lfrfid", + "flipper_application", + "flipperformat", + "toolbox" + ] +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal.h b/firmware/targets/furi_hal_include/furi_hal.h index 8613c4d5e..ad4340dd4 100644 --- a/firmware/targets/furi_hal_include/furi_hal.h +++ b/firmware/targets/furi_hal_include/furi_hal.h @@ -10,37 +10,34 @@ template struct STOP_EXTERNING_ME {}; #endif -#include "furi_hal_cortex.h" -#include "furi_hal_clock.h" -#include "furi_hal_crypto.h" -#include "furi_hal_console.h" -#include "furi_hal_debug.h" -#include "furi_hal_os.h" -#include "furi_hal_sd.h" -#include "furi_hal_i2c.h" -#include "furi_hal_resources.h" -#include "furi_hal_region.h" -#include "furi_hal_rtc.h" -#include "furi_hal_speaker.h" -#include "furi_hal_gpio.h" -#include "furi_hal_light.h" -#include "furi_hal_power.h" -#include "furi_hal_interrupt.h" -#include "furi_hal_version.h" -#include "furi_hal_bt.h" -#include "furi_hal_spi.h" -#include "furi_hal_flash.h" -#include "furi_hal_subghz.h" -#include "furi_hal_vibro.h" -#include "furi_hal_ibutton.h" -#include "furi_hal_rfid.h" -#include "furi_hal_nfc.h" -#include "furi_hal_usb.h" -#include "furi_hal_usb_hid.h" -#include "furi_hal_compress.h" -#include "furi_hal_uart.h" -#include "furi_hal_info.h" -#include "furi_hal_random.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 +#include +#include #ifdef __cplusplus extern "C" { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt.h b/firmware/targets/furi_hal_include/furi_hal_bt.h index 800fc3fe3..196b2edb3 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt.h @@ -12,7 +12,7 @@ #include #include -#include "furi_hal_bt_serial.h" +#include #define FURI_HAL_BT_STACK_VERSION_MAJOR (1) #define FURI_HAL_BT_STACK_VERSION_MINOR (12) diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index df7ffa93d..ab00ef0d7 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -8,13 +8,13 @@ extern "C" { #endif /** Early initialize SPI HAL */ -void furi_hal_spi_init_early(); +void furi_hal_spi_config_init_early(); /** Early deinitialize SPI HAL */ -void furi_hal_spi_deinit_early(); +void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ -void furi_hal_spi_init(); +void furi_hal_spi_config_init(); /** Initialize SPI Bus * diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 720fdfd17..aec4fc787 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -67,6 +67,24 @@ bool furi_hal_version_do_i_belong_here(); */ const char* furi_hal_version_get_model_name(); +/** Get model name + * + * @return model code C-string + */ +const char* furi_hal_version_get_model_code(); + +/** Get FCC ID + * + * @return FCC id as C-string + */ +const char* furi_hal_version_get_fcc_id(); + +/** Get IC id + * + * @return IC id as C-string + */ +const char* furi_hal_version_get_ic_id(); + /** Get OTP version * * @return OTP Version diff --git a/lib/SConscript b/lib/SConscript index abede5f33..d1e8e8c7e 100644 --- a/lib/SConscript +++ b/lib/SConscript @@ -8,7 +8,6 @@ env.Append( Dir("flipper_format"), Dir("infrared"), Dir("nfc"), - Dir("one_wire"), Dir("ST25RFAL002"), Dir("subghz"), Dir("toolbox"), @@ -16,16 +15,9 @@ env.Append( Dir("update_util"), Dir("print"), ], - SDK_HEADERS=[ - File("one_wire/one_wire_host_timing.h"), - File("one_wire/one_wire_host.h"), - File("one_wire/one_wire_slave.h"), - File("one_wire/one_wire_device.h"), - File("one_wire/ibutton/ibutton_worker.h"), - File("one_wire/maxim_crc.h"), - ], ) + env.Append( CPPPATH=[ "#/", @@ -34,6 +26,23 @@ env.Append( # Ugly hack Dir("../assets/compiled"), ], + SDK_HEADERS=[ + *( + File(f"#/lib/mlib/m-{name}.h") + for name in ( + "algo", + "array", + "bptree", + "core", + "deque", + "dict", + "list", + "rbtree", + "tuple", + "variant", + ) + ), + ], CPPDEFINES=[ '"M_MEMORY_FULL(x)=abort()"', ], @@ -47,13 +56,13 @@ env.Append( # littlefs # subghz # toolbox +# one_wire +# micro-ecc # misc # digital_signal -# fnv1a-hash -# micro-ecc +# fnv1a_hash # microtar # nfc -# one_wire # qrcode # u8g2 # update_util @@ -77,6 +86,7 @@ libs = env.BuildModules( "drivers", "fatfs", "flipper_format", + "one_wire", "infrared", "littlefs", "mbedtls", diff --git a/lib/app-scened-template/view_modules/popup_vm.cpp b/lib/app-scened-template/view_modules/popup_vm.cpp index e2c8732e8..330aa44ca 100644 --- a/lib/app-scened-template/view_modules/popup_vm.cpp +++ b/lib/app-scened-template/view_modules/popup_vm.cpp @@ -1,5 +1,6 @@ #include "popup_vm.h" -#include "gui/modules/popup.h" +#include + PopupVM::PopupVM() { popup = popup_alloc(); } diff --git a/lib/flipper_application/application_manifest.c b/lib/flipper_application/application_manifest.c index ab92e4930..fea92c262 100644 --- a/lib/flipper_application/application_manifest.c +++ b/lib/flipper_application/application_manifest.c @@ -1,5 +1,7 @@ #include "application_manifest.h" +#include + bool flipper_application_manifest_is_valid(const FlipperApplicationManifest* manifest) { if((manifest->base.manifest_magic != FAP_MANIFEST_MAGIC) || (manifest->base.manifest_version != FAP_MANIFEST_SUPPORTED_VERSION)) { @@ -19,3 +21,8 @@ bool flipper_application_manifest_is_compatible( return true; } + +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest) { + const Version* version = furi_hal_version_get_firmware_version(); + return version_get_target(version) == manifest->base.hardware_target_id; +} \ No newline at end of file diff --git a/lib/flipper_application/application_manifest.h b/lib/flipper_application/application_manifest.h index f46d44fd7..25e4f8d0a 100644 --- a/lib/flipper_application/application_manifest.h +++ b/lib/flipper_application/application_manifest.h @@ -65,6 +65,14 @@ bool flipper_application_manifest_is_compatible( const FlipperApplicationManifest* manifest, const ElfApiInterface* api_interface); +/** + * @brief Check if application is compatible with current hardware + * + * @param manifest + * @return bool + */ +bool flipper_application_manifest_is_target_compatible(const FlipperApplicationManifest* manifest); + #ifdef __cplusplus } #endif diff --git a/lib/flipper_application/flipper_application.c b/lib/flipper_application/flipper_application.c index 8b049a7d4..58909218a 100644 --- a/lib/flipper_application/flipper_application.c +++ b/lib/flipper_application/flipper_application.c @@ -43,6 +43,10 @@ static FlipperApplicationPreloadStatus return FlipperApplicationPreloadStatusInvalidManifest; } + if(!flipper_application_manifest_is_target_compatible(&app->manifest)) { + return FlipperApplicationPreloadStatusTargetMismatch; + } + if(!flipper_application_manifest_is_compatible( &app->manifest, elf_file_get_api_interface(app->elf))) { return FlipperApplicationPreloadStatusApiMismatch; diff --git a/lib/misc.scons b/lib/misc.scons index 91ad276a0..cd2377ceb 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -26,7 +26,6 @@ sources = [] libs_recurse = [ "digital_signal", "micro-ecc", - "one_wire", "u8g2", "update_util", ] diff --git a/lib/nfc/parsers/all_in_one.c b/lib/nfc/parsers/all_in_one.c index c02710a29..edcc0d0c7 100644 --- a/lib/nfc/parsers/all_in_one.c +++ b/lib/nfc/parsers/all_in_one.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal.h" +#include #define ALL_IN_ONE_LAYOUT_UNKNOWN 0 #define ALL_IN_ONE_LAYOUT_A 1 diff --git a/lib/nfc/parsers/plantain_4k_parser.c b/lib/nfc/parsers/plantain_4k_parser.c index e636bee00..aed41965c 100644 --- a/lib/nfc/parsers/plantain_4k_parser.c +++ b/lib/nfc/parsers/plantain_4k_parser.c @@ -3,7 +3,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext plantain_keys_4k[] = { {.sector = 0, .key_a = 0xFFFFFFFFFFFF, .key_b = 0xFFFFFFFFFFFF}, diff --git a/lib/nfc/parsers/plantain_parser.c b/lib/nfc/parsers/plantain_parser.c index c0e2a0947..3a1d17732 100644 --- a/lib/nfc/parsers/plantain_parser.c +++ b/lib/nfc/parsers/plantain_parser.c @@ -3,7 +3,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext plantain_keys[] = { {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, diff --git a/lib/nfc/parsers/two_cities.c b/lib/nfc/parsers/two_cities.c index 335248b2a..0e2ed5690 100644 --- a/lib/nfc/parsers/two_cities.c +++ b/lib/nfc/parsers/two_cities.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal.h" +#include static const MfClassicAuthContext two_cities_keys_4k[] = { {.sector = 0, .key_a = 0xffffffffffff, .key_b = 0xffffffffffff}, diff --git a/lib/nfc/protocols/mifare_ultralight.c b/lib/nfc/protocols/mifare_ultralight.c index d642e290a..0e28c0074 100644 --- a/lib/nfc/protocols/mifare_ultralight.c +++ b/lib/nfc/protocols/mifare_ultralight.c @@ -3,7 +3,7 @@ #include "mifare_ultralight.h" #include "nfc_util.h" #include -#include "furi_hal_nfc.h" +#include #define TAG "MfUltralight" diff --git a/lib/one_wire/SConscript b/lib/one_wire/SConscript new file mode 100644 index 000000000..5e06d21e3 --- /dev/null +++ b/lib/one_wire/SConscript @@ -0,0 +1,27 @@ +Import("env") + +env.Append( + LINT_SOURCES=[ + Dir("."), + ], + CPPPATH=[ + "#/lib/one_wire", + ], + SDK_HEADERS=[ + File("one_wire_host_timing.h"), + File("one_wire_host.h"), + File("one_wire_slave.h"), + File("one_wire_device.h"), + File("ibutton/ibutton_worker.h"), + File("maxim_crc.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="one_wire") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 1448f0ea6..d1c7dc356 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -5,8 +5,8 @@ #include #include -#include "furi.h" -#include "furi_hal.h" +#include +#include #include "../types.h" #ifdef __cplusplus diff --git a/lib/subghz/protocols/princeton_for_testing.c b/lib/subghz/protocols/princeton_for_testing.c index fa5616020..478d14cdf 100644 --- a/lib/subghz/protocols/princeton_for_testing.c +++ b/lib/subghz/protocols/princeton_for_testing.c @@ -1,6 +1,6 @@ #include "princeton_for_testing.h" -#include "furi_hal.h" +#include #include "../blocks/math.h" /* diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 57e23c38c..5d7ea0cec 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -4,7 +4,7 @@ #include #include -#include "furi_hal_subghz_configs.h" +#include #define TAG "SubGhzSetting" diff --git a/lib/update_util/update_operation.c b/lib/update_util/update_operation.c index c6a9ccc5f..6e05b0233 100644 --- a/lib/update_util/update_operation.c +++ b/lib/update_util/update_operation.c @@ -21,8 +21,10 @@ static const char* update_prepare_result_descr[] = { [UpdatePrepareResultStageMissing] = "Missing Stage2 loader", [UpdatePrepareResultStageIntegrityError] = "Corrupted Stage2 loader", [UpdatePrepareResultManifestPointerError] = "Failed to create update pointer file", + [UpdatePrepareResultTargetMismatch] = "Hardware target mismatch", [UpdatePrepareResultOutdatedManifestVersion] = "Update package is too old", [UpdatePrepareResultIntFull] = "Need more free space in internal storage", + [UpdatePrepareResultUnspecifiedError] = "Unknown error", }; const char* update_operation_describe_preparation_result(const UpdatePrepareResult value) { @@ -163,8 +165,9 @@ UpdatePrepareResult update_operation_prepare(const char* manifest_file_path) { result = UpdatePrepareResultOutdatedManifestVersion; break; } - - if(furi_hal_version_get_hw_target() != manifest->target) { + /* Only compare hardware target if it is set - pre-production devices accept any firmware*/ + if(furi_hal_version_get_hw_target() && + (furi_hal_version_get_hw_target() != manifest->target)) { result = UpdatePrepareResultTargetMismatch; break; } diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index a7460d889..aa03d265b 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, field -from typing import List, Optional, Tuple +from typing import List, Optional, Tuple, Callable from enum import Enum import os @@ -71,6 +71,9 @@ class FlipperApplication: _appdir: Optional[object] = None _apppath: Optional[str] = None + def supports_hardware_target(self, target: str): + return target in self.targets or "all" in self.targets + class AppManager: def __init__(self): @@ -158,15 +161,26 @@ class AppBuildset: FlipperAppType.STARTUP, ) - def __init__(self, appmgr: AppManager, appnames: List[str], hw_target: str): + @staticmethod + def print_writer(message): + print(message) + + def __init__( + self, + appmgr: AppManager, + appnames: List[str], + hw_target: str, + message_writer: Callable = None, + ): self.appmgr = appmgr self.appnames = set(appnames) - self.hw_target = hw_target self._orig_appnames = appnames + self.hw_target = hw_target + self._writer = message_writer if message_writer else self.print_writer self._process_deps() - self._filter_by_target() self._check_conflicts() self._check_unsatisfied() # unneeded? + self._check_target_match() self.apps = sorted( list(map(self.appmgr.get, self.appnames)), key=lambda app: app.appid, @@ -175,28 +189,32 @@ class AppBuildset: def _is_missing_dep(self, dep_name: str): return dep_name not in self.appnames - def _filter_by_target(self): - for appname in self.appnames.copy(): - app = self.appmgr.get(appname) - # if app.apptype not in self.BUILTIN_APP_TYPES: - if not any(map(lambda t: t in app.targets, ["all", self.hw_target])): - print( - f"Removing {appname} due to target mismatch (building for {self.hw_target}, app supports {app.targets}" - ) - self.appnames.remove(appname) + def _check_if_app_target_supported(self, app_name: str): + return self.appmgr.get(app_name).supports_hardware_target(self.hw_target) + + def _get_app_depends(self, app_name: str) -> List[str]: + # Skip app if its target is not supported by the target we are building for + if not self._check_if_app_target_supported(app_name): + self._writer( + f"Skipping {app_name} due to target mismatch (building for {self.hw_target}, app supports {app_def.targets}" + ) + return [] + + app_def = self.appmgr.get(app_name) + return list( + filter( + self._check_if_app_target_supported, + filter(self._is_missing_dep, app_def.provides + app_def.requires), + ) + ) def _process_deps(self): while True: provided = [] - for app in self.appnames: - # print(app) - provided.extend( - filter( - self._is_missing_dep, - self.appmgr.get(app).provides + self.appmgr.get(app).requires, - ) - ) - # print("provides round", provided) + for app_name in self.appnames: + provided.extend(self._get_app_depends(app_name)) + + # print("provides round: ", provided) if len(provided) == 0: break self.appnames.update(provided) @@ -204,7 +222,6 @@ class AppBuildset: def _check_conflicts(self): conflicts = [] for app in self.appnames: - # print(app) if conflict_app_name := list( filter( lambda dep_name: dep_name in self.appnames, @@ -231,6 +248,17 @@ class AppBuildset: f"Unsatisfied dependencies for {', '.join(f'{missing_dep[0]}: {missing_dep[1]}' for missing_dep in unsatisfied)}" ) + def _check_target_match(self): + incompatible = [] + for app in self.appnames: + if not self.appmgr.get(app).supports_hardware_target(self.hw_target): + incompatible.append(app) + + if len(incompatible): + raise AppBuilderException( + f"Apps incompatible with target {self.hw_target}: {', '.join(incompatible)}" + ) + def get_apps_cdefs(self): cdefs = set() for app in self.apps: diff --git a/scripts/fbt_tools/fbt_apps.py b/scripts/fbt_tools/fbt_apps.py index 96528f4e5..9dbe30720 100644 --- a/scripts/fbt_tools/fbt_apps.py +++ b/scripts/fbt_tools/fbt_apps.py @@ -1,5 +1,6 @@ from SCons.Builder import Builder from SCons.Action import Action +from SCons.Errors import StopError from SCons.Warnings import warn, WarningOnByDefault from ansi.color import fg @@ -32,9 +33,13 @@ def LoadAppManifest(env, entry): def PrepareApplicationsBuild(env): - appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( - env["APPS"], env.subst("f${TARGET_HW}") - ) + try: + appbuild = env["APPBUILD"] = env["APPMGR"].filter_apps( + env["APPS"], env.subst("f${TARGET_HW}") + ) + except Exception as e: + raise StopError(e) + env.Append( SDK_HEADERS=appbuild.get_sdk_headers(), ) diff --git a/scripts/fbt_tools/fbt_hwtarget.py b/scripts/fbt_tools/fbt_hwtarget.py new file mode 100644 index 000000000..b4e1e58ac --- /dev/null +++ b/scripts/fbt_tools/fbt_hwtarget.py @@ -0,0 +1,134 @@ +from SCons.Builder import Builder +from SCons.Action import Action +import json + + +class HardwareTargetLoader: + def __init__(self, env, target_scons_dir, target_id): + self.env = env + self.target_scons_dir = target_scons_dir + self.target_dir = self._getTargetDir(target_id) + # self.target_id = target_id + self.layered_target_dirs = [] + + self.include_paths = [] + self.sdk_header_paths = [] + self.startup_script = None + self.linker_script_flash = None + self.linker_script_ram = None + self.linker_script_app = None + self.sdk_symbols = None + self.linker_dependencies = [] + self.excluded_sources = [] + self.excluded_headers = [] + self.excluded_modules = [] + self._processTargetDefinitions(target_id) + + def _getTargetDir(self, target_id): + return self.target_scons_dir.Dir(f"f{target_id}") + + def _loadDescription(self, target_id): + target_json_file = self._getTargetDir(target_id).File("target.json") + if not target_json_file.exists(): + raise Exception(f"Target file {target_json_file} does not exist") + with open(target_json_file.get_abspath(), "r") as f: + vals = json.load(f) + return vals + + def _processTargetDefinitions(self, target_id): + self.layered_target_dirs.append(f"targets/f{target_id}") + + config = self._loadDescription(target_id) + + for path_list in ("include_paths", "sdk_header_paths"): + getattr(self, path_list).extend( + f"#/firmware/targets/f{target_id}/{p}" + for p in config.get(path_list, []) + ) + + self.excluded_sources.extend(config.get("excluded_sources", [])) + self.excluded_headers.extend(config.get("excluded_headers", [])) + self.excluded_modules.extend(config.get("excluded_modules", [])) + + file_attrs = ( + # (name, use_src_node) + ("startup_script", False), + ("linker_script_flash", True), + ("linker_script_ram", True), + ("linker_script_app", True), + ("sdk_symbols", True), + ) + + for attr_name, use_src_node in file_attrs: + if (val := config.get(attr_name)) and not getattr(self, attr_name): + node = self.env.File(f"firmware/targets/f{target_id}/{val}") + if use_src_node: + node = node.srcnode() + setattr(self, attr_name, node) + + for attr_name in ("linker_dependencies",): + if (val := config.get(attr_name)) and not getattr(self, attr_name): + setattr(self, attr_name, val) + + if inherited_target := config.get("inherit", None): + self._processTargetDefinitions(inherited_target) + + def gatherSources(self): + sources = [self.startup_script] + seen_filenames = set(self.excluded_sources) + # print("Layers: ", self.layered_target_dirs) + for target_dir in self.layered_target_dirs: + accepted_sources = list( + filter( + lambda f: f.name not in seen_filenames, + self.env.GlobRecursive("*.c", target_dir), + ) + ) + seen_filenames.update(f.name for f in accepted_sources) + sources.extend(accepted_sources) + # print(f"Found {len(sources)} sources: {list(f.name for f in sources)}") + return sources + + def gatherSdkHeaders(self): + sdk_headers = [] + seen_sdk_headers = set(self.excluded_headers) + for sdk_path in self.sdk_header_paths: + # dirty, but fast - exclude headers from overlayed targets by name + # proper way would be to use relative paths, but names will do for now + for header in self.env.GlobRecursive("*.h", sdk_path, "*_i.h"): + if header.name not in seen_sdk_headers: + seen_sdk_headers.add(header.name) + sdk_headers.append(header) + return sdk_headers + + +def ConfigureForTarget(env, target_id): + target_loader = HardwareTargetLoader(env, env.Dir("#/firmware/targets"), target_id) + env.Replace( + TARGET_CFG=target_loader, + SDK_DEFINITION=target_loader.sdk_symbols, + SKIP_MODULES=target_loader.excluded_modules, + ) + + env.Append( + CPPPATH=target_loader.include_paths, + SDK_HEADERS=target_loader.gatherSdkHeaders(), + ) + + +def ApplyLibFlags(env): + flags_to_apply = env["FW_LIB_OPTS"].get( + env.get("FW_LIB_NAME"), + env["FW_LIB_OPTS"]["Default"], + ) + # print("Flags for ", env.get("FW_LIB_NAME", "Default"), flags_to_apply) + env.MergeFlags(flags_to_apply) + + +def generate(env): + env.AddMethod(ConfigureForTarget) + env.AddMethod(ApplyLibFlags) + + +def exists(env): + return True diff --git a/scripts/fbt_tools/sconsmodular.py b/scripts/fbt_tools/sconsmodular.py index b115706cb..57ae8f055 100644 --- a/scripts/fbt_tools/sconsmodular.py +++ b/scripts/fbt_tools/sconsmodular.py @@ -22,6 +22,8 @@ def BuildModule(env, module): def BuildModules(env, modules): result = [] for module in modules: + if module in env.get("SKIP_MODULES", []): + continue build_res = env.BuildModule(module) # print("module ", module, build_res) if build_res is None: diff --git a/site_scons/commandline.scons b/site_scons/commandline.scons index e01c8a39e..e3ddc59aa 100644 --- a/site_scons/commandline.scons +++ b/site_scons/commandline.scons @@ -79,6 +79,7 @@ vars.AddVariables( default="7", allowed_values=[ "7", + "18", ], ), ( @@ -197,7 +198,7 @@ vars.AddVariables( # "basic_plugins", # Debug # "debug_apps", - ) + ), }, ), ( diff --git a/site_scons/extapps.scons b/site_scons/extapps.scons index bff9a8c30..abe1a4534 100644 --- a/site_scons/extapps.scons +++ b/site_scons/extapps.scons @@ -16,7 +16,7 @@ appenv = ENV["APPENV"] = ENV.Clone( ) appenv.Replace( - LINKER_SCRIPT=appenv.subst("$APP_LINKER_SCRIPT"), + LINKER_SCRIPT_PATH=appenv["APP_LINKER_SCRIPT_PATH"], ) appenv.AppendUnique( @@ -78,17 +78,20 @@ known_extapps = [ if extra_app_list := GetOption("extra_ext_apps"): known_extapps.extend(map(appenv["APPMGR"].get, extra_app_list.split(","))) +incompatible_apps = [] for app in known_extapps: - if not any(map(lambda t: t in app.targets, ["all", appenv.subst("f${TARGET_HW}")])): - warn( - WarningOnByDefault, - f"Can't build '{app.name}' (id '{app.appid}'): target mismatch" - f" (building for {appenv.subst('f${TARGET_HW}')}, app supports {app.targets}", - ) + if not app.supports_hardware_target(appenv.subst("f${TARGET_HW}")): + incompatible_apps.append(app) continue appenv.BuildAppElf(app) +if incompatible_apps: + warn( + WarningOnByDefault, + f"Skipping build of {len(incompatible_apps)} incompatible app(s): " + + ", ".join(f"'{app.name}' (id '{app.appid}')" for app in incompatible_apps), + ) if appenv["FORCE"]: appenv.AlwaysBuild( diff --git a/site_scons/firmwareopts.scons b/site_scons/firmwareopts.scons index 9f707b4d8..e4cc8db58 100644 --- a/site_scons/firmwareopts.scons +++ b/site_scons/firmwareopts.scons @@ -49,18 +49,15 @@ ENV.AppendUnique( ], ) -ENV.SetDefault( - LINKER_SCRIPT_PATH="firmware/targets/f${TARGET_HW}/${LINKER_SCRIPT}.ld", -) if ENV["FIRMWARE_BUILD_CFG"] == "updater": ENV.Append( IMAGE_BASE_ADDRESS="0x20000000", - LINKER_SCRIPT="stm32wb55xx_ram_fw", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_ram, ) else: ENV.Append( IMAGE_BASE_ADDRESS="0x8000000", - LINKER_SCRIPT="stm32wb55xx_flash", - APP_LINKER_SCRIPT="application_ext", + LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_flash, + APP_LINKER_SCRIPT_PATH=ENV["TARGET_CFG"].linker_script_app, ) From ce17c0d55ef8596fb2f1ce45929d0085e501aa48 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:14:30 +0100 Subject: [PATCH 105/231] Update archive_browser_view.h --- applications/main/archive/views/archive_browser_view.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a7414fffe..a525a7db6 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -9,7 +9,10 @@ #include #include #include -#include +#include "../helpers/archive_files.h" +#include "../helpers/archive_menu.h" +#include "../helpers/archive_favorites.h" +#include "gui/modules/file_browser_worker.h" #define MAX_LEN_PX 110 #define MAX_NAME_LEN 255 From 189055d8f36e29ba93fa597a447be5804786f06a Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:44:13 +0100 Subject: [PATCH 106/231] fix bs --- applications/main/gpio/gpio_item.c | 51 +++++++++++++++++++++++++++++ applications/main/gpio/gpio_item.h | 15 +++++++++ firmware/targets/f7/api_symbols.csv | 12 +++---- 3 files changed, 72 insertions(+), 6 deletions(-) create mode 100644 applications/main/gpio/gpio_item.c create mode 100644 applications/main/gpio/gpio_item.h diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c new file mode 100644 index 000000000..2d0f5f676 --- /dev/null +++ b/applications/main/gpio/gpio_item.c @@ -0,0 +1,51 @@ +#include "gpio_item.h" + +#include + +typedef struct { + const char* name; + const GpioPin* pin; +} GpioItem; + +static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { + {"1.2: PA7", &gpio_ext_pa7}, + {"1.3: PA6", &gpio_ext_pa6}, + {"1.4: PA4", &gpio_ext_pa4}, + {"1.5: PB3", &gpio_ext_pb3}, + {"1.6: PB2", &gpio_ext_pb2}, + {"1.7: PC3", &gpio_ext_pc3}, + {"2.7: PC1", &gpio_ext_pc1}, + {"2.8: PC0", &gpio_ext_pc0}, +}; + +void gpio_item_configure_pin(uint8_t index, GpioMode mode) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, false); + furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); +} + +void gpio_item_configure_all_pins(GpioMode mode) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_configure_pin(i, mode); + } +} + +void gpio_item_set_pin(uint8_t index, bool level) { + furi_assert(index < GPIO_ITEM_COUNT); + furi_hal_gpio_write(gpio_item[index].pin, level); +} + +void gpio_item_set_all_pins(bool level) { + for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { + gpio_item_set_pin(i, level); + } +} + +const char* gpio_item_get_pin_name(uint8_t index) { + furi_assert(index < GPIO_ITEM_COUNT + 1); + if(index == GPIO_ITEM_COUNT) { + return "ALL"; + } else { + return gpio_item[index].name; + } +} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h new file mode 100644 index 000000000..5cb2b86c1 --- /dev/null +++ b/applications/main/gpio/gpio_item.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define GPIO_ITEM_COUNT 8 + +void gpio_item_configure_pin(uint8_t index, GpioMode mode); + +void gpio_item_configure_all_pins(GpioMode mode); + +void gpio_item_set_pin(uint8_t index, bool level); + +void gpio_item_set_all_pins(bool level); + +const char* gpio_item_get_pin_name(uint8_t index); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index eccc33cbd..ef978eca9 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.7,, +Version,+,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,7 +205,6 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, -Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2077,9 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" +Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, -Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 894fe99abd8181b6eee87d03392114211353c4b8 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:47:52 +0100 Subject: [PATCH 107/231] Revert "fix bs" This reverts commit 189055d8f36e29ba93fa597a447be5804786f06a. --- firmware/targets/f7/api_symbols.csv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ef978eca9..eccc33cbd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.8,, +Version,+,12.7,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,6 +205,7 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, +Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2076,10 +2077,9 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,+,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" -Function,+,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, -Function,-,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, +Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 0bc1a003685a242200c3fb4a009938d83105b643 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 7 Feb 2023 19:48:30 +0100 Subject: [PATCH 108/231] Update api_symbols.csv --- firmware/targets/f7/api_symbols.csv | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index eccc33cbd..ebfeec172 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.7,, +Version,v,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -205,7 +205,6 @@ Header,+,lib/toolbox/stream/string_stream.h,, Header,+,lib/toolbox/tar/tar_archive.h,, Header,+,lib/toolbox/value_index.h,, Header,+,lib/toolbox/version.h,, -Header,+,lib/u8g2/u8g2.h,, Function,-,LL_ADC_CommonDeInit,ErrorStatus,ADC_Common_TypeDef* Function,-,LL_ADC_CommonInit,ErrorStatus,"ADC_Common_TypeDef*, LL_ADC_CommonInitTypeDef*" Function,-,LL_ADC_CommonStructInit,void,LL_ADC_CommonInitTypeDef* @@ -2077,9 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,-,nfc_util_bytes2num,uint64_t,"uint8_t*, uint8_t" +Function,?,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" +Function,?,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,-,ulTaskGetIdleRunTimeCounter,configRUN_TIME_COUNTER_TYPE, -Function,-,ulTaskGetIdleRunTimePercent,configRUN_TIME_COUNTER_TYPE, +Function,?,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,?,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,-,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, configRUN_TIME_COUNTER_TYPE*" +Function,?,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 15ec5b4e0dd3c7cac33a795d46899e21cc422971 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Wed, 8 Feb 2023 03:58:08 +0100 Subject: [PATCH 109/231] fixed api, fixed cartfiles & removed my stupidity --- applications/main/gpio/gpio_item.c | 51 ------------------ applications/main/gpio/gpio_item.h | 15 ------ .../main/gpio/views/gpio_i2c_scanner.c | 2 +- applications/main/gpio/views/gpio_i2c_sfp.c | 2 +- assets/resources/wav_player/CartKeyLock78.wav | Bin 365342 -> 61032 bytes .../resources/wav_player/CartKeyUnlock78.wav | Bin 396548 -> 66234 bytes firmware/targets/f7/api_symbols.csv | 12 ++--- 7 files changed, 8 insertions(+), 74 deletions(-) delete mode 100644 applications/main/gpio/gpio_item.c delete mode 100644 applications/main/gpio/gpio_item.h diff --git a/applications/main/gpio/gpio_item.c b/applications/main/gpio/gpio_item.c deleted file mode 100644 index 2d0f5f676..000000000 --- a/applications/main/gpio/gpio_item.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "gpio_item.h" - -#include - -typedef struct { - const char* name; - const GpioPin* pin; -} GpioItem; - -static const GpioItem gpio_item[GPIO_ITEM_COUNT] = { - {"1.2: PA7", &gpio_ext_pa7}, - {"1.3: PA6", &gpio_ext_pa6}, - {"1.4: PA4", &gpio_ext_pa4}, - {"1.5: PB3", &gpio_ext_pb3}, - {"1.6: PB2", &gpio_ext_pb2}, - {"1.7: PC3", &gpio_ext_pc3}, - {"2.7: PC1", &gpio_ext_pc1}, - {"2.8: PC0", &gpio_ext_pc0}, -}; - -void gpio_item_configure_pin(uint8_t index, GpioMode mode) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, false); - furi_hal_gpio_init(gpio_item[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh); -} - -void gpio_item_configure_all_pins(GpioMode mode) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_configure_pin(i, mode); - } -} - -void gpio_item_set_pin(uint8_t index, bool level) { - furi_assert(index < GPIO_ITEM_COUNT); - furi_hal_gpio_write(gpio_item[index].pin, level); -} - -void gpio_item_set_all_pins(bool level) { - for(uint8_t i = 0; i < GPIO_ITEM_COUNT; i++) { - gpio_item_set_pin(i, level); - } -} - -const char* gpio_item_get_pin_name(uint8_t index) { - furi_assert(index < GPIO_ITEM_COUNT + 1); - if(index == GPIO_ITEM_COUNT) { - return "ALL"; - } else { - return gpio_item[index].name; - } -} diff --git a/applications/main/gpio/gpio_item.h b/applications/main/gpio/gpio_item.h deleted file mode 100644 index 5cb2b86c1..000000000 --- a/applications/main/gpio/gpio_item.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include - -#define GPIO_ITEM_COUNT 8 - -void gpio_item_configure_pin(uint8_t index, GpioMode mode); - -void gpio_item_configure_all_pins(GpioMode mode); - -void gpio_item_set_pin(uint8_t index, bool level); - -void gpio_item_set_all_pins(bool level); - -const char* gpio_item_get_pin_name(uint8_t index); diff --git a/applications/main/gpio/views/gpio_i2c_scanner.c b/applications/main/gpio/views/gpio_i2c_scanner.c index 0f00639d6..fc9ed78a7 100644 --- a/applications/main/gpio/views/gpio_i2c_scanner.c +++ b/applications/main/gpio/views/gpio_i2c_scanner.c @@ -1,6 +1,6 @@ #include #include "gpio_i2c_scanner.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include diff --git a/applications/main/gpio/views/gpio_i2c_sfp.c b/applications/main/gpio/views/gpio_i2c_sfp.c index 85492e783..c8e14ce7a 100644 --- a/applications/main/gpio/views/gpio_i2c_sfp.c +++ b/applications/main/gpio/views/gpio_i2c_sfp.c @@ -1,6 +1,6 @@ #include #include "gpio_i2c_sfp.h" -#include "../gpio_item.h" +#include "../gpio_items.h" #include diff --git a/assets/resources/wav_player/CartKeyLock78.wav b/assets/resources/wav_player/CartKeyLock78.wav index 71f56f328b076c708d621fa29c2cd93e022881f4..744f9d1a0f4f62944e6249d7a042cfc6ba9f6184 100644 GIT binary patch literal 61032 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 literal 365342 zcmeFZXH;8Rwlzxkt?qJHvddKt*cgm4(O|&jY;rI;=bS+z34sDa2$3WZLPF%6bB-n& zFgb$(6Ad;vm#bXu3SHIB$GQxU?tbsyJHGM0_kO%F&i><#ES)gV*?X-$*PPqZP*3k7 z`%heUnzp*WD9rV9TwGj#;QAvMx$mSC1-&y|-X0i__pCp0k&%WVfFOV%fFOV%fFOV% zfFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV% zfFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV% zfFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV% zfFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV%fFOV% zfFOV%@PC9r+<&(JqeCD+Mi4*{KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF> zKoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF> zKoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF> zKoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoCF>KoI!<6#}qDH<=t8OD2Ep z{ZD<$Nh6alK~;^7NWN_TgXn_Qy~E*+2c|e|?Al{^>vO_P_u9-_LV;egD<( z;0SSXr{|_npcfMp8yg>=kPsI~AmDJZu_-BZdR$yYL}+MiY-VP5Hj!v;t*h(jS6jQW zF+EM8C@I~#<>%Mav$0WKZEnubud7>7u)aPx7!V*VEH9swG&lF~Ar>npCLuv2&d)!3 zM5oKki;9MYO-=3Y78K~{h>N38latS$Ra9te^YGZ(_VykgwzL=-UA(BPTU-0&NoS{} z<>kwF?^abE9gU7UJ72maFJDyj`t|B+c({OotZZiH;oY%D)N z4^Mdb{QUZQe7uklH+Mk5#Khyrsj0VaUA*Y!H9h_2O?|zgp`f6r=jiC`*X`|=mO?`I z_B}l>U-tDmIPml9=#-RfZua+kc?k-tsj*m#i~aop0U{!biZt5N()4spjGUajd~EF0 z)WSkqnvRZ&3XwQ5vAoP;X=&ZQ9T0H;{_1LBp`oFWkgaV^&B8)Og{7s4h`D)H)%tp8 zr<K~au)MswnwTgpEhrciG(5bxNT?Z6&1O;9UKM*UcT(;u(lQwva{>%e)Vc_(8Wbi(8Q#<`St7ZaZk@{*Ax}A zvv+qVCj$fd`S09GOT($W+ZBqVUSrKP<+4o6XupWnx4baZd8s7OtX zo7>W|rRDMC%1V9xYuC)onwy_IX>b2{56_=ZP6h>uifU?pyay2xxQD&H)l~vP=%htO z&CDz(`Z)Ih>7+kr97?etsDl24j1Bd6_^E6%`kci(6aU+)PcCmzR*h zLTT$`k$>5;7J0G92 za#q&H#)Aibem8E&$uXJR+e=H)(IO%e67lit>zkW2nw;G2+XTY$^3D#6B`+@^5EwW< zzPFd3udICevXxb9>-u_Wsg4d0kA+1`%kFMdld0*|s|E%Q4KH6#P6h;sit6f?lpGvP zPltqvimIw+XYcQ?tVBf#3W|sji3UXJutN zIxwamJUBdTY&0_y5pi+p?|<|CE-htc z-MxF`hLclA$NG9%nSsIe>(-9qQcMbS|S$ znc2;orlvJDySu}~D3qL>iAiDMqesKT;o%Aj1_m6?%F6gS9xpGitQ;FVGVl{Gzm z|9)hoy1J23TH3_KzyJ=XeA1$#IyyQ#aX3{~B_%Ajr{}?gq$DjZIXN`?{{7|U(o#D+ zB_$u9mX_7krY0{hWo1Xlii)MBuCBm9Sy??j24iBPw>LaoMh5O@cz9qSD(cQ18JU=v zxw-ZAj0|=4TencCv9aCVvN9thF)=T%p`pXW=4MmVYu5}70j1tvXJ>eqa&jI$x_{r( z^X5%8we0Ne?Ww8IP$8i^cT!R|H`mt_6J=$kq=>}D#m!9?OI1}=G$?3fWP7`;%+T=0 z4HJ|6{E?B85(ftv8B0qJr@z0v+}HQ+T?dD}yorgfE;WH7?Q+}+XW^z^E#!a_XW$;riqL@F(< zuBOp^eVv>lBC@k9Dj1A_00)QQ;Earl3Jxbc+|@NGh)ypmVzDA3JUq~7CbOa?p3J3&ucP}q8xuBq^C^0c8$lE(2A~`uDBPAs!CMpV#hb)dnijSw$Q&TCF z@bKVZJf6iWDoRfe4Rvzz^P|&iYAPzoWG5$E+sMeGqJ{=GJ1EG`&fh;ZwY)qh2Z!_W z3Jgq4EGWpyA(8z2e0>Q77K_87QbR+7g9!u%gUwD%#9+|q=;)LbCNn81Dk?M-hojNh z?9@~o4uuL2r&8JMjEvaWkPtLFCWgUaGAWd>u#gZEiOFQMgtXT8qLZ| zM<*ntscC$?q{P)#L&L+PqGEcwr6nXpPtVaYCud}&xj7_6L&MZGE$zXB?(V24H8pkh zsHoP~jt&w@TU$c|i|yzb8luyUj8s)aL%X{d7Ah+}JoNNXsFs%b`I;I}PgPY1hr+^% ziIx@=N=?nkh)Qj5udk1eHZ`@ep;8+fs;Y1}D=SCGq@?ENh6W17#l_t{A)%_Oq=ZOx zcZWBrp`oAvjW#iHbWBdJsmaR=3NkWsbYw7ETZ@Z*eKj;JEi*GGCi?pDcuh?MgOrqs zla`#Ur>CVA8#_8WJIi3`=*Y^VP`$m2iv5}E1qCW~adCM$B}Gk5QISlZn_F2)Ns*Nm5(*A}@L**n zJzZX&m)FImt7~bA!%@JLFgrfzL5EkQIA6QfWzHnz54Zk3TqNZ8qV_KeAt zl;q(F2$-9D^{S#mLxY>!+PbrIZ?Cr2$mrTN6O+2St*zEpYim9}UEQ*>Cr`%5Lqla` z^z=BK<>iqPELKrbTRSy%W@cs+=Y)nfF4D|7#P%0{FYEn`>JglrjLrY7ChFV)=V+{>u zW$)c15^HOFdMYc)WKT~Qm(bAE)WX8t+|#j%$ET)dWF#hHv0-6EVp>{eW^ytH6A%y{ zo|wpHr=<}HC{$=Dg~DW}r^m*IowV54)YP=J_;}n&i;GK2qEZP2EH)-4C56FANPy!I z2&t(oR$^j!ctAj8B%Pj3`T3zx@$noEixnG-Mu&uu$>}GJOvYfMqNr2`BP9io z4-13&fyGKrj*JWpjEG1{;c!w@BP0F%LPL|2b8}fNJl@|Qg`!e(ayXpW*r1@`U^1C~ z(qOI#3xi{(rV&zJgTH@BNK#T>US=kd=q4@WY3}L2GMXUQ^Sap1=NeYRc91 z(j^6jf`Y@tnVEn99-iB`Q&TrL=jS6LZr&6Zr&1q1T3(Kc5j$xl66Dp8lZuN+M=vgJ zZ?jlRN}{6S;j^<(p1?Zc$`xnl`}ZF|&dpU)x_sH*eqiAF^M(c;o%84Q^;=qAzaANJ zbGvavU%#&I)vKAAz`&a~RaA0w4-OU=BO^sbq@}6U`T2VT!-s{1 zc6N8}7#pXh_4L%#1P80A=;*}8_Vta8F`0ULyu7lq(b2uV3kz*+F)_BbR#qV)Y<6X3 zK>>vl6y)z89i5iO;m~OCo`r=aB(T}(=`k_E!6+1ol%AfE5f^tFd!SFDgocKM#KmQ0 z<>e(OqtOsO8H~b0HXDcY^9v4+i-X7&7Z-lgVq@v_loS#vGBP@vN=;2oO2XsA!{H4- z?MFo5@iZEXMWaPW!ipsVj_uz!x0D+N$U1Nh6WqO;1P&2=MTT ziptHcuFl8^2(YmU3d+rGY2k1n6EZU^E`IuSXNOLgl)QAw!J)tZ_3MEFCnp{rMMW0t z@ncvkD=Dd~1G_Lgo0zDsuA%~S|JqtchMJnFsIPBt@A5Kaw}OJs&Yhi`n^jdX0~;GR zHa>qoHs-wy$?d^qym>2~GHMOLq+1bg-xHwf+6_uEnp`qbn$Zln2F_@m7{(d}O zS{m}_v9U*w*lbnRn>Rf@hlig(@9cDTmXdOIZf`$00JcC(OiwRAe`8~OJSs{~PE9R7 zeq>~DkVH~fS5=LSY;EoCrcewFw6rjorl$M%)6<=tEG!6w=H}jBCez8u+#HW@X&D*< z3^X-8JaTf{+H!LI{7g-KeAw*T+Kdb@FB21A-`w1;uClVQFiT5sZw{xkv$`6KwX}3` zNl)+XZEuf@GdI6?4~wm>?dV`I92|6XJUrO!`g%6o(^E&s);1}rp`o&pNVKFtD@J+Z!6HrlzBV$2T|k^sw1pUY3?YL7ACV zRV)??<>Z7yu~^mB+1X)X_V(W1G#ca*1cI}(t1FpYR#s7wkl^X*>>M4PpI=cyqxtzc zJ7X|eS!HFZsR04b&S7C3PGx0AMsTpBBOIx!io*#Bac~F-$jGR#FDwiXx3Y3}PE2fQ zsIN~2x}g7#4=bCL}Nz5XVA8i9{wd zCnqH(G}OljgJH2sN|?-$5GSXAfYj8=%KZH3XeTFk_qe!{lG4)ncyDhH4?G^mc0vLQ z6%<4!b50t>7Km3_S!_0u7!(v53Rjt)j>Y=4XwQ9(eZIb#f=+WTsAgaTgS)!{fdfQT*k)B z%g4vFvszkQTvAd!J;%p8JGQo5T$e6oW&Q1MhlgnNnKR#fL!rES_3~wWJP*&MOIYmo z_N!M}S@QD2!Whi_JTL)DN@8M>k*9q*xog)vJo@^Uml+HhnJZUZUEysjECiOr)^=dv z?OWjC1O1IGk&;qWjE|p~7#YFg?%a`< zj*cE3otsNb)6$ZWIb8>ql-Sv+s|N>nbSy1ZRXIA!%G%nNmM$!Gb_NDYORK6v`HaR*vGgDF$5MXNR>>L+YR8(4;ni>(|?;jRMrLtJ38B9nBtS;E>q@;+5 zfB+08B_%tX$;9FO{ey!klu%8HK<4h{$)64~sW z94a+9_+#Y%I3H$Zm6p=!r)x7Nv$8TL2i9Qz{z*wiML9VHg15K7KZTNa(moz3BLm)} z$VgZhB_%-)1T!>|7!!lXM@PeZf0`GCgoKAvC>bY>Oh%tH$Xnog(CDBbA`y;~kN|HZ z9-p3`nR&Wa!sAar4+#kjgw!;t^n@|{FJUP@NK|P zb#$1UU%srZU0?t9?d)toz>OQKs)dCwUaYQ0MhXeZ%4TFdd$zerAY8xB%ZtS>Ev>J| z#R&*pz8oC9u&}pBqusdi*=KHUD=WYK*4e3}bMc~`-NeLizYPzYn_s$QYC1gp_rFg} zn3(+WkK*E$mB0M5w&v#cr$6!XvRLom@9(40pMHAo9EI}Zk3avMmd4Hf`R8c#;o;x^ zUREX{aqgU--`?KwaYMuH+gw}*2IJ%Z_(xx#x;ht^j7(kKn>T%ZhK7Isvw}in<1fD~ zEI2uR`K6>}Y3YX#TU%%}H@CEOQPGDF`}+|Q7cUA3q^JMz!^@Yku@^6X^9>fevvY7j zCSSbx#TO{l_V(MiaAqzp7njY=;gvG_J zEm(ElycrPi;K9a5PL75KKfj$_UES1FR+g@=u&}dpSJ(c2M~98g`SaJW2L-jY0Rt2r zjYgA5bUK+FeA1GVs;a81DHMBqbMwf^^77VJCezEy+B!VEu&};9EzQHj+BzblxVW*A z!SM97whjx+&#$gdNpW+twZ-A8t6N%VG&?&ZBj6u;dpR6uXI))i-{Rul-u!$oF9QQ_ z@4~|S_hD79ukY@jm)F-{ST7Q3MCy{7y(g=i>md?(^ zL`zE}BN(k+T^SiJF2=^e!G(pbtvNX;l)ZgW5R+M2%4CLxd3$5AnVE%!$;puCMMvl6 zmY37%{{Bu*;o(_Xm6c3pP|&|deS3R9zqGWPn!>{9Xa@&Z*SNUS(y}rt)z{bCn@lb$ zDk+JN_waCZ#9|8y>gw3+z(8ADKfm;J_(fEdy}hd|g;H5rU7eT+2!w~{;LT=r}m|`gK>Ajm^!QmX_`9FJBH1ySWJp>gz)!92xQT6%qpWabpA6 z2|hkq+4S_skAVf|NQ&5mdTwUGRpi)Ief%ACyaBVFuO-c%)d4K=bR&lYOo{*56TVLP)esi;(os^WD zTTjokXFWZxuD5QPnwFQZt#x<%_()0Jy9aDwo>)~N% z=j+#XbgHUey_%m#p#%k$l(Ms*JzHDD;RFP3-h^5UtemB#`S?6NVU|u!1&-F+yR&m{ zE-Oo4Us^gSXlQ6}uc5)gL0;a+r>hH|w7tEgq@7)T{rY-qtFyC+h^{V&Gc(iP?&l{X zbMIbO*33*>o45C^TX*lWSgWg3Q!z2h%34~fsSufBV-*#Zl;YzT7XdLPB_Sbi@2;-d zSq?`}PeQ`oy|Hn6IzQjcOj;UZ-|})}qo=2e3b1E0GfhoFL0Vc)P6Y*HV+{>KL3i(3 zTeDdG{ngd}{wgZ>?xm#k^wia%P_U9Ele@ZrJ%rI47x&=7{Cr`djg6$Fre;J$etv0b zMh2CdkdT}l8;e3gT+Ys}u7(WU*4D!#DXFruxHu-p-QC+eKEANy8^gkMbuBHElRG*Z8;L}7b4$yZ zn3@_`2e`NZf1I0JRz{-%3ysHfIKV`~ikw8s%F4;1Pyz#?pPikbpO^?a7I40huK@o` zAh6lFx$rqKyG&+bA@ILmUcteMi8(nR*`b7lf`a^f3dPsg-5rbNaLUTq?1%_AH$Oir zwXiThpG@}g@$|&wv$Knf;X2&h!oq-w=Wrq;Jv~vVw6u~E7Aq{w)ipFUC#R{2#exxF zV33{tp#SWmB@U}FO^T6uYW{hXZ5&FSfg2mt{hq3GzD z8K@Bn2wb^>MlUTL9dS5fVqbjW=(xCed^|9qr}x=sT3Yw-A0Mx+d3c;Zf9FnF*@q9? z+aVzrE(i$FXwRN)ZDFyOE^%|?@td1FJ0ub}_g7y92Cl8Wd6SnXEq&>dxA*+~yLYv< znwpT6Pf!2#uU%cn#ymWhmR(&(M;#rOmVA6BCiV4OTaAq-COka%?lm?(ecIV+ZhrBi zrY7VO)6-sF*RH9lRaCrvyRhKr2l2R|;D;Y}cf-QYox5-WhuhrT-X@W*Uj6plkdT#? zy}hI)e*Sak0s=NRe)_4pT1n~KZ(UsG=6?OPr^nFn(j{~Afq@SnMn-IGE?$JA9v+U4 zy1QS!Dleaz`S|gJ2VP!Ru1HAGXs2~9NlBHr)|N`Gtjx;t_0`jJa7asQX(=j#I*p}ediwBicQ>&8R#tTS@bKVZ zVxozOkrA0ZFfcyOVwsz(s`~ra)C>*f9)2yI)Q;@ zWnEn?7Oe68{VOXUJgBG$3^X=|I6pL0U5!Q?8+&-<;1)&}>ZrKPJI z6;)YTQGvx88Unl9($dwHmgeeeXGbD6Hgvc5%_y_wy?$>F+Ns_49))gw5{jYiNjyGBL5SNl0jDsIHEQ zad7bPNJ*)#&d(1GwXpE;$jE4GD=!ZXH8QfdPfhLYtgVfRFff3paQ}W=8xE(ZXJix~ z-*?i0bKYqsYKTM|8+&^SrMkMjoItR% z1Ky&(9)3C6zvi5d1+uhyB$lE(7r>m>7 z5`!@}x3i0jtFEr8Nlu2R5E+@7SyYso8XoTF2Ww3>J0&G5>a-4)k&&Dn6%`T^6$PKu zXi-r?L6MORMsYEV1@E(g0T$cUwZ0BDQ;m~GAUt@mzFu8zZ-48So*vXxMn+;|)zuXh zLql6y1_o#}Lqi1xZ|};=zCJ)lNy*Erre<=oqQc$%?p=TX_V&k*+uGdRZr`@FtgnCm zd}aoX77&n8+j!LD|q1qS~g~I_Kn~-qI z)5gV7si4UMmNxmM#l}WPVlX5Ujh2>1rA9|bM8w1Z#|!)w8XXk{H3vF9GSbHS}6YV{L6MEpfP-nvRZ)40m@^Q&FUHnz6$ z@y*RmO^`E~n2<>A?cLo83HR>Zy&DqJ&@ecdnQ3dQsR{Glz(8)Ui;IqquWwlyj9G7Q zeSI&l?Ci!y8VxjRSnS{+XifC>uUygAE-v2O931rZ6%PfQ1VUqDQ&UWg zp`o50e188vgW>3CZXO*CGhs%Cr>BDhncUji+nb(lZ*OQA91NB6loVTA0|OMQyu71> z$+WZ6((?8$DH#|jEe!}TGV=1u&+qH2tPBY;GxPS&&mSDDs|yR$*SE2u(@$qbJw0RN z*x35|%F3`XO-*g>=;)@Vj*i4cD=QO|n3&E^xHnf<3ky6R)}j!bbahdv#>R<>vNBg! zs4M5@_V+{OT0z0eDktaR!=@%*-#d4pS~)fK-~o}Ss(SZsV&d%V(o%Z5j*h&1csM9Q zva^khq@}&QpzaN*ii*0scXvO2Tv4H?$IX53UR&FD-%U<>dtbkFTZXeg-F zIGoq7_xFiJZtja0vDn?+w{KZ20f94T92_PlUc4wM5EuW`pG-|hM}PXMyIV)+i!Tfe z1_zIi=jQC}&Ye?HX>R`g_w{ve@AK!Sq)JMD`e|n;IQXltczIJ(-@JM91cQN$giL<* z>h=s_xwR@=I6OwYB5pjt&I{E-p>Y?(TQ* z+S@fW&YaQF>FxdNUwe8qG`P5wm3w=Sk5^Xg?9QB#k!fl=KHk~_#zR=Rvhw)&yYE6n z|MaJG=Moa$y?gmGI{J$*zWRzlfHTL$eEKODS76|a7stoN#r*t#_=B@EFs@x)YHH`s zo#u9ZecIZeeWt71-TlK40|Vyf7cS`NG&X|%$JqGnSyk2Q>Q}GE$6Z}7UzU^0$vHTf zpT}SzyU)yg^yuNk$Vf#+L&NOsm6hS)&`=2pIXMz(aIm{OB0^qXO^rv zy1hNE;0la}V;adqwK zSz9Y8(9{GiVqM+FMpKiGjevl@erf6U_UI@YEiDbzz_m4q8}jlR8mXx(D+>#t=#rF- zikg@JO@NAukdUWmN5|}JW~QblTt|ER>S{^Jy?Y`er|e97yS=@rC}=C+z8x8HcIM$x zS1&7j^=e@uDCpWXDXH}Iot?QkG@6G;L?k)+$&<&A@pxWdKE9Zky}cJN($gg*u3yJs z*4JOW$j+9L;o}PlSzCMeuBb>-@-KfeGwbU+I;yHtRsHfyef`!}h)f0s-+ZH|*V_8s zcjM!rveVM4s(ShIv}UTVUR3n_`QjoP&Bq7T+P%HCH9Y>t4FLf>9u#V^v4VoUykTLB zi#t0EhLjW^9}2a&`0^#FH*ees3R+xz_pYW!P3_`E2Zx6bfBdntQ%~>QIUSwm=9e#f zdreKxol{e*tAjJ!+kg4x?b`(f-+#Zgg~7ntn9QT2tt|pUKtMz!G4bisXU`H7Z`}Ci z8$Z99nZ3P?3~}*u=Nud!JOH(tlG53;CMJD-@83UoU~hl*s;+K*{hK$_(|&#e0(b8M zdP_@aG%qh??x5bpdpQA%1Se{J9qs3 zfuSug2e#JRySaI8uDsmINm<#&rMS4iKQGV0K}E&IrL=Tp$`?}kA0{YpDNW)73v?q`SG)VJ<9+68y~-Ydc@Pe zpnrPIkN^Ac|Ni$!`Sm+g!J_*D-RFBb0#RLt_~{Jg#}ns z+`5&LvaLIvO5!apB=nQOV2O*%=-6@qvnJM#lE`++0`~KmV;;2?^ULEh$M_ zT3kFPW^L`slZ*^GIdD_W&p&&XmnSE8{=9_+Fr=lWT3T1GfZhV?hDJu*+=hnD|LM&0 z^169bN9XkY2@Vz%1n17t5nMMsX)^iIBXI7BiV6sXhfhzhtWc?9Vmv&7f#3%!DpFU6 z=P)`78dzgvp%c&9$jEo!b$6SaU%8^M-_QVBDOjt5LS<{~!2_rYL(OD&cWy2;^!oK% zw~~@}c0S$%i3Hxi^mI8nA)$zf#l@#jVXgYjH=tR6{20y#m6*1+-QBu6L&M9Ljf`4b z-@JMF5bi-&x3co+XnGn{DeCHlg-1s#EAY08iN(jSt*xxY#0U%Dyotrm&VrjtM1+SY zC}?&TDqm`9{QO>CV`E20b#?ddo!-NnHys_OrdO`OlRi2c7;tpFdR0{w9FjvrFg6qv zva+5$nVp3du(&u>wzs#zA$j{YnY_NfyUSoe9UUB02M1YMGBOt~*xCZm1iraTm*6?< z?9|lg>0Q2TY~0@d_U+ghxOMdPYieMPeR>b@E-fvEhu^p%A(5E4v9YoO-akP>&?1Ar zM^u!T7loReJ2=S80|%9l5BL|Ws|^gmH#a)^=1p7My?cOCbMy1({rz@!+}vtvMMb;2 zLqo2vSFb84fNE%NE-2{gRZ&qI?ciW<58P{_qM#pn@`TBhl@$~W4_{n7Jj~BmP`G%} z+Pb}ceZ9C?OY7=YGc!;=RaV}+cm2AVSyR)~r@&y}zHM$^Q?tK6IvN}-DQRd}T)eY0 zF%cRnF0QOhr!Oo(J@NK!aI#HKK6nrjAtoj%85g&_ytbB@C?y3?eSZGn0N4~!Q8apX z_SLJJ8g1<>SFEhMyPrSr=rA+8bV*CAq5^z#@Jy7I^YfoPnVj_Z2iU>s~g2IIhmX;kIJ3CcX+S(T`7#nwXe*gXW zxSJbr9W^!If4{mK8hZ1lfbQI2_+e!{g!UCMOFEU%cq+J2P{1R8*v(05e5b z*WqDjr=6X!u!Ti!?bcRzx3{;ftess=&H8#rhr9c&TLuRC`OC|_z5f0Z5*iwG`uKQv zcW5YR>eJIFCc3-PXjxfZ-HeQdh0)RIXeA|GT{ipCBXH!3i_6K;>D${I8?muBZ}ReD zFe@v&yGcpByl2ihJI~F%dk1=@Km5VS=;6cTL^Dn>D(CF$qK3-q<^!)m3Y3Z`E zUw(P=BsBE=`D@n_6JNYIJfu*rT|0l?-+yWfkWo+&76xzG+8Xd+l9DLY;NZ$iUY@C` zib`bU$jItycDA0Lq-0=VfB)(#D2Vv@Y;BvHS69I`#?NnQ2@ck-E=NZ}!BgIBWCRow zckhA^aDF~2>h^8W!L6(;E&}s)`*v7ZU*G5`g`%h^ArTnZ-aaytlA@^z+J>&Kxw(P@ z;MBvzp;lU0Xl13Wj6#8iqpr@{T2$1+qO$VQqqa6zSMX-$=B};|3;?GiB}JvKtbn6i zSs8e}zP^=}k`h~66%{|f=H}Vi%1SpkH8mHPq9UkFdV8y@+t_4e^z@XKd3!4>o0z1h zkB;{Bkx05HEhgsv{jsr(3`&l^Fc>&oU|>iHm71LmI$Us7kjZ&@P~8X&baqCe z($gy{^73#v&^J&hg@xet^6_zZ2Ngs~37sAo2rd^E3*2!L5zfv&K8cB?rGN<9v&DN6-B4BSf>mQ)Nqkd3GW7Js#ogV3kFBUMHy0CgaA<2gIOy$la}yT_H6w7|o}O2( z$jZV@@%V96)U|8ZuY-?$bCXIH7Utmz37MPQ+Db_g6#V*YFR!JgAAf9UxO*3T1QQcK z|J>hiY6?ol{{EkT9vd?^|Lika*@}vH?`CJ6oj?CvL?k==yYDtO0s_AH;@UL^*M2+5>e65K67{9*!cV38yjV0KKbO{y^)chf9~$Sd-s!16cuZ0 zkB&M!4Gcd2TvfHP@%{UW30vE*zLJxxsrlRAc6Lyx%aEHl^`S#oE*Hcq} z_~EC#T8Dw{JnE#m(*RK0p7{ zPc1EX?|$=*z5VR$KmKw5KGY#pRJyu;{dHtSPmhaBQnIn}_;_{I+M0`tkFUJ^`1t+% zh=@P``TTi0{rBJh_P6BZZ@)czmPma6{`cQAGA>-;;_~s?-~a8m++5H_nwu{z{q|c$ z#r5l4T-w_6^T)?yW2&lLT=Me6!^g+FyN-^3{{=T>Or=JQ678f5MzkC@Q zdgjcx-%_b>-W(p{aOci_{WXE`;>FvyRO1D=Y2o5Ff0p z%FCe^9vZ5wY++GcytOqo6%{2VB`=?t`1mozU3vLWKb4fE(dJJY)WpoqK|fejM5l*` z2L%xbX=w~bY;0&~SXg{Kn+-m+prC*N&`Ia#pE9&)bX**Z1#U7>O#|zjlarc?!FYQI z20|USpnyvC_xJRSipt6Y=Wb}It7~vDxOMaMu~M+B zf2y1y5Ui|hZ7GzdlNKLuZEa>29bHq?)|Qs$>}+C!Mi&(|H8GiuYN%6lZ4#2Lhp_q_Q$G(Zj>R0fWiS z1w~kJu#;0@U}|bv8R(?I13{r=X0li$QczG}AZXw7^62!iFfT6@DkY_)BtIXAv#`+B z_3|n#ggk{#zjseo*4{ohcX+t55gaI5TG7#sji4jb)m2ji?Z&_WgJEbWEe&e9)z!8( zKR;z<8=In{#l`#gBO=t*&CEEQxw*l?$VdeR4UL3^hYyE`@puIVMMV;6bQIhIkaz;IA0PkWht5tTqYD=d4LdvEzaJa3wLN?G&K*!n&&~PxT)cSe z7Ly4&!ib2gR|N#9)cyT~gQO%;(VI78Vs>_3ykN5>C9hpWqnDRoya1im*I(P(_V@4Z z78WWhe)El`Wls-OH`LY7pEot_>-+J?2M=s*A&YNo`|-!wSt^=&&S8h8yUH<0QGB_?Sg~BjhCAXIjNV|$jGy2b#-QDVq$J?pf2m^u(uZ$ zHa0FRTVL<#0e_s97N|b@`a(h^C6$yC5*8NL)|gCr`Lk!`QWAJ&LPNpJ!r_oeJ}25Y0wFgSVj#GBK-2s0o}iqZhzKVqUtb!nq5__;tE;y+ zm0DDkmq#RedHMQ+rZ7LBM1p8aAmro}6o4Yn*B6IlGQsm55)vFtB4uXgVu3#teC2Q@ z@$n%cK|y#tgOQn;kN{U06T|*be@9RdIM=hX3JMYu{QThVii#>Llat-uLAO{|*4&($ z>gZ@_7!p!bbN_y7s;(|50f&b_e3+Vo2*k&il=S?hB_;9kojd36zrOzS&kYUWC3bUL zSop_31_q3bzW&ot=PyGiNSdjE{$WGdA|}hV4!x+ z;oQE>%^esBHNn!-yLUxIAlHBTG&fgX{>l|k&++l6PxJF-WX_$lwH+CG`LeQ7PVUSZ zW8=}$-+mh$)Ybju6D6gN4#;jjJkFj4)&0*u@9u_%Ub-YAlAiwZo^{S0c zclVPgF#2HK47$U%Hn>j>jhdR*ucxPdePIU}@Qwm2A}R__i0$q5bqeKlFOY{1LBpY< zA|T-G+|)EP1MYJP30vFp^0~Q|mVf|NRSOFiYiz8qFFxMP478}dy%Q5fMb6H5?kFh* z29}l%479g17~t%8caM!NDgrHRWTc0OuP@|iIXU3B2@H&hVVyJ*$^WFq#pUN07E-D3 zlyNxF&VUls)AKZkOiuo|+X9RXG}^`G)Un27qEM&(iV8LxW`7h4va$4Z_&hW;E32+9 zCkIrxUS6~lM_o}79`EMn<3ptu6l7)LaNgcQK`AMPg_)UHtcOQX5RC?z90ud!;^&u~ zTw0o!hsV3S`}n{J%gckY3Yw$b-0bX_7+>Fj015?s&oMFn{%CYkQhq*%6CLg884!@1 zTvk?65*O#?<>rRN!TXz-7#Qf~6&abH4*MmXjy5_SDwKG9aBx@{xQW2)9UTqcqL>&O z4P13l)dUY79Dzm)4u))-#j2~z&qt$SFAD|(kOrrYs3;D%yL)^*IA~<_>8E$@R8;)> z>(i%EQI{|C^2WtIeR_11kigCT*=N4K>+3)Kke4SY2yQnR1MThLcGJ`Zx7*yDvGE`O zC@&AbwC!yN2QIFgH*0D@d*SQL#r4T22?@Xb_TfWR6xT^Zqu;+jK4vmOd*SH#>J{iM zZr$SIvas0RhCD|}2|RQQ3&+PxOOQ#4i-U*m(IYD>E-nFq($ZglU0-*0=HlYv$;$fa zr~Un)pfhK42TWRF~2dV1sI;H@(=`}3dW{&_*KmS)>xw<}l`1I*%6frmN>^wRG6`ha}_~T#?n5ZZvC4GG+bAEn& zJT4BLvC+{31Cx_1mXVQ+jHhQ))6!CHt)=DHUvqQ&_zVo}?+*@Qv6`AXIuQ|-m2GWV zS;4_}cD}wTDX`vww;sG}ELLVFm3r!DW-!17heo4N;33P*jEMpNBdpBI%fZ>{=!iyh zIJLF#7F%0;cz{~AzMev{v$L?kU`k4wn>n1IAkf0mXeA{d-|2(|crQQBx2Y$MLV^7O zV2yL?;5>CL!`lY6_)};8$C;YRjE#jFePSZ4cd*!i0C3tD7iVYVa4?3c)Z&vyp?G<@ zxDpHh8TwGf!RKQBkngOHDq(cmzH*RGBT8vB_yJwmzNI?U@pIL!`F9obY}?Bi6ta#owWFPVPRh0 zkPy&~!;W#6F9!t7&H|SxCH3vMZf>))KmOR-s-tu1lC|~d=r6wv51X0-k{unt|NiKa zr{~$TA|h<|ix-a`q0tvET)$4C?Cfl8L`QRTU%m`u_vK4A`}XZ?*Fr+p*WbM>E{3@4 z@4vC}kAHyw>_7fPM`v*GAODz~G%@+ZA4Elqi+}uaZOz;JFMqjmB_-wU+t;rX6Z!bQ z{T7()w{J^IK-J^t2Ro0!o@@dFu*=ocrwt7T1{W@9XcQJgK5TCfyFz7Vf+8_E7@SC; zgPNbGQZ+O{sW&&byv$_k>#L|lMGXzj&u3=BJ|F)6t*w)jSy@I#5)w{MH8nFcrKRTP zH*XpkobCjL!6+*m7%-W0b1=8uy{n~_kT89+yBmX{r6nch@88+EuuxQFZhrf=ogM6N z0~~>bgqc}I1-KbqTs}Q%?(X0)7#Z>Lk&w{VrqPCmy1Qdz&CDz;NTk}@y1Ik}H#hK) zga3<6c6D`jCKADg8yDx|a_RuDs{=Qey*PqXD~k0NVjG9Fdrw4tqJ^a1hI4V`(&S&BFK(565ET z;^=e+BPoeU1ch!w0_+eH69btc?DW89!#*eh0az^Lk92xe6fiHR`-MO>g#IZL6ciK{ zm7ZQu0DCmKxxtQ0Wo5a!IGmf?DGQaG3%e`<*Hl?qRu&uUe9~fLD=Mn1Q!du?c0|xdwb8$zI)f$2vrdWhq19=f1Q|ca=Lo; z?%nF@w{I60QK-`$_tw{^r^CVo1jNOGwrRU;o@WP0faeSFeVKY;0f+m7fpQGcT{NzZMi^FkZeqI3SUDc&=Q* zvwj*l_MfzVp3cC^yz~K9v(bAPz&1Hf*lTT+<=|iW@qQ-$YdEADJeXDYHD$j zMpIS2a|er^oZQ&R%{4TXJK2YgrHQ zYj0m(E-28|6%cTA?Cw4|fc=5MQP|)A!w<0EB`>e8Zet_t`{M6^^{TWqo4vmeH7_0> zVc~=XP)SBaT)QS9K%wmI!3tDZn2#?iYIF0&i_A=EX;2~V?7V+p_`leD%doi8wA(-L z^vpXwGi{NMlMp1q2^J(cf#B}$ZdFL(RZu_`6z-D3-QC@S6G#FiArP02bhkD`Mn)b#{`J>n^48YFhqwYAJm~AYz5V`uU7do$i4$&aYimFL*x9L}aqO6t z)!f{N50jI|#)l4JUHjw-IwZ%ADJYbcJ$f`d>+a6Q1(qjRf02=>z^9~aZmzFWsgjaV z=5KC-q$VbI?wp_B%*@tSUY>%&*|T?(X*W+1vB*sH&EhK7PEt zO{WVBqt{ScIz8Rl33ic>Phuj@#fS(pIW8_eeV_D`oLo?logEY7>l+wIR4evpA{iO! z>2YzO`^3j*W~QfOo`L74ayC?4qoN3c5uu~QiW!wy8Z9xA$%Lk#L<$Ro&mC1*tUlx8 zK~Y5=7G$G+*9Is-DI8J}=s_UJfHjtvmzGAQQYaA-v9SpWiJT1zJox&fqiHk-XQR4?ux5MK!13yl^L zfjLi75{m`RAV$B0gtWAM9!+vG#=f|?kdUx28V%nyE)I?a5NjeM{rphf#(KohPeTJ$ z{n^<^k5G3I7f0P;W8=n+v@|U(C8fy7@$u#5++1yKX=!v0wzq3*jg18a&}+DPv!lbo z0W6h*f|ZrNJ|7<`DP7%?lH0eZrbr}laYe=%u55Vt_3Nvv z@$muzIQk|hK|7R|K7QQ6VP@v_>y8d>ZLBU)ZJD07vpaiMN2jan{rj~w507KVE?&&f zfBt-Xn?gBv4&K())#c^L$P1h;Dhf``<;c6LA#0}p0(_1UwE3Up_jozdZk zj`!$M@Q(KOdVA5oK+hdKMORmJVsmot-CJFaijtCopLKI{ZH-EmmcDc;DG8KDCR0L! zkI&Ca5Vxo@Dl`G-lEiIUP8XD^8L`Jr@;tU|Xmjq=7)jtZQy&ZlQ3k!AifPnUP zkQp5uE?>5>DJmKoDl2nxQdC4GZfdHj$gOU};K)#Kx(rFQ@H=TuaTja|D2)i5Y|b8{UX zB$Bqaon1iz{O)A3zCIjllasBjfq|;3#>VOCBO@&>{{FJEP{%5t{w@S&mU z>9jOmT@@7y1uloOGD}M(C4c{+p@$ELhrPT61;xaCd}?ZXdWwqR%|g8iBs0u1h$?}P zkAnl53?B|w{e*Y4q9P+BFwn^g69?(X*X6iP`+bv2Xe=4NLX z6;)M*s<4BDprT4@YZF4M^4j0q_(Q6o8Tz9WBP$qNAZ}NJ){A0VxUn+RRMo zOKfe2h6tVq___W4sJ~iSfh5w=@$%)&jJrE8FWg7BZh^2WEDVz2>MG&Skd=l1Y<4y} zT2}V*<)ozL<)x+MWL3_FIW|GsmX)Pa@y;w3I>_*0%+E8KmoD+}xVWHaT2i8}&ckDC z3+7;Vx092MjDtg4+xB*Mx2r3<<5(#T4*K{=N}>xsGt=4W?~f5AEp2=p)JSx}8H};9 zzP`xFD_8XOQ&Xp>CngdTwY9ahXf$~1Ku=Ru_3^2#9UO!UPFdN_t+sYyp}igC1W**S zvU+=QJVr!#c(}WRi&j_&k9BxB*fsI-`~6N&PaHp}Fu1!rIz~pa*&sW?BNrH0P|(nj zmlqIVW8>?akpZFv&J6Sy8XLiWu(ApWK%Fcv&({~^u%e=lj^bi(Z+(4?DqUTbmBGP= zhM;nGb=B8XDCmWNdWq2nD{(5dsj0b{PPei$HI0as3;~AOgYRrLPHZ0Fo%eV!At~%E07Z5 zO9-H?!$mWhdy5@%;^ zZH!S96P1#L(VJw-o1W3D2R_wOpH$7*jQNskDZT?LP2*SIax&H+&L0yef{ZEHX95a zGI@O+wP){|fawIJ5 z!Gm}2^78ojKKsnw9n=@}8$bWt$Y^@{r=PB0x3)fULPMjz{?Q{)r?|L49pBj*7_hWF zazsk1pkQzB`gP0@1O*AcJe@8gA}Y#YJbCiwO;HgT6rP^*^DkdEHyapWoz>QcYl5Dh zprE~dR~KeB=tJu2HZ?tY0`EFMKUOQyGeMCfFQ1jQySuqbrGf$-60)?kwZ&qAknZ9# zHU_pL>MNF(Lqp)rnVX+Gr>EE4{N%~>w70j22&#UM9$miHadQU+EiK)<2SpASm%BSWbWKgVy6`}Of8W(*U~uM)iAjI|+qdK6#H^gn-rFPA zJu))+`Fnd%dU0`yh_G1D{9q42x3syrw};*lI%yjlPo7|JoH$``KRUX%S5cv?eDtWH zVSD?%d#K=?IH9ZC)`q_*LF9mb4DMkeA#m4;x;_ZpEY{t-w{FG72@3M_gSx%G4uug9 zkH7!y?DlqUuACe{28(P3)J&kw~Ev{!a^sQHwX+_^J8?%~15 zr=XCVyS+U->+jFYD#nvu6zr$RPkb{>6*VPGe(UUa0S0zJvx29l(;3 z?d{P~cX#j#;p-zfP?s)&hH~>JjRxi!m5Od)TH58y5)$Fz;FRa*D=VKr@8Z(ex3N)L zs;SAtV`^GkySP|ZW?+CZy0H=55=Td(HnX!cH5C*jEv=)2cbl6-CV`R?i-q%;Mw5|| zlq6_1Nl8!-1qF?bt*oS_$;+A9HxJr@9v$N~#xQd=X@9jN0y17|aW@L2flAqte!1{Vc zg@wiC%YJ@6J*%tb*!Qg?(NOYczeU`mXZR!BAt%vy|wl5FuntF2=w%-t2Z{<+nt@Iq%17z>+jtg z8}suM6H`|Q^Zx#QBpQGtHafbq)6`^XDI#KIR9d>Y*w*Ia0=j>4^3oFgnKCk`PU-7c zRIIO$jz&joX=!UyDE0O2?Qn0~+lPha=cC$<>rzY%(bw_z#?>@G9~~$%8J#2WPKt|Z zG#}1}9v2931a~_x{q3Qygq)yr~mjm*r)&Vb^h~X zK0X((gTDq|Xa6>Fi+Hz>w~4>l{=NJ6@BbzL^?t%p@b8Z&^!UVk?Z4ChbN}`6`?rbz z@h=|z>-Os&_;nBbx(9yU1HbNp|NA{aT#1P1{p;1_|K#?^cOxE+E7<<_uebN_!~Y*& z7q|AWj2~~|-haKS@B8bCdCUGB2J;zI`eS0K)I<)WFFrmd1~vbrq~v584b&iFW<{k& zL?A&SC55OMfV7TEd`wJ8NO*W+VrFJ)YIJmPFmw^w+30%gcP61o1x+d=14_EUKj70 z4(t!Y1x7Ib!S%=Lc4z12&6pTbQAtUtY|)hw5a8nS^_`hP4GoDIZf+A3ckblpBT>M? zVQLE9J~K0{g?f5kyciopVw$dQef{Idv$Njbe0=iqnVGk4&CP{`h=@o_$H#;J49^34 zar5(QYb=(Ml7vJ^$n^B>+a)FX`m(aY!IP8l#~Bz13)|VXw{LB=v{+dQ3hL{Zm94I} zw>vor3tzojQgZX=$cV2mDsx~@uB_~nJ6BhcTXy-fv~*MyR_I7fJA2l_p|^Kqqp(m} znIMRwRfPjt;lIs>10|KzpWwW0=xp9MV88Mk)`9?+YbJDq@ zqSn?nHc$gNcMg4p-QB!ASy^6QB-cH9gnTIx5g(tKnI})MV&vhmwgy8M`~J)s9i5t* z?d{G^OG}V0!8sZk`Ittuyc`jMjJfRWt*w=nm>4N3d3j_|ZERpI#Lo}!$^3jm!o`bx ze7?RD6B`?8X_AtsPPw?u&c1#P7a=dNrRBiDt5@UWPEO~~>*+z`J~xNIgMva%&eqoT z>twQ^ASf@(%OKy2h=9gvc-SXsG$zpzk2ftwo%a4%j! z|M&IRdU|af{%S{ufx+?P@GL%mK0a<^bLH|6L5;~#Bpv92yD5fS;rAI!}c7Cw9!8q(H2bjZM9eEh?Qxj8Te2_?$% zvaRixUqW5}?YC=dE-nWTii$#Iv%c=+^vNg3k3-k>=1pAO;lqax#m2sR_5OWU*12!L`5CrY!u4Y7RahxT*r@xgrF)BAAk1j z;luv^w{OG0h&oe1!1gwjW6H{3e+?(n&p!_jU%B#!Kd7qq^!)ha{Jf>*XP=3SmzF+% zzOdlzeCUv{a9-Yv7h79o^1ep-@nf(~g@oY2+}%a(MnC|09{2C(Wa#P1$$5Guqhp#A|Qg*l22Uc2-sfC+zOs)>cc)3l|Iw>gslO zMn+s*kQwTB zN$K~$S5j(heDVa&&Lc;zl($Y6?c6MT7goKd3_weDJJ8^MBLSkY` zNq6twxr4D4Bgo3i_I65&nAq8~zP_`wTU%*q&{Mg%p!NqRBrh+#qfnZen1ISPHun5^ zdpkHx78b}vYHd|h`{N%~RnZ^4e%;LM_rDhrDKG#2`>icc&(A*N=FZA`|NiaU;dCb9SDbM70Zyb);@S zf8O73WyQ;DU;y_g;UvCvDJu))Ds)LMUrtShV%^X0!UYM5goM>qs4X-!l$2s)kvf%| zYhrTsDttxL)0vrY5`>1fwhj!iSY~G0+94sRh0y6p#fXmX>KYheF!c4+)JUYZHqhkk z?DX}+!-+1am6fJuaByqupV7?L)!(d#yR$rf!1Mh{seQGN7lT@myDV!UPjnFAOIKa74QPJCr zzK5}~msddnn54nMW@gULELLM9k~kb3TwUq(vNGt3TwT4q=yc>3MMSu`czdJb1MMX= z5zu;;mPSWIhs0pi*Voq4>5h)*hg`c>SC^LNl z##E|1XG6!btc<~cGmb<8>l2+HPfsM=Ku?ckn$S=r+#pq!!r7oMKz}qknnD4S2wlFo zIOr-cCn9Pmm^DFXKqPuFIGiT%5hEg?KuAkVN{WsqGz-vk(CJZ}+=I9{%$4HfF;~KE zq<}kAE5&3&yOxpy+HhoKNl8OPem;q0Z;ySB zlqV9&*4D!V6voohurPCT@K@{VT3XW6Jv|*9qM|A)kT>V+iw;?3WqUg)l7@z^uE@+o zex8|`n;Q~I3JL-P&CT82v$8rn>+54;Y&Z<{q@;$1+FEE{y}i@ZTUx5CDHOB+o&VO{ z935?7psybl1pg9{``!+rO*s%i#?Cm!63l}|oVQNgu&ZOzE&zyTqlhK8SihN}CMPk4FR><=FvJn;1V&2J7L zX0d+!@!h+G1dx;I^fzw^m)^mHv9Uk=@XIgx`A3f)IACYDz5U@sWhEcqfdgh{>+3)N z+|okK1m@>|`Q`d`P0a%bQktB&1)Zq=<_0@B|01tnBU<6euZ)iiUI27@#+~az#)OnW|5o)Yt3k zBE=5c+saBkJ%U6JzN>)&h zP{6^@&dUo6v$1h?O-*fXuBnNPG&MywPF_ zST?(|61U+WDCQ^wl9Ro>z#GlUVYA`)LSh!OMG_Nn#=xIbQj(lZWQgSc3zHiumRVUy zdkYLC64jt=Lf0Sb9Gu6{ToGAR$f{0GPE5pz0frk!4`fb7MiMI_^ie4kDm9t2Va(XS zjTIFt(!{HvriyhI)KRGXqM8gn7nDuO$q5NjQ4~sSEQ7&fB_<*rh(cj7va<>87>N`g zk5m<+dkY3DYUIRdMy%aXPsZ91Yq+OyHe_q~=8`IO#(aOpoJuWQF z&L(nbx0;#>2@?~OlZlBM8kaAV$rush<25wk2**_`E>2DD^5xLb!9i#NOia|&!oo&J zH#W-4alM4f5$Ca$m4pNoz{|@`O^%L|l14^FMbp#u^_V^C=;Y-sF81{yVOCR<;D=JF znwka%Sy@;=&}d3Z3JNhX$nnX_($tiY@bEyYVQ#LWp^OX|-ZL|$rRL_+(#UgNS%HQ@ zP0iZ++O_57p&<%IU0q+F#hRI!no3I2)P%REzJ6dJJ>Ad{36$mKb91e&o}Pk&JUkW_ z(EAJyW@kge5*P@A9x{E1FI2C zVPRaHkB_e}bPmY#^YcT(PhMVWDH3vAU6IjRQj(e)80g|cCTC|?Re{9o;NaxcJ7(%Zh{G#~)^rde6=>x72FMZWJo_4Fjj zfyl`6@d*gPGqSSc;-Gd$u2Fh=WTcNzP*8GmNeP@n?(S}GF)^qJuvqX(goNbgR#rl- zWM_wzkH*HbvWN&PD;pcAvoQv^yTdnCRaI9yugpL3?$qEW7DbVsUnd<6rtw5JvSO`LQU|?_W@^V=j(Tl6Ao1d?$ zLO%>+7gB?exD0*a%F4tz z*43@7)YVy8iHaHoFgkB>)2s;cViBLjJ1fks36 zFnpwQb9DOU%TiLIp*Z_kEEN@Taex1jk()P5OLcX{#N6C^d*NfpTF2SBw|959s|#79 zy1Eq=;0B{UpsbvkxwO>R2W^m)6q5;BDV2KhqKF9A&3EpUmJ<5mk&(?!}JdkQuUY?#F9PH|f`P{W@7?}w^WkCUQ zHNCw3r$coH4eq{b7Na?p ziWEc|Ej2YW6RC2bQ6QfY*QdxxsMQh@&?m*`%FBz7cXZU#b9XNAvIM?OHPhV zo}PaE7)}i&x>Qy^e7L+E7A7n#DTx&hRIDJh1O(vNMWQV?w~Nc*Ab2o(dSYT89)pA1 z+ch;NCeqSS6})=Y)~2nEnp|)1n>U1tLs7A!;^D)I2|K%^N2R6VdRbof@;Z7{Kp;K+ z>C>H^xVQ@!(47W}2Am-wq2%NzPo6(dNde*2$7gkQZ;!<~fBrCs_6ogDe*XFMzyH0B z&Fbo3{?gH*qH^Sjtu1O0Lql3x2M-z>!^t)=p{4b^-zh5g_kZ{>HKoo;jJt55xfvch zef5`Jv|Q{;^#*K)BXEYs-U2VNOJP-?%p1gDJ;y(OQGDm_wHR~ zrIgg4{scYI4?oPzfQ0bJKcdRGv(woLPPVgiVj_-TtSNBBV!T8>G&8fjJU17*49{UwznrITUi+zlF3z7ot?;#)z|m(Dk?4lW7ZQFhpQlJ&IBWtMx#(LUO=OX z+i=4Wy0nlGB8!{JB$1$WD=e(8Mp^{4V%M&%u1-zG#K_8uiGd!nu#lGr$Ahsk)Rvu{ zZf@e@nwsG93=czjXl$IFJv%!v5EiDSq@$CaedEUBVtl-U!lg@*kyBHPizz9JisIry zLF41=>&R5+<%QdQZ?CDzz~J0DLqq7Vhlerm)zho2ymxPEDj)!4?3|p9jhUISu!|RE zWMX1QM*8}KgC!*7+we|^h~(tFdGqX9eEg|XUw=*L z(Q!U8+-L0+t)mQ53t*tL!3=bO{gSS#yNmPxwxFjX>^1!f8NRW`YbSWj} z&K)>OBqfD}LPF-|Zr%iy9641jEpv1E`G$tl($3Cxbx6^6bkxxB@IX#XZEZk+iHVO7 zn>{c9ZK;KYuWw;tM+fK_`ueEQAmPB@AIkK|$fl;=-rQV!d+2ywU2}4fv3l(qTwb=E zoYS;4_-d1qkV`}mf50g4@(K^n$S5pKNx|8J1P`Q-5^JX1T*65W)(|pJkwoO?2E{e5 zVEebrIS%r`K#(`m)2piT^T}j8J1;L5tFErRJSN7;$;Aa@81c@aBfz_xQe2>;pGLbI@I0BY$KfOsEs%}5*f(3xq*SUw#bZ!vpXcj%F4whIk~2$yc|pi zclY>scXX+}y0K!@|nSnwp?mx3UTgtEhl}*U{0^GAat>l%ODaPV#d?!VL}&9T}uY zj+mGX4!(LdFkoQt#TT-&wY5L|aPMAN7zq1?WW zv_pRWufB40oSAv>prAlP;_zX6`-KG%1XWdW^ubX#HDzfDf?#JS;jTMzLPqA=HMpk8 zWG*g#{@B!ee)@N*d;exSoRn^*BON+g|n3#b>Pyx^c9xC&BJ?d%K;f`e;n`Z&3PL~XUI3aLw8Uf_Zi7S7Igb&*IaDn>>* zISUJ8W08@_lt9u8(p_X^goOP3hK81xv$9lFE?j`NadWe##?Vku(Av7A1N{g$Hz_Gg z%f`mLcLxSsT}4GvjRzag)03N9S~@*_YioW!D2SW;!UZtrp`HY1F(hPVWoIWZ57`7{ z^3oDfs}d0j2tenpx>{3{kI%sY?}q9%dVb^M;QlEoefF7_R$t%u-{S~C5>{K=4?ip| z+1r2h6+wX8+Y1c*>Z>znSgf~ikw$s=@E`x^>wEk5)2C@^XV3or_jY#hUDnlIzI^0} zmDTk04?heInVOzC1G?R}-_FdqxbX67XjE1{dbF^B1P3Lhv^1g)E+{B1jXa#iMI?cq zJLly!Ir->Od%L;$sZ+wjBvNPR+B!J6w6vuqGt<#gSJ%e}w2q1j=+>>QkQ9aES6kcMJSC;4 zr@1*Y(#Xi#IyDv9afIWAM(gP4>PkwourM;h_>9bJTU$N7fB-hTqobg}$H&~<+Z%bi z1qC4?j*h;*OlE0mP7dh%SQ8f(=H`NU0Qw8`kSQr(u_1j2!~hm6B*fO%+&nOl&7Pbb z8A(rv(jK*wrKRR(FE2$!GqaKsBpK1^8XD^AR4Ov*Q&KcEupR}6Bt2bCO;pswqo-$a zF)QoxWo~YJd(3CRN<4bh*m!*Wr=QTR{OT)^#D4zy$rCd9@Zqy(S*-8Ad-pCS1)0!Z zUK<mNL* zpioh6X)>cvy_yC|NZF2HC zE?(r~a&sFU1&IYZaP)Te_L`ebOaugw6SBA0-w*eirDbz76v6v_IMi)OB*J?#GSblj zI@gseiHXC*eSOi<$l#<>2M0$+Qd7-1c{Ru?8yLvQaOC8H!^hFunwUuVI4U?>M1+x% zv2k>CZEa0WM1-NCscBNuz`)=jgMsb|d|Z>085yRgSQ8Ho&CloM85$z*s<|1pHxm;H z2~?Hl=PN6*kF>P%^A{I;djkV6Up6)_Dq2}VPf|_}j?$eSsOv9Xl9k1k1{B0gm-zX; zy`f}4r<0f0%BsB`vqW?T_4Fz#Zr=uV>EnnU7N((rk#By!w>KzAR#r`oP9GZT>;$D! zS$RL9MNSUYFwA;Eyh4K4^fa>VQ56UbghQaVmgsKQ)!n$!+Un#aAz@@xTzvg{W22oN z5<)XFmY2uILPMpcFJGq9=jWG}SS&3qRn@pSc$F(EkcWpou&{uXFC`^g+v4KU(dueX zPZbq2vy6=X{)UFYKy`H^qqMYX4m*p%(9)8Z4+uYMdb!(iHm<7HV znOsr=5*VmAnDM|7N};&AdV41)mzQ6=78mF2?Bo;|S6SK6fXrHgJcblmA0J|#H8O(t zS5VN^WwEBF#>QwgH8n-W@bKYbR5^8ZB_-Y6J3BWv8X9bDKtU}lTVF>GlcXfkFP@xC z;H0;zsYOQi^^J|Cr5PEisUbTL6)6u76B9o_)U!aZa&d`^D=)9AijQ}40(-5ov92yA z#>~vrluB)D>*``KP+tuVZEWoC&&sm4*3`uKgB)EC4_#dsm)zX8Hl$l2{Wd=zo+OMW zetsn-U0tQ6etrf9cua3^LqkM_v9W~(jn>#moG&enLJ0@}w-JtB zxN)KHVK7j=M$H4+^|`ruc{G}{Gm_{S45XZObolvQzO1X8p1!iOzW&iwOAtKy`E_*4 z%5L2n7;tyLZ~>`mAfv&xASH#|w)J&fMMOp8}(JuPN=q!SjgQ`^qzT(%{KN2LneVDu?LZ5^6JaXX4{QRin}1L8nqPGf|Br zlLG^zqcbvcbCZ+9!r)Ec?~)<|2lWx+s){;DULIVfzP<#jDkjF+8NKV;+PXR#&C=4u zBqpY_6W0JWHC|p5lh#(uu#p-Bzfo7$+FE(Jp&=@(`T3YhhJ_&&GCO;86g>+wGj!$$ z2a&>NY;0mer}y`dj%H=q+Un|J4$$13nu_^=uP+>isi`(LSFd_`UAxxbUsmStZ)WE2 zUs*XeR#z7gpsVZTR9XscN_4aaXN!#N?CkGPPS(>yHYao^NS9Vtc5o;u85yaoBa=-` z+}!f>dwObWLPISqyuERMV`FfzhK9a=LPB?UR~L<@r>CLe?|kX1loPaB}6Enr3IKsxaPLS>@#oaHzcA-WnP)Si;J6^ za33L08l7*Xf^%`%+71ldzmL8&AD^{#fB&;*gM;?=LPBO{aDh!txx1e~ub}~d6H>R& zpGSJ-&6{&`=z_y>w6?ao3Uv;gY*=OBJAlP}{W>zfk%#Bw1GQRXBQgs0^_!aR-feF; zGdp)qTf3$PoD+NpHMO!bP`5!Zg5vJpJtS>{TgPH;Z{NC=kRT=|CU#dXrY7po*RT8e@$t#XfD|-4 z8ytN8yqFlW*RXmO61s2!{y40Zl$1n7@OJ>q^2!w*&OdKVbc3j}U^YTVUZf!k%T2LS(bM~ya_tMhKmtYRUKRr8(8v2zhsFJm|BB#{I=OOC;a>{4=*VZ z6Z`ygb93a5R#slR^rt`R>JAKmbB|h;hDLYypZ~nF;_7<(w2De&7a#b9FMn{T?iO-y)r%*=Xv zo;~aDH#djt?%FkEsv=7c$NjBaQ&Tv8q@|fm>Cd#4sP8odp#f{xf7hwzT;8V9j1sG&k4VOQGoKU=@bE;>1KGbtfkF z_V)A;iYqwL+S>N3(v_9X%}l1PEs}4mt3fRC_BJtrcB#7?<2)F-=s;#?gAouEggi7R z6Ran&;;O5Qico{VIGB=BT8h3(SQt`L(U}bmrBFbH%gm(Ff`bDCqoP=>>};&mLO5Dz z=%t~ZhPwmw3hffB*BdKfca? zevSRt+kf2t&;RMw{{46R@4Np$e!KrF|NcMmy#2@F|9^j_|M}DZ_mThpdc;rv^I8A- z=a0|)&%gim-e32?uY2IvJ@D%u_;nBbzqAMb)Aj!Uh0llU>HnY049Ttk(^>YhMnJgC z=p4?{{+x(-B~%5#j)U3-DoOMXu=8q2h!ie?J=~C56F2jf6shVmvh!sy^%iFg#s2bYKRfvlCsg z&`>=+3yZY03626XJsnkI3Zm<#N}mQ-xDW< zgfcRoJ=@!(Qh7KVm5LQfZtlg4+}va`642Oe8JVxYwzZv@c>1)iPFeZOFRxr_Xt;N; zwbj7j#0fAo;NL^f2`rN*PuAAL!^Opwl(Mt$-X%DUQd05pM5S9!PC&rNXJ}|;B_~H+ z9sK%^4%EPObcBWN?c3WoH{05ro#B~iY`l51v(w#OPR_=rz8?M_FE1#NGcr(FK&AW2 z6;P)7`$?orm(z}x%MB_j1>c-Y71;zcd3;$kpP$Yfz* zX=yrrX=!dQB0^LYOs~a7tcyfNd3l3^78m#Su3f`-gHI8TM;#pj0eAPQsprqz+Vu2J zo&?Y5{{8N53k#57s;Z!hcW^j+7QB-uPgYk+qze~hWC%_RdL}Y548|sB1I^~bg^-Yi z1u$E%T8FOd#*OT3d3kPbC#T_ILS@3oXJ-fI4_3Ou!j_huosS<64?8-ZKMzMCJXanb zyu9-A+1a3pVbzP&DpVhWgL!#DRN36z-exjoWG-HejosJ)|7V}zjI=s=`O~LC4!(cC zvQk6i%o$5d=o1?nu3p6|zNhD#Z^p)KZO@$1&_Lqe_3J)9{QRH`KYqNr8WhCCBQ6dt z1rpwQc+Q;*4PE4LfZ<<63gYte?k?P%=gxV1FD`!bO>;A}=`JpllP_O(b{ZOTbDNp< z^}TvEI%;iw>XeE~IVZPlbQIoES=s#jot@cPFR#<5MMP3lcXzk9QQr_3Pe|C>x_6Jo zl9m=0j*bQ)rl3Gt`s7JR$MNyq-Lf(jmD8tD8+h=5_zqyBzkE3|Vr>mwPJKN*i(X#* z{KV0{yc`g~%`GktU)K6Mg@SV?JbY&?+s9{d@#V|LMjf3C z7u?;Ef(V}?H@CTYe?KTUmX@batEp91?(U9^IB*gKk%G9pi+2MxH9Z|HAk;TdiP+wT zvR_hCL?k9=YwPjj+*}C>Bs+|c@9bQ=rlfTGw2jT+AUc1jrkI-c^${GMvu8n-e9mED zd3y5mtE*R3JmFAo{QWVSAPM2djj*tD=XiM|BEY4Mjz(@-aPaan{_4`wpr$S_zj%SP zIspNAv|hdH=+M{a=7!q~Dex8+r%!8WR98bcWoJhu2=4CA%y@X5K8>{d2M@Nk&?S(N zNKQsog2|MULh2jHuUT232*VkC?_P1Sy!^?N=riu_)^J?HCr_H2LwhkeXl{P`w6=Cz z+w0fYuY=>IrdCt)^5x2kukV>Nl9D+&4FwM0b{!oqE(?p{;kR!`M$FBR9aC1Ws(Sbktm%^{Wo1iB9y|aw z?C4Puk?iajFYe!ujpgST5@IkOayAA7RV^Cr!GjksGBWu2zx>kKd43+}22v2Mt-%`8_o0@WVK7Rb-#hjc657yR#f=->{=TAx^KDV$ibR|1G&z|MvT)fD`6B@d` z{qki&fvD)ALrzY}9W5=Dkoe*Yb8~Q|8XHwq4j$Cg>+5^-W^@!sEAlR1y;@lb2mlYg zuyA*GZH+=fI$%P=ty{NmV~pbC3k;n3r$bv*^!RanR&c!b;wBq8DlK6NZA8&8CR|*OUt$#uSQs!{m&kx-TWQ7F&GaZ3Q zhzblOlarId_J!K`-+Q1jG1soaHRJB?*_2mFJ82@g~GC@$Hhfl z+{mc98cCqu-pH+GGMAS@%oG&7c#%q7TwGX)ih_=ZL|RX-yc^Uq{s5{ zSy}bA%JnHL{$!%@3v-o`IP=f1QRpsjo_HAZnYimJ)i;ISagF{i#=xBYtudj-V zsVS0jhK6Edpl3`@9_MW6a9zEM9KXfI+*|_#C+3BogrcFmd~0iJDl}9^MpZQ>Wqy8qJTz29L|7P*|t|J8bknJvOGL&Y{+Ef20)JqhA)WVF)`r!r*hIB_fs8NEch6r zqrJTY0-!C=%8HEKR}T@l0|S$ji;AFr#QpF+Wd~q2WWHOwmX=%m9L=TwAwgR0uARsgp99?3q6&011mYoeB!@h5b z!3Ylr#T@638#u-OR-&dwk8l~7xGdKMS=^;K3vKkn$5lQS^T z)&@_rrDbAbdwXjuI+muUv9V1}EiLi!=H|x6QBe&IO-*rehK2?PRBCs3XD75Ll9F+8 z_wN1muf3d9wZn&z2=M;>-MgWoNQX{J+S_~jG%rs^1|5yAuGQ7zVr^|9A$$AQ*0r_j zYV;n=%&Mwx-01H1^+m@LNpa8xsHoW3R94=&fqvq}i)w1nx=c(U_4eXLXvN_t16AA0 zYitZ!7kzysN@DcFxp?IYoJr_tfLCs7TU0bY-qaKj09{U2*3eL6qrbnZDs(T%ETT}< z)WE?V92_2IFpP~23~020f$?!-r5G96-#<53P+(#Lmuy+t*jPn{o13btwRKKTPfuBy zmzSm{&c4aXj*ie!EiGeXoW~<0DJd2f#>TO+ot;BN+1dMvd>E%)U5$->e2_SU4!Ewa zpWn4>Z~(iw=<7p+GBMHA6cS=&MMBj2L?7bySl)-wzQ0kV{oTfxBr0mRjtMhgf)eo%gXX(^Kl&P;G{N=i{t zR#sFLXz}o#WoE+P86FNsO27Wg4&mX2g;iB7 z7V=-o971FKq#aEiOS`uZYK1lb<$?&0D2 z`D}Jgq| zr)Ov9!a{Mesi}ejGKz8L+uMV%(b;+92FM$dk`@+ljP&$)dcx1aWKK=tI(6lWwsu0o zzyPjrX(drM8GW!eJtFbAd`d16v1&HCnRvfA5v*F)VJV;f$IxCOk|+J=L4+^k;;HP zGFN4ZznSpI{1mGeSM^eVm4P*kj~g~r0p?#K~w@w~i8k2W^K!p@(UlFG<<@ZioJ)EWc@>GTH=o!pkc%3odw6aoAaCEGwK#I1f=ann=_JxI`qphtG5vHb2PDmSTZN(KC z#F6gq;o+PduxV9Q0|H7*ySiFik?`liap;psELKHDc{#Lh_V$5+nVImC1P9yLczCc_ zO-)cAA=5Q75^IvgL}aDK#FUrU)-o6_E`&aNF-BJgywopr5lPCxhZcA~iM+4dv%MIB06Rxa8+| zb(NI`1(}+WgVWPN zDrv3e|Ur$Vcv3>PwOAE+WUS20pBHiShZ|>iZjy}uDj|E9$Yb!C4$d6rG z+TG2`k&r-jVrJ&$%eFQnBSAqYr@_Go4_aF-EX2f|ocjBBccJQmXRoPgd%L;W*!b*O zkjNiA=VzgH{QOo{y}eJLLUn~Srn3l$`xcnAukk4*NlwEkKuFW=jY-gkv29^dlMJ` z`fDer+1Y2$s;U$eK$spL{_eY}DXazvFW>aEkB^9mmR4EW?(Y0NR-FXr?E3ZCSScwu z1mSxCn@UbDG!#4r77NKBVPOLUi;E>CNP3Nk7#v($DlG+7+TFdeabcmM!OIJCPaN%e zd00c*+d~&vQQ_nyD{E+o_1C~aY^=V%scCBJ$jH!8Qj)Q;pJJ37L{O-!txXcv=69OLF_=+C^Z#VDqIx_&o{2BaHxTRfGj04nL(EeCRWdXE z{Os*Xr2PEa+U)Fr0C?RpGiz%L3&X;kodW{0vg+yz3rQqPOBWX=6OO3pXd4?lySTXO z>iT*jr6oKZiI^;wj}Is)`T12Wvol=Sx>9NgS&YI1Q=R(5i#s+yl~Zzq%W^qicaB}HC@zCO|) zT3fGO^Y+%#va({a+S;I?R#JlgdU6u(X*oF!4JLDGX>Bb#TVG#Q6)8CD>*eKERye*J z8s_Gzt6f}BYk=Q}%?78y#s+EL4GrGjii(DY>FKCo#KsyM+t@&t+tCpf1)o`FCei?7 zV${`@l}V(AhSt`&I9=VVS0f`kI(mEK<25yvm2qax&leV&o2#k@2lw~ixB*I$k`k&) zSnHtsV`*7ii^_quH6NdvT0z13dVjx@6TF}q8N0iv#-s8Z6}7#MZW~znzP?0_A60h~ zlb)XE&xeL=Y|frlQ!6dKeH&vAs_twy>iT|uJUo(;EY_Vnw{O$wmpB^~h8r7+i5L;6 zRH7y>Dth=Z>frb8vDr#WCr?^g4GunhSYNNEcKEQK-oU{7_j7Z$wnvXDDmFI$_~YHX zfq^Gaii#E#eEL>-_H9XtsOV>(As6(~qna8uwPVK!?q*w?xjB3_ z7#{})Y;5@Xjg7mz-@RL2_Vnc9lHsH_V8#>@a^?&#Z*=s{n|JP{rbn-_Nh_TEW@k<6T@F9iyX5ODikE zG=fh6w;2pKH(T4VFgCljwXo3N-@*cp{Q7z}8wt!_UMVTHwNUWea<-@_q=2WSczU8f zo10rxlb077>g??8O{b%J93Kx2eL@0Kkf3FZj!sP_*4wD+Fc|syL^_?X?>^lEo?$W> zzdE zk&|m`di{EA49;()7k>Z!!h)sc?|&~OR9X4+&o5p?L>xQD!^2{|e*OG8jmE`w=1fAu z%a`ANn~}lI{rTr^ZpcE<$q^PlbjZ;WnQ<8zNDcJzx^?UQ`-TQ|JX~BhHvazi?r!u^ ztgJv1o1E0p0Nbml=kI@CS^}LyLZYq?{!erBPd*V4sILC`XOP5x_q$W4^74NA>D{}S zn1ct89%V3o{P8b;NlyOitKa=DIQZ48Uw$brK7Rbb0dw@-$B`pQXNU7WDvFm^R5U9K-#;mdhv&?hxVY!ffB2!WP*CvDA$Rv1H{QOj ztd!xT52Ks&@+Hz6zxYC5e`x64yO9wyGjw*4U4wKSsJh$R-@RK}f~y~0wI@%uw!pPK ze?B^TWd&MsB)5cxt*-9uAgu-S)8XN}cTv|zJz{Y1)~$*PLqqiIG2#pifbOfVo}CRU zl#h?7sJwha!u0g`IFhg=C4+;ZGhnlAZPnDgy&D>?b9`;cS@!ZOE1Q@=9-N_}nHiPZ z(9qbJoD8Qp5--3bbauwsSzL_12;p4M$!TaPFGtD+GR@fR;{V0od&Wg|?`z*?pS{n{ zPGVxBMiDD0AOg}sLAvzbVJJf{L+>4i0fsvC89EHT3kXP;j?$4PAOdz{)FdWmC)xY> z%(d6ylkEF>o>%97alg3NtBDzgnPIK}`u~2v>-t`WF)^SPMn~gpM`ep@2K*#+gRHGV z1#D=@$Uq;|)3dP=J5Fb3kP0xReeq&q0*SbATy%7_wV@MgW)>gc)Kp&|8EIr>W`@K% zcusV5)YU^n+uLv7&dr5`3@okNw+jnxY?PH99og)Wk*X?ZXC);oD;Dea?WQKU?Tm~v zGWz>(+z1Z7eqCQbC1q%+uMeCoWo3}UMn;gng;%Pn0ilvaQdf6(FDt{!>E@=dk6BM^ zE8(Yebd>9dj=HyPF#v$T>MgpN7FejYc9#N@{AdSV&!}s!~!qaRRjt zx=pUGJUnu8;M=UNVg0#uiAvqzq{(Dth>D&*?cxIRb5W6!5-+cvU3d4&3er~j_`p0x zYO;i0gELkcj}a-WoPHs zRzm~oFFn1gDk35C;zjuSHa5n_0s`QBWH6SNkP{~_4?5Mv#O!Q(y0WsEn1=^JTtP?O z!lJ5bYO1o*!U7b5f`XZuo}Qo}B_%lOCMMwD)X~w?qtnqjPfOF#xOz1(aAX7&U`@?~ z2QOSe@*V!m-d;%wo~ov%($cUnCnwNriiSJ>BLXCxp{jd6+SPIm-vfcJOS70EEbjO?(Xc2glZ6hkcx<2Jmx(X7It4dHE^cT@ z%qDz&RaCUKL~c?$lHZ3qwQrr2G1^vTSTjOfaXIn8?pJHNAGt z)fEIiHrt+)?1}ZJx3{d!(NRgs*0!u{bhM_1OqP;@e|TFJS?=x7B6 z6_td9si`}6=yU}IadA&iOfHj>R8?hUe0(}P$Hy6r>(|A_Jv?sRx_1xf7)bJ{I6y!X z5wWvFr}*YgOG_Reb@lS{jg5f;XO4pk8ncCk-d<31q&eK*&CS`_pdgUqQd1vsj?`2U z5!_w2Ih0SR*q}z3o<`0zp=-E#^Vu_~5sn^JQ2`?!$->8v!_)r!Ig}H8d{?hBnMf86 z3lkKSluS>5@L+8X^sDpdkubKfK%}I3drwZTt)Ux#<_z-HpFBZMlYoGW%h=e{r;Uxe zy8Qf5xx9SY(E-f{vd#AP+S_$?j~r1^X>5G)VsOyF;6MLUP_VG@&6|x4e}676KEBk{ zXV3QbKot=ZN>BfmbEKx8JNNb1K|yMJ=p&~#Q;k((_nTvqn;&wG1*eg_X8IT97MyZi8ASlFRMpL{~0 ztgkkQudmO|ZDKMs^zPl@AlT9>D$r~U4VjvL z`K7cplxrg+78VB&3JVt(zkR#8iO;~#&tQD>&7()Lv2Z1&q`Z0a{rBnVr%!+WIfb&d z_3mA9F+cxb{$g%EGxP3UeZ7RlU;d)2J30B!e+~>NDjql>Bh%ge;lus=c6Oh9A|zB< z`P*-Md%nJ3eZ|Y0o&CcP&!0y`eEH>}LqS0g9>7iY_1Ay?A!r&YlIaU~LV&41)8q zvH}jaloYaor>ED~b90rIB_%^cr>9Z5z=7xN+}pdlT2`i|g}lBSHx?Jm%aM%-%K82K z;Cl1%>FQQjZ*2_@LP4mdRa^|NH>&IF*K>21md3_{gW<-Dj~^c&8VU}+d|6B^ENor{IRqY5I}y{y?b3<&dwqts;UeI@~Yk4 z#l+xd9~&DSjET8^T}um0gx+2_tKj$?9`5apiP6x|)S`-1 zWSD_W(9&XSyWhK>nnE(1l2UH&_V)eziHYLkmoLMk_WXHap^OYSx1AldrHzf6oRnp4 z?fQE7G0n|+ctE$?-0bT^PP?LFVd3`n%uGlKK3iVi_BOI11O+i`x_57TJ3Cui`pg;h z8<&=tOwXa1G%B$!Gr#OcXw2oZ1(#4zyLh|nwkX#tE=PVxT8u)#Keq` zkBz}0iL^RsIq3AOR|N%;mx^`C(o#Xe#|P_DeZ94{j11JP>+9|9_V$-A>*bG_BG759tyNWlM+pwW>MFPk5)wq^i@%(>xUzCW z!oWahCm3eR$}uth{kLz^>6)78Y;<qE(VG0bk6pIDkDUphrn~Ny}YF!TxI=#HSs3EPf62Il1^x=_!Tv5@`kd{U!V@A$m z-MCR!78q!5P9_r+s_1B9O2y_JNSGy)BO?n7%Qyr-@QKm)uc$~)c5$(`Mi;-Kfywms z1yL_K84RnqILw#9u!7#p%?CDE=(A99czF@~Y2q;$noLe+a&WM-v$uC@ zDj2abF-}g7j?vMm%JF$@Y%r0os>;sx^Ru&~P_WA?D2R%3b@lLovpzSMO7-(2h?vt9`a%2+{^7BEL@bZHG4yH#-}&az8&33FP>inv#;RFcOJOrqPJWii3l#EoceV)yc_F zwqeRrR|f_Pct(T2_&kzIvUN^*2*d(g3WGdNKZ$9B`~n4 z2syzNiiL%{JA+YOT~H7bVsG#6o}7$tCOjOPe;Tc%q^Kw^&dtrkBR;;EbHIP<;1Cf} zSXfa(P;a5ULw0dckh3#pp-g6GCh-?S^#gjOpI=xQc8yRDBH=nY8OjI{Qo#)>C;;cd z&5c3o4GxB*7z|`jPn`F;oP#*mL49R1vHE#< z5H$g=SAxn{SO}#Eg%T2ymX@CnzNM>cFemM~yc{(SnM_=9OeXkM&Yb)NqRVJyWnlqk zKe~+jgc}fHyu1tz9UQ>vudR)UFf+5VijT*6Or=^|o15b&>g$t|NF*a8fB%Y#j*fx? zFE4X*3MDVEv60EdRe-v^t*xTM-`~K%$|@?S35cdyopa~rene6Ke;&oP5KmaBp85x+T;MxM!q@)DB6<61N)hvzX>FMeU@^4WQ z=0R8$QRzSv1olm4W?^AU3OG1{feZ%tCg2*P7K9!ctQ1$*xVWOCl9Hq(P_e_pva@l1 z2L#~GhbuEXJ0^z6#!gE^Dg(I2aU4E3kuw435SXQyFYPNN;CG6Pi~pBa0(qgR-{2l1 zvO|ePPmZnXzjHN_E*cw4Hg88T+(XWTwK&w0};6>I2OM zGQ1fKBt`^t)XkBR$;r^|g@;3}40Q;UB1EReKKVEzf~X&fWK}3b@Vf~<5TPJ}9-7Fv zfWIj`JSHX~0rdK)sC~7_{%6A#z3&Ly|C~^f!1+%77a!(-b(3Lbz;^)+4Dk#ihZN68 z#k>D4;W>D`|15ap@O;99PW<%0|Av46>HSy5^NCme~3{@gHCBfA$!k{Qvz5zyI4G|Np-~{{MPU#54B4N8&sEUq8zq{~p9|CjKSz7;4Zl)H$Fpa* zZ{t<3Uk8U83>K(&==7zfyLaI>fPVzNuh>{YK~&-k3!rSu%U`_c?>{&9=uvSolx<#K z3k$DaRaITRdib!haZk^qNA>lps>hD$>vwcKdeqvgrFHC>mR4Kat5?v@VPaQV`Sj`Z zG?K-koqPBYUJ^k;F)>W(78heo9R;Ps$OyEQ z!NKpo8yd2(IC)Y-qqX(To2e zFDQu0Xm5|f5EJ9$3k$=fv7kUy^zdP8>)W^Y_TZ#Adep?Ew-?F{HMJv0;LrH(J5=At zkE^IOHGTKp+?!%j-o2fj z%uG?yGiUIPfe!*kjyuPH_4;*dtG4#pvlbTp{h*+ln4CDFqC$9m;5U(##ry!yB5rPB zVFm-4QgLxYoD{dDqz4bSx091aMFj<;qc=C7KF!Gy7XJEcd`k}>78S|L9zSkn)!)Cf zgO2R+XS1^u%GtBx;+dJ-+e=F!A;ippgF2swUkNE; zVB}=|w_j;y=K1rQ8uVYFx_b5M#tj`E5X6uy4mS}uw~9(-<@R=8pS3mgjK#%B+=gFC zOe`yFcXw?qI$B6bR1}$m8yiVUAnL=fw7Z*`x$jp(4?s?in;YHy2M;PMkxOG@0!LI& z4}5*v+AS?FUrtQ8xLmlPriRZjJ?-gv?wpiVPR`EG(o%3R4-Y?oR1`Qr$niUOE--L@ zerpRoq;u!Iyk=*gKd-JK|q zH#adaKYrZQw6E{Qi>@vZQFV21+<5tNbQG*2O-*?Drlvr3gz zlt2$_Y<%-3Iy@>WM~`Z2x3_=u&F$MH(y3F>!@hjEu;A}6D5#)NRP^M@@^VPXg$u&M z`-gsy@xVsw!B#_AW_J)R> zocQ_CPq}w*XvmwxI#*Q0)P8W#pF`G?lS@vXpP!vYR)wr=RMg}o+@|R52M6E2y|94$ zfT$=k?WU&-3w3mmlvhzPItm^p*nIW%_wKc}+S`kZBTeT1{lP(RZy6Z_gR-*qbwX){ zbjj7#>1n8%q4S@bnwSU=7Z$#BDKvCyYJNT;L0p`lA9P);8Y(J6LcYFt@4`=}tW2<% zUcc_>(AGY7Oj8q{>E2#*b8c>BWz6+QM(piRoRE^jbhiR>F>S%CeAy=hn&h=0E; zpRsEt5}B~`OiaXQiHQLx0C`FY2?QHpe_y>n4@pQM_OLWsdODF{gGv+$Bp_bGrH}6g z!~y7+@t*ec-O|&Ol0rhfyr5Oc&(F#t>bUrLjunrSR|3x+WOsHp1djt(Z% z(-R8S($eEG<1e7!0VZ+;=2+-En(+dtRE-o#Nin6sOljGwnDvFDPf{cx=tmyQcH#<91 zQmm|ujUyvlTKf8!OlN0P)4)I?jl#*v#Khmfyd26w0|NyGOrjQ!i0Iygy?v9Z^#fhW+#N#4rI(b9rz ztg~}%t)<1$QC=Q2L#VeY6gZB`$`%%SdcZ7JRi)F1hr7Em`-ggaV4$ZbEKF8bS~@gz zbaZK{un<{OuC6z3%*|n*rmXDZ(%3jNQ(sReOG^{)ucO1uOIG&!bvk`$sJlBfR8bMG zh1<8s#*&lO)vsRl_rG;(W~QXX++0o$KDhb$rY0w+D_4w+@QI*&!Za=<1f7HMaD9Cv zqu5v^dxVB+Ya1A(rcO@I%w%LhyP}`~Lh0BTgJEN1ZXOa+z{#aT?#t9rj*HvbdGshgo}2rNFDR7F%^!X!D!OptPk%Bs zU0nR|VQ5HI^)G)>RP5>b=Rap>&CL%Ul#yv}{_VGo4QJ=iKI7%h$$7&`M-B=)di3N; zyb?HqTwKSGhlOo#KYdE4pFMlzNI<~u?)&!@6=GsXj<~sPZGHIA)uo{D*=PFtBO`zR z`|aC$dS85@pa6IH__&P?K?C31ynWl*`TThW1$c%gCz1E4tW3;t;K4*1_r?Zzj)H>V z8q9JC1VokP=ZA?Z?wse&k;%|P*4APYOd_?ktgg1VyST{9TU)c)Gc)b&V1t^NWM>Zz zwY5Rhs;f(!(c$5GoFgQJ&91A%nvKK<5L;QSkPuLMnatYSq9Pt*xw|VV z$;pw)#l@48gZTnVzZ5mgoG?C>gu3VwXqQu#XJj)R99CK z5hbP6RPYp|qt(^5wQ01jt{XSP!?m>Z_3;?YeM8O>8{5`)^JY?#sj0qxNC?!FSbM=% ztf**hg&)J%*xS3Hps|r)=zDqP=GNEaS|fUv@JA4nBX}i{3+v?M;gOJlUU+;w@;5?5 zv$Knf8H|t+u!fV93k#XdxHxxrCnsbem6vB{2L#yIU}{oVS5*}jrmik0=jqwih3Yss zSV#!+0^^yK%$TR!|Ug*E@GQI$T}F#MISua%N`-1_A=GG9w{j zY%C^5Nl8;PGjn0#&YhST85vpGxHxdn6B1-)#l(Vxku8{u@s9(BdW!2H~;K7X>=H>zdAf-Nj+}B4UojIeVR8$0t4LBdjUtU`q8A06)rUX&n zh>MGgQmO0f@KngjUA`P1zO?lCF?t!iyiQI-Lk}L5m#eCtJ&OeQ?d|${q;{eDc>H*1 z$j(4o2Vlpx^ zGvUdD9{`CC#Eti&VdfLqa(D1b#;Y>K|$u`*4FSrw6w&>YiUVI z+1t0Y?CnASh?>*R?&i(4wWcO3D98<0HB-~V!uk1u z0VG(-%cpSCcL|cwrAxuVP;W&=UAhFn&BDUY4yf#0T#k;jvp@cb#0SuR2%?mU$(LVV zxl&#I=FQR)g>vfD<;&^m_wS=uhjg5Tgq4+rg|M&-oFgo3Vd4J$^zyne<$G~46X>jP*BhL)y942LOGh_Bc_)Fl!#ChrGN6e6j$sE#i3k!+N&zP8^ zqOvkfx9#o2!Z6Lu%yf6Ru<-KA%R>e%;E=}ATvk>_N88!KgHlz6^dwu(5glD# zUR9NpLWg(i=jZAg8j6$y zNR8^Ihf+!EqCY(8h=Na1cx;psSjE&(ELM}^iun8wAIw7Hvb0j8`NT#MC zA(fRkZe(VX$@==<-fVVH4~u1HhO|r;YkT|Q!@@!h4Pjvi2XsHM#*2w*YUbu5_boge z?tkoPM@LgqR8*i4>+GDLFD=#AzjO&bh2`b0E>BNcS@4CimgD?3F<~;Nr;!XIE>7HK z@7$qMWn?a2_VMZN9v;TyOP7$3h)y+V56QFY$7+kmj5)|l9*4F3FX=#C+G(K)` zfBd+F1dH|b>Aib?ekV?xI~Nmk|Nh1XjRvK8P|(uS-X4o}?HY1`z!G6H<>cUdM)#h1Yw{P*P$B$cB3=MtrO=qX6 zsh}YG=`UV%cAA**@W8wM_;F8Cqmc<|tdAIi(c#Sb4Q zlNT0#{<)(=Mdk2e6O)OFfBa))L`Ua8|5HMursliv=I0$8zWh=^fWg?=nVX|fpstUL zTj$W!V`2mZ&`aFdc>1)U0RC>U+mWcGt}Y}5z0>{sWo7E>psBaEuB_D6nV5j4UR#S@ z5atFtIwd8eDMtbEeaIyyx~YirP3qo*4lJ~jqQoQ#aHu#eBc!2En(9!TVN zc5Q7dD@{%I_E)ah*kHfV)#dGd?HWO{@96=-L0da1X<(qYH!e<{lYxT$UVA&0s;PPH z8imr>NT{+^Ik|5&HItLg%|1SwnzptjCDYRb1Mnq5>ka=*QIUoQ7Z*77aGF0l$RG5CnN-L4v+?sS&Dldx;)TbKrfn;lb??q2qNVTDMQehdU+)zKp_D2grj3< zXkK1LMRs;zAhbO3@u*Ia7>9dtMn+K)cne-$1Y-e;Rk-1TgE4U@`bOT~zP^Nm1icWf zcUZqzEJ9lVwN73h7$8uhoYU7gEG#)WD~oXE!TE;0WniGABX$*4 zRYgVN;e;EnhLhqJ8*6Qi33^3EO${_2NEoZHM>?4tC)*5X@rxI2ZTk90kIKj}ncLfQ zbHTy9yywpO_zVvtF;!an{COv*o*w+Ikj7(W1-C(OualFwxQPk+nEm|}inO%8erf6I z>h0Tpei9ODYB@QJi??rkdGYayi6tg3FM}J;!*l9XK)~|yvuC)|96xSjGdlX}RY!+` z0WyC2``^AD9=5dP;*ysyFMs-Uem)>TKmab$>FLqYkPvZkS=spbNltb`c({}l9?#A$ zETp9=D_^-1896Zl7oC=tJSU}iXz0!zbcV#le0-qSO-z)M;^TwQe0e!DQ(XMmF*`fx zL_0e*G~gi{9Q?2UT3RBLzy4ZArmE`QyUk5MKd3cRQy)Lx+KP-kefrd?kPtY(XteX^ zxww3MmX}|?EG~wE*4B1(^u>#o7JYpI0o=8pJwqzg`Sa)}Z){-S%g3ju$7U}q;2P!U zH#Tl++S=;qFgM3~+R*Uy>FB6~18P||`^Ag-d2es{4hsukyjWZF_dj;*^y%p6`}fz@ z!os+?jvWgM!rX;ML|L)xnAMV`I z(mHTJRJ5`2mtR&^$Yi8N=H)$pJUvY&fBB`LAd~s}_2#Cp?~x8BLR?(Q$Yl$BlJBw?DE%+LS&YjgAE%LfjqsEmz$_%J!Cp@Cf4 z8#g|D*xGV&`QnR9mr6>0`svZ5(9lz-E?#6XUc7ktFd_o48Wy&(vAs>Do;r2tkel25 z{G&(N*%vP!;v@o2Pk;AaOAGjRmX>2^N6oKUZol%SIl6a-o-cEsq)5}NdVT|!6*bOl*i zgkI0b2ieJ>13{ewe{E_i)(IlZ9y)n{PL48>1_^cwq4PrWI6Sy9F`1co4zzG#VMwjS zj@{ckBm_NN@M3&?LPPPGNRtZ+N=d;!J1WZCdq3A1ybSOtz_W;qgx?K13BsX}KF_V=@VTl9v}cy&!gQU~F!&vFKz`sZLHdHXxzF;o|FSW=1B{>DaX-Cp$P;Sor%x z-It#aHKD6(YHD>gc0e{Z*khHIA)(gU89pB<#y~cJx+W(F)Pmq(OH1g@&}p!>#T_*} zdu!{_qryU2+0Q>$QmU%j+Z!JTr;?8^A_Ce_I$ccc%o$9RpFC-4F*Lkz!PvO9^~sa5 zF>h}P2_2o%QmB*z14Tq+WN0*;57E(5QW6qjVMzB%PL`J!6-6pE$SV7u%I)pOMpIJ} z5gVJfHbRXoAYfnsAAC;_rrpxgsi}AG_ViFF!oukE&CidF`1_;#l##Kqv9?C1%gUnG zUR>PR$j(+!5ETsyx^rh^BR?O#YzGIdk1Upw5}YeHZ^9d+tbFPeysFUD8yTHBV`zBm z*7x5}OxW0*IH9Q6)b#Vu8yghLS6`hyOQ%15if$FiwSj@l%Sem9c=7mg509m#H*acc zh`tvXrd?f@mZ+)Q+aEkYQZ5gVwsvhTX1BJsr%pi=Fg-mq6c{KWAtwi=Y)?;2jHc$k z>xs}ir>6Gxb#}(a!k?6$-rL*S8WN(euCEV|Rb!){pQ56c7M@1N6Z){v$y|U8T>Fg{n zZeW0{cFepaB%uA?*g(FEprE)ojkd^1_C!uMKfjk(fB)1}VxqJ(dL9DvZuDUu8kG?+sKImK zE1|!Hk`szWBy+s#OhQ5$8;6E412r(f+_ST@yxiLxwRl=uLjydA z1_mTjax!Y(urM<-8=J($#>V=38V%{hNZzZjPe>q>9XLtCEu4c!Gd9-H@boMyYHZBN z!28CWqOlQl3d}4RjMmoLT4>xoJW^9@YpbfDS0pmkpe*s$(R)SNbrbZ@14_;RXVifku=uqK)2dyEPd8n@|D_N|F z2uDZo7@){TN*k(k_!_ga&^aXteVCiLyZigYu?@W|UJYvc^mGuSaPK5k#F)$wns2Cx zL1RLV5EA0(=;49%#NuKkEIT+5`fvEr$>gxGf`ZCQsIBenpark4&dKrhwXyN^WH1Oi znZ3P}6ZDg)FhFX9E<7hEDhm4V#KfW^7Aq!(P(tH5`_~@f&~tNxV!oiDx|+eDaQLF2 zUsP8!nMjy+aiP;ID@#kENhhw>k`gFny}Zz8gyIao_JoAIJp2{#+X%lq?%}wip*D|> zc6W!9fz2)~^!L}+1~H|xb7A4;&5#fs9XQ#lIIOAocsowYa99{9g*1+$-`*Zcc=&0K zftPbtqDG2|ao`-#)3&t08(?M@8j4IdHXAc~dwXQgR#y7^>*$!8QmOUzRaGG&dU`}B zy}LUt&DX{!6d2ghFfvkE>EWTJWpAI81Im$?m#(gz9hAVO zrLL|@N=8QM9I8QkJ2-wiI#g;;&%gj?PsYYnYIpa*Kzh275oVxGO?`ctna0M7ikMJ< z8fa)JE9>ak*f>26ACHujg++BWyviOPGBU`L~uSVTlhDj@+HjP!IhHAThP*zxhHsiY*hWU-fCSV&HmkpYFi zzaJG8b~E<&ot=w|`T6ql+}vhn;PChL+1U}FVRv_E$kkO?7)~yNtVeKfmzT%KLG_W8 zBy@=(Ar~(S3lpsI#so) zd%)qp+t{2rBQMWl-M>FNN})i1$YgG9&CZ5}p_iYTxw(ndX-qs~Vip(g-)AtcT!F3% zNwnbP3UIV;s0VU$<>k+w#S8+I5KBv0S$B6rcLFjjvafe`;2Y!ThZ}fp?dDBeTLA%0 zO{9Ewc9KY-?;-c@&K(br)2A=1-S56*u|QP7MDy=|Z)lK}J#vIhUS9sM|GITc zQ4x%T{{FxJeRR~o;L}fKWa{c(zn+}5wEW@=F|pFpSFaWpz%saWDL?=9Yjo1!>q}04 z^k`=%KAw+r#K-UKpo7fKedv&v*Xru?=M2WVbDwFORlsH)o6w?99Xm2GYP?YGrcd;7or?fm)N+*hwwR@~e^`|R}T)YKO*Ha0vx|NQ5J z2jk-2z5DIAoSc&8D7T|L(iKK3&~IhZGc`30+w5^n{D9qT8UoN(G*ym<9$TH4am%uH;ojEt02M8x>`{CrN1j*h&1U|?_W^mIW1 zSUld|t*v8Y;GarLT3WJLot;Pk#~!4#w6CwY*v3X)o0=jjElph4^%ILl$n?q6@~5voJz^bVB>&ogAND5$pPyNq*(ao z;B6v^o7hh=nG8l~DB<7#{XB?{W->ujhl?aMG$#j>O*nc21Jlw93jXNy0q+_Ao}Lb> zYj7~WNl*c@vWTfUdZn11+Sq`HSzcaI0S+OVOz^tW(lCJu4$jEHv=!`mq~G9d0fPXO zPI&A5IHcT|n4BDV?uZ%(b0ZKM!3_I%{wyXrzP`4$K0Y})wY5wpGN)Z#8H|R8vNG_B zY;0(pyj;watT_irWVN+~I}Vz##>S*1F!sa3YHOipm3qe76)$D9gOk7<{ zO1iqR{{pd|#cFRaF81)y)pc;l%j@p0sqy#M)wQ%tO>J*)Xo!d~Hny}(Ol)asY>bXJ zGTP7VPENM8#J;Sq?&eJfgGAEN!NdwHl7jzDm6wdKv z$jGFouCF7P1w0Z^=VxaV5>TyUCcm`AV93avJ?rE&GVJLT`ZRI6Qs~hZi!U2)`1(F?IFYT6nII&?YZmRP^}q;$lF+ zxpRVo@$r~1gPjM@)$Hu@GL0rEc=|NqS1K=8R_5o2Z;bFOp}#RY`ucTuH#p`R8VwD5 zd+@%&@mNvu@F6|}H@CDji}m>N(h{iqA|lX7t*^(&qwbH7zkh#w8!RQbx@Kl}c9=|Q z=@TbxZ2J4bC{P{67ne zQ>XA5wzmfcFlE9V`SD{orFeP4^Lg-KeVt0Zd>Q#^o16FymoLL9g_OD6TnPy-E_{ZK zjUtYh51iG$zQ>Q7nzXct9J^PqhKKDr$qU-rO-(Of-noO%aP3-2$>Ya!b5OY<@nCs* zW(G+l=g&t(EG|M_#>aQ+6f)PJJSizrRK$E|W`_6-xbDZtk$CM?F1A z)lyO_F9)55MB?Jw{|v}f5EUi*F9Z!cG4a8J-QCnwVd1l9{ru+VcXqO~MMU5W9ThvP9I;OJ2PuH^{cN)Bq+-J z`<0dd<3Hr&Iy&CJpO`Q-{P<%rvFhsYzn`Bckv{(T!i9o@pMHAsBsloUkyEGW^w+Q7 zyh%;v;o;_vjC}U&-MjpJUfxeXb#w$Lv$FEir2_{vGzJDfin*E#1`g>#vv_a&bvWl$HJP!-EG<30%04k@4!)lP57TCr*6*bucG0?a?DT{oJ{) zzVh&ZsxUWKK;Ym(JG+I2_wU=<)YRY*oS1m`uDe@Tmzx{uWIi_pOCP-ySJB=#K(8~ba3$f z`|yzQ^MCmznLIfO#iX$CmtWf2V$R>taODaV@?&HF_{Zd=sp-+9YHBxczJEV6Ln3|k zm9%tS-OoR7ZhClp{`r|RDJjpMJ$L{O{)rRF;N0Dfj68h!;6Z=?Cr^I;wY>b&rO!Si zlUG;&`Oo%tdHFBCFgFKHbaeFk^^ZQ1lx%K(|9)=H$_k{c($cqYmzNzKKmGLFx!l|z ze|-58uXOluRMa=$ynoMR^6?!$?CZO?_m6+n)QE`u^{=L;Gc&*a*4=&W+D9MB$bdZ9 z*Qc-l>8BDBrKL}w-nj!!f`mkVK9pc_agvgl5U;P_y9X7OtSn}kD=TSfa&kgKL?#EE zH8L{LLG|=77^bFbYCb-7bv->fId*o&#spoC!LYDERd@UL+#Hh$udanfYwM#&qoZza zTwF(wT3IzWBeN$w+|<WC!2y`*wAS3fF&eAw}9j$^ghDEuqyyHf!HlXMiP7ybZ8=$Ao!*mug|5mD*v{{&od`@4|LOk~%hng{2cP&;^W z4sZ&=z=S3uC@3KTy&4*g==l)JXY94GSAt3v8fK`ez(5ZTg>niy)BPF^`8NI>&JIEI zg`OLoBlND2co!FkE<$9axw(dhn_G4D;^Oddc(}MYAD^RRXXol_T^)%1ii+UlkB#~I zUf{SKiR`SbENC))eZjs(LXwaWh^Z zP!O|bc!oVag@vzQFD?cj45@62ikX=!D|hdPg&{MIPG8`t`NP64T>>{?j&nproaea4 z0|OTqA3lWh9e2*2`I+RR(v<#~*8GjEtZ=Yh?7NKk@Sy7r%M4yzJ&i5OSY9 zSy}P*J%0TBc{=^^&lhN%J05gTl4lleq2Bx zH5ECU5fMB*yu9JzpyftJ@^cRCkS12{MsJ?!FQIk^iL{QQ=e_mvb@R->axi`3CM zb_{p_hYveD;ZcVI6s&IK27y+-xrvE}goK7hLBYxj)F6_Q*RSX2udQL{DagrqjEo!` z8y}C3mXs6~_4n`T85)X>m6MZ^fer(xgF{i#ty`s~E-u%vlSp}aeSOW%;o-){BvN{MS66#G zjb_O?5)zQLke+U0qM%@7lam9!t+TU=im`D@N>5K$S9G+T+}X3Ls%dEx6EidA%kl1fT2_jPb^aRE1{h~q#ah+`ETCVyjX4Z-zVwFR!X9qHm_A1{TNd+x7KcULgLaq`-w283{jMTwF^FSm~ymBR00V`NoaJ zL<gfIRA7{WJca3NLP9|SrrZR-EkB>dN=N{^EId3XhnQnSnStJESy^^A;Vg-Y zA{=_o&Kep91~D<+-7_xxCUtJxEICXU!8$(0Hs*#d1GjsPYovyB~ zrbeSpOx(GXlmwp))}PhYf&%Q^!Ct&~uc}I4pPwJ>Te#iL%ubyGQTNrW`FRQ_MO#XW z$$a(k%Gd-DcUtuMbkdp0%o<;&gOkdQ-% zzWg#Q4D*`2ybGM1SXbBe^}qkUsR;@v5@}-M?c26Cb#*Q-@GqcBRab{jueSEdlYs$q z^TUTFC6VEE=MLBr;4VCQg8U*OArTSm%CQ5ydR0g$Fc1^Cq9R2_xP#i-R#$6k3=Oe! z#ah|eXk#NNsH&Qi1C3W?q=JHmMphQ~^wE#7Jc5!iVu(h?LBPfFZT?ztxQdWg4k?0&EN^}@+vLu>;!#USJ%-oFR#5F6K>4Sb91}9 zq1)BfwXjG{?d)u6!5MC7NTs5~nw)HI4o@-8>a;X?@O*vQ?2e9{95Pu)$IT7BS~fc{ z(82<0yvD}T(x4y<3#j~RYS?U0zQOimvCz{;mK8{Zm=`%aySr1VNNi0^B+g`lS_d^_ zR1_Q^DJj9h6bk72VBtkZQaDFM1bnST3LPdt3`Rx8~USY5rP z<<%=N@z0zA$#idTb2BwnQ4y)R3kx$dadG?mGbjutB?Sb0e2}F|Tp2B~b^+@hik#@5!tLU8bf3&^>epP!qH=5SI31quFU zUY@+XprEhsojZGbRaLsWP=$4OudcG$dU_Wxnw!IQR$p&n0i`~Z2}OgowWz3pK~d54 zG`N@0pp}#?FOQ8yM#6KQojp4{F%ciH$~mai;oK$dG&RG+Z{8dnOiNQyIel7QJ}GHz z?9Lsel<4ZBk6u$l&@rJe!A>$hp4gW`VTxT7_{&6s8+L!#3sR|YlqDyFUVv#Ey4~sN zpooEe2pHyJWHOG=P91w9gYe+325+IV^rSqd~7 zcz01z*rj3C1C<jBue(7qhOcj4UR_;Yp0_s~ z{F#|Zpnj!mN+QijD>e5-UH81v((^Uf_O$U5CVF zd|$Zcot&^kN0y3(x_VIeY7T>R=)q)jX=L`7Y`EG3nk45e#K%=z=D zPN6FRb%UTF7Z*VTVKSkdCzHWwZ)*d)#@2Rx{O#M$PHpYu#|;en`hNIfWW>}I8Fe5> zA^iz19{9{ACmkFPABH9z)TPi+US1)gw6rHrknDZ&B0qmZ!n0@Jf6rnG3m-Y+`F@~C)?Zo{FIb*b?Nl3 zuEs`xfB28$<8R(nhAuLqj)i zAj?@-H$J|t4WxM;9ew@i=!S;6I;_l4(lv7q8qM08L_+>!Vx#j>(eRCI7CFCQGNuJ-jcH~06is=9RxbQ3+jz(C}J zve_ikl`GcP<>lk!H8sddGczkIo0(~8A(MrKu3u*|S5_t_LPMpb6cpfwotlb@l9GZ) zbbTFnRFKhJT!x0$)>teB1zuk82sSs%%T-iPo-{S>?tbzFdiPVOw6(!4o0tHNUQrQ_ zp1C==Tj1x|+go3cih}AWJbZqBX$b_PbLZe8zjrS!4cc!oD z>*}hr^H*O939(qOU+?ThMe*|T^CM5^@nblMczG!lsNX?8hwrDook&_n-=?t<8LRNV z8yZ$uZ*Go^po&yg1zUZ69a^|Uht$;IWQL~5%*?+o8XNWa2VBex|^Gg4U{Nsc1}(} z0JJ^0&y|)&M1VFE8w+x2Vj{FFv9U!Q8Ysc+!2YkKghq391-}=sl#~P?T|@-59s~tD zE)Hyw*jP(TQ&S{VLm6Ogt*PnhSy>6?f~TjJmW@qT*3FyM)q#PAhPJjD8SNZKYjCiE zfu$v#-qzOGi1QuUdEhS4>E`C(MS!-GljG!MVBqc!z5;X^B$B6RT3UHImz1i7%dZMC8q+DKZYO=5p6f`md zVQYNc-CaNcOuKKsSzh+_J$+h8C@brmZyrBJLelZ$!NG7=)9EKqe)5U4^UBJbHw6W} zynp(Wg~jqRm?2lM{`Iehh6@WHKHR+vKh>2hsG(L@O-v3P;N>kVd;flI&Cc#Gf8pgV zD*E~7?QJKg0|$;C&CdSt;r;u#IMh?|@$cXN;~xyhv16Zn5*YaU^@k57C1=ij^pUOY z+S*S)6&Ig5bKroX;q2^BKUG%p@*X&#u0A`9PN|a8fddK(!^0mw+`9(`ioASR*M|=) zD>gO<4~mP|*1ms_jD$mnE?r`=Uc8u_Ba;svwoo?x_VdF4?m2JAvH!-wXN-^pXTT7?UBxa3U6V7LOFh%mlxg}OyqcY z1O#GZ;r&la0zWP+Y;JCAi^Wn`mzE9=o|w3QzobM*2Lz&yj)es_8|lq$;TaqRH6ALLhK7lW>S`w^B~I>mUf!)+<>k)K z3JS)?492Znb#;Dz8X9J1>FKv_q03-oL?UHoLgy13YhYk(oS1m)R!`F>{dy9*0ZLeHGHcV${YinGbg$2mP&CMMhSy}Gx_V&;ifi)EvXlskJ zzp@g!hyFE52QHe z=7MzO=VxQ%;emGs9yp0)Z;$na&4z{^YH}vC>R(x~_VzwL==&5GM@2zT9uWa%L~=3` zkiZ>A&W*o6vfJY0iTcvVhe#|!ZZfV}u-RBFVh7^x4sJBqM^RDe2otF^2?^-gq9eUO z%MK5x(LgH(uN4{_BtfIY3 zsaaXMb!*=%H9p?O$+o<0#%*@r*;b9LCK0aC5^z_BWkr7|t zOP4UQU*jBUX-Z1+@^NufQ*(0}8Je2P%J84VX$GDkd|=bl42Fuz<;!kvH*em(o0kU) zgS~xg>%u~9t*z~~YferLoW$AcYBE_>l|pH2o1aI%hm@3!O@00RJQ6TOL<|hd%NG{f z+U)HG1=ZCH3s+W#hCDp@`K6`P(pFdhANJldDy}?j`_0VTJv}`=>2$}15Hvt=cL@;O z-QA^dch^D+cPrc_K!RIv*91Z!ge0Agw$JpM$*F7h%j$lf=RNDJv(7r-dVlB?P!v_W z_x{U$U)S$iU5$?yr_qgKVxUsU&X$uy-FRYRX9p}ts1YhFktqdjI;bR26q=ihiJ6*$ zf`|WCckY;)78XuT_4Hu%Xl90WYj-ztr<9eE+uqxY922a1k@FcFtE`OVpyuYWvC>jo zTNM>2r?N7PWh9c3k+*kM)&2XGm2PfoYM8GvmSHq9G71eXD=RNY?bXr}p0L8ghzK~g zVqz*QsZ{Kz;TkP1Me+mo;t2`$_07%D3f{S6Z(mX}Jlxz&BB8>fub-C(E*qE_rlz2f zqtgW4F8WDGe1c9GjKjD%u)v{7NlQyffg|4EKPoDXMixUh6}&u98iQg3B{9+#Gc)lM z5(%?^A&vYW8Hvn!C}2@t0$(IBFewR2J$Pr3DFCuEpbp;1Qvzn8G z-3n+QaMWjI1qVa(ijEliW^QiI&gh#J7bBMd>A0z>IXUP^d3j-Hm75Fl3DUWcKyi}d zmYA4NBdQaWfv6~S=ZK6+kadtAK=h~J-i7uC%2rg(pz=jdLUD0^KHih7E4ccoz(D1T z4iyLj$eqLv6slcxvvPBhR-vnV`?iAvNDzaAetvv>oSZH$ot+OJ zH#gha2@7j!<>o$k(AO78%M{htPERK?V^vj=m^U$T|9(h_q@GoeDR% zy1KqTxNqd-+}vtw@7>GEF*OAvqN1X|zqHiN4GGNk^#cPXC611on#klE9i>v;-PP2r zt+TV++KP%?T;K#mEwiM=-X7V{NZhTjC;f*nQB*WO-qwaZFGItE0&rUc1I5JPg&iH8 zm;kqxpWoB7uWx1s6#{m4j7!_wAZ8HvE|mIyesXepdN?z<4-Z5fKupyBnjRk`fP(t!+=w{(fJdrR9|?3=GcBU0t)Yb#-uddD0S+2~vfZ z7rF!JEjl_PD+3g;@Nh>*7Z*4+QNePiC8S`jjj9#Y;3o+wxJFSuLmD$wq)}1H$)Mb$ zFN-;mAb6hmD^Z(*8;@AmJ2+Tcf&o}v4F)B8G03Y&6^cmmsH%cO*4Ea>COo{fw6+${ zLi!0(Fo^wzhevdDb~cgvMx?~yeW0R1h9}s|A1EDTMrGv<8i;fNrCGKj1Z2`Vj zXlPOr?quZ2A@u|67*q%eA3La{=q14^k93$5=X-K;K>>w=-4BvmQGLKV2ze@~B1T4< znW?LzT0S&1Hda=KevK=Q%~@YhFgW2HETeg0QEi0VfXHCM=mEccax!%2=vd*ku*geIyW~k(9sd8It>k#mGI%BJ_46oLIS}Tp->8GSwAi=wzi;qVRhr-VQcH_o0U~j zk&^>=A}U6urSJ%%_Kh@Zuo!J^!3ZfT!cNWD7!F&o813xTX&KTP8SU-RKN}cWS!HCP zBbS_PVPS5L6=Po?Qt7m`f`VFFpeHaiR8;ivsH=l}O;=Y`)XJ*1c6Ju@D?UDT^}IaX z1EHaciaI*k+0)Z_EF*L0PHO4`?MOYHkB^MVQEsr0guE@y=N6W*9Gc!=%2?#_*O;1lwhK33V@bd=+jgBI#iOY6!Nkg1`JGBLSuK}4ju_}~EicX;tZHYbSMTwGwM zLwm(VJ7Qz^_MSe?$l&3*al^}Nef{<8>S}TEvuCxnJ3Aje0;&D_bse47)<=(;nlv@( z>7nU+^=e|m&W@SJf3K>-S`xl_DXHAt{r%O|kPvoSQfEZO($d;mVxo``-fLeUMmy*% zy}ZW9!9f6-+ST>`eRLr7^(7>bpZ551Z7s6+Ky*1c=;<*wW?+z)FDhDF>+7?(=irc+ z&&@?Gj6~w&laqs^aAqbnR76BtIwfUkX>KkmN<@TEysWK(N6y5AM1z$TGFe1~fx*sh zczACQv~YTQ=;2S|k?xj8#K zR$;5F_wKp5@$rF5v%Cy`3}~Zd^32T8P)GBs5?pjjkdyGl>N5CayPn zdON#`iRaH-Tfv*Ou^AXRI_l^klEZs@U%a??59&L4`ReL}gOL$S%S)H|`6-mWz3FL3 z$8+Zx8KIIoI7mpidGp4NprF0Iw{I&d#Kr09U0jxzfB2!NM?>TKb#3jow*CFCE>!i^ z)vKyjR`7Yyx2&zjyC%Dvgeol0toQadCDwGEzX` z)-7;va0OsD;OTk){@fgy3{tY2+wd^7qWby*0=BkYT{}DN?KU>(7S-1;FN4-ADr#g@ zRyH+NTWe;<%M0K9{CrcBsVNH!t_DJ$#210t6XM5)oyk&(DK z1qE?&tPnao6B4ww;nl6H>*zq$^Y(3I@l8*cl$e-EOFKGtbnNa94|{v_@HD=)9Pxv0p~Q%A?jDm}fUgGd|J z)eQ{=t(HtiCYrlDIE6VmB$9;%YG~Efd3k|>4h~*k@RMg}!&`o$pb8Flb;V<(yafax zmn=0EBfq~t;cTj^I!P`kk+QPDX@X-Lv;+$6h>0PQ+}#Op6ZZ93zk)P_`4Fq&$Vlv5 zp}-0Xiim(mE+qxISy=gDWq^!wUmCp%yezoquujGbIwJ$LMKx8J z{1*;Dyo#?ch&SM_gM#eg0S9|QL2RsthqH4?2bwB{BhdBGlgvWXv3`4AX0t2y2NlgVgGdMURBKAKiKv4bT^F~F%wL^URi2t32 zbaGZgLve<1woiTnIz04oL^>FeB}AM%f+m0B|G}$5-$vvL;qmW%0{`_fzQVg9JPE{O zeErAq#~JwJ4E%8h{x}1FoPqx@XW;*NM*00SP99@!{4ICww?~*Uas2l4-{v`dpP1Wz z|GRkR$t)X7J5WRXO1rQeIi<6*bV?;;8i-e?OA)(;l+1Z^PsKU@O@9*E-EGdzbqodQ+ZE8Z6vZ^Wwacyk} z2mSrvMyS!aZbwJR2;k!bk7R#;dK&d?DJcpCy0f4lHZ~59(9rq$#l`4oZf+J9FR!Vo zjg5>9V(&FFa&XYp1g@E-WoIW6q>-6nW`z=pdb>DkY5khii1OU_smRI zmYf_ryRB_s-_A~5osJGCr@47|H+)GpHat9fdQe&q4%*Z5m5`1H6{m*>$koZoo0|&@ zNN|FFdxhqnh>m7sgKKDY_4#x7saRRv-REhpAu1JhM|=C}>9=n?I#g6HU4q8o=~HA| z5$RcQqnVmsy(%eLUXFb-{BA-*Sy}u0NXJI{bV>^0@b*Fj&uwUv^B^ej8O!NK+Q z>}*j{Iyy_s!NG$AI0QjEB^W-)hyuU<_;_s0!GWFq&Ykk|$B!o`-QC&P&~I2>ot*UX zVQ1&%jf$F^o1F~{hN@U0qgII7=lZySvlVetx*F$mGq<<>lB|2?nW?fwmAL0t&;4n07Z$CU78Up2CN2jjt2`wAV!von9`S}kY z&d!1b4Clf8{PZ+BG2Gn2!RV8Kme0xxCC1~&<>kuC#QR3oKvR>83rwPC&pJAEbgo}l zQmU@r*|~ey!h(fGK>KFLJHSwiv@GqbPn(h|IeGBWJ!9v;)vhliDwii&J(=q9~*fo{^pi*TTU zFsQ3bN2jPrrS9$Z^_kF;*g&X!`4X9XOiUsod3i@iTU)402nnU9@9!hwkAnj`mbEq1 zjW{^2Tyb=qn1E}OoBP5AOUtpbr%$V^Wo7Z%#>U>gyLZpfkcGv_=>Gk;Z%0PpJ499& z68>y#uFy!xP#;fCAq#UH+ zpwXT`k5@g(5Y*8@eZH|#S^4s1P0gO3*RRLMke{ZYP+$M*6+RCutBg!(DLU+M*YWbE zrapSKwiX=BL_2WhE-r#}O;7LTH9x<*i~m2U#VjqodezvdrgrO=i_7#h@&s`e8yWTW zJ%8TUXJCN8P+7UN1J5hCw#CH{AA+?9#wLZbzrVH?9?s7%CI%<%#zuU+kPxoD%}wYW z`T6PTkqox7LZKkD5;U##^^%f1cUW01Ec*Hm4mvuFjNqijNI5v@;K0kPrBze2yE`)C z>dMI}Cr6>IuaA$P^h75o$Hsz!czJnv0s{#)sEmw|P(Z-sT+{iT6TBu?R9pVnZZ|6RtEByogF(nUKRdajK3lxxW-miptTnfiHMk;1yfi~PCx*D zHA+W^fk9chqGEHix7Wsol~r0gCue&b)g=ju6S8JThNh;na#Ymd z;MiDlvZ|`AY(PM7@5Dq_mX?-;1QZG&s%dLWOS`z#*AETl<{B7CNuleC4ugY(qN0;i zbMx{tGVZ0MjEqW4(Np&JmXuUcNl5{PFfb7P!GwgNq5l5RP+8g$5ivYGHHE({FCP>H zCpLwmqeG~!mzHp5z>`NevbNU3f>6j!Of)vy*@3^Bm$$GmG!zshEv=%Go<28+F6x~- zii+{^NJ~mjmzNh5^z`iSUt9zyiJKdV3^OzEz1+Hmv^yyK?Cp7ZQ4`wO0L>S#1V!iG zUSc8>%^jqu*xLH)6|x{DBxGeHBm4U9-%m+_YXkKBrlzzsq?-o>5VSs2-2MHb^+kfW zlM`wK=*)o?6BGneHlbEN(MUo^i4+59Dxn^LN*OxJkPuW>5)<(l-ZUarj>vx_h}=+a zV!u!9+M(e@GB8rVL;rVzH##2JO{XD1<{txcg!PNEx$4n;!3v@iDHkOP5F_xZM*HZrnI?#@Tse@RWnS_BK{$H*aFCfjJu#VKK3S0_3lFdEL0d$(fRZBwplba&bjP!B>o=PPq3M z7LJaJia;ON)5FLNWjaQ_uCBww_I3@8t5?<3Iy%6hv$JDhz%2Ue6%rzmTuY%GA3uH^ z9nH!L@Ad;)jwZBR?Cjp&?GM!mi8 z)7#k*6a_GaiLpOBdui$3Jtrq<+_SPCK7>A;lM{~pt*u9o($j^7IXQ!a*Vm7ai;9GW zFI}>?CdC%o}S7|7Z-VXV`Jzo zz(tmnR8mS#o|qUJiH??&laTQ9yL)$X5+k#`Jc-oWIx$gNYH4}4fnSpDByhLMT(5B!DijB3lwy+2ZDJrV0OiA(d1alP>U8swoG(vYD{Y!!f zO4N%zJiubj%Y(<)-5tGPbY_Et{rup{%g8{Y5GukaS_4#(2}U`=F3QZz%8H4BtK&DX zFMby~Gt_rIJirjo%}q}y7-^^ipHxS|2un*trIgUVLyr+aJCL3Lw=LLgMAaD7BqY3K zWs%9Dq2RTqq=5M4>8YZksu~i~-@m>NYO{!lj7(_g-Ma$=(90pqv8oE{JX>2?S)@w# z^x#b1zOAK|mW5fT0U`2PTBqq8&ge0g~W24Z5?)>JBy zkSHjK93ZG^JUk>M2#?L^sGpy(u)KU$)+UX_84)>im4 zkOTm~1^}HPPo!PbY|12?^lQ`}w)J5V;x9Dmyy*_#iC@6gTkv z($Y}b_w|L>5eh&G#mC3e($zI7sjLj%8CO>l3H(~3X7BAy;_Qa6|-D*6<@hBODwI>OWLT$QVIi0B$Cz2$6Y|l$1du4x-#+1}pUD=d_gV`MZj zxpxm^rM*2py^s*H&>udGjYaZrM8x*?(GeIU(A`f@zj#qscl-8*3mO`2ZF_r7O{%IF zFW$KW5(|>BFId3&#}K6#Rn z!Oe~R3U+uD3NP=ubLQsb(|}g$egpZoS69j`+)&1t@G#Q2rn-`e|R`NK&ZR$@`B6x@L_$uv9X|_rDc0N zn64 zR-iF4fq?-5=-+`>84z$Xa^p9V2~ki0J*um#i%UcVG^lZLB$^io{u_91oSYmTPZWAC zE;csMy;oEq`wEY7cV^HC)X*k@RhgUX<3lKV;Z6hB+r=d*sk|J%Yn`>!#=MJ>_ zqoWNC-rl;puC7&8V`EKCUS8_zNU5Bh?CuTKU@A;nco%g6|xgZg@~Qi3^!|&c58_UnPw1f)+jx{LTR8{Tm!MiUhadXquw6V#|Y-uSi zC6S1=LwkE`Yht2}4bnHNtFhvSM_)Mi-KU?me16HGghnT`Dxc42U!f(9EH7#W$IoRWfP z6KqBhLy)@yrYrPk1SK^q3$urxAHf45c33_>psM1Eg)S^6rnI!YJU;#;MGkil5~BV6 zb8?{Ca&xn`_V>qKm6-`;m%l%Vk_ib=^Tox1Z|>_WM?1p8kdawcg~Ti5)jodw;DM(n zGc!ERv$IP}DJfD?{QN#X!^5DVX=q4F`umTLZfqdWlb_$!b$Iy6ljdeP*+HvbUq{W5 zjSZEGbz1J5wKXd%s$mBQOG`mP+}t7}$;lfVtE*5rOG_swudOXFM@HVd#lqs}2gd}N z%*FLJEs+8|?6NXR$%_|3O#b%UzCInDD_4}2+uK1&_V8e5S5T;;(ZhbDB=7EGZNtLC z$_mQU=4O06C+CeDuC7q&WM_+uv$7&V?#UB;QVtGB$MNyw<5pTi5;L=r(cQbp$9;W9 zMmKKA%fsIcN-`@es;N6W_h{@51_o*Ava;vTH#UNUZ`~3W&dAu`-`RN!T$?oo> zN3pRiEZ44id9AD*9AsqNy!rRP8ylmVik%NA+vDTE(DHY+wa=X+=t9fOPEL%B^70K0 zZ{92|xwu@v4xQHX=gZ5kt`{$|v8AUU9c^#JD|Yc>SlH3gt5>P1H*S3SrL*()_76YQ z){2W?xngg>v;=;Uj0_T=78ZW`sk2j3@{><)-|p-C@yC%7HMLJa6&7x826f2T_>)g= z-mIwj_rG7g3JN-R?%K74gx9a%yh%%AWxakqB;@hqmoGCjp)YfBSy_4hJU^e4^Ym$? zJR<_>ElAph_^23Ln9S;wD zhP1T9L(o^wpTBl3GVc8ybd&^7Ab$FnW}i zcXnoEn3^gpySkQ@wY0>?tEqu^gswwxua(uMOBXH}8zYImzdt@6JP#PF84gWQE@DdWJh|bS%Z6QYhcYbg0 z;v$72E6c)SVS!Ynii$gT=;^h!TU!qf1_tcxkRt%<&d7+nJ3qgwD*9YV?B(GR5rGwpL!Q zt_~V|W#!CFb+wTZ508OCb@jpmyc*oxaA%@2?csr5SZV3@_QZsrpNNQ(Qf}_X#`JV> zuz&!(8c3W&?f{6FV`JbkKx-BlxUjIho1c%4f{V-8*#15kBZ7h+9+Q(`KO#HRz@W8t zXQ#PYPmh@yNe$cE$ZWcC1Jt1xFQ%q!ZLeJ6Fg9jlLQjv5!Z+Vc zOjyxqjN;;$6DB9^>@Hp6;!027+e5t{o;}oaKmp|EXJ-!z0{saLP5}WBEDjIz@_a)t#MLS*SmHc@-2?Re{KDXc!PsQPJF-o^E5KukY`VY!E8d z(NU0g*x8kq_V>56pq7HQOkyH-b_Aau_i%VP#*ft0xHu^4!@^Lfg&zgfdZMm-LSv4I z0J97p72Me;ete89@a<#G0~S4)DdFL$oTQ|LhWhvrt06GRu%=H=hF%W}J-AoEjK)3z zJ5Eqok>-rLF}yIu0a_aIUNLT9OraqI!iW)x@Ujv7R=i_ERUaRpPCLLK@b?c1!9EtN zqaa#hFUG#~^q3f|-9S|X1Hj+k(b3a0DG587hzL8{5fxQY0p5kBrJkOT5B#sCrN}k1 zvdYgN9`5W64pvrHQ;Uk~>FMuJNzv0&QStRf-aA+`y1F4D^|Zs=8*YpE_=gYQyaB5W zwAG1;pMQSv0DDDANh%c#FB0j>6|f-Sl8uanW7OMwYYQ6h>(~GGH+%c_^>^=zidb3y z*Z(pz+ucPiR!ZvBDMdx(8g6W8X(4s6ukYi>!$TvZQ>V_HDJ=Z>@t0o$0#4D6kdW`c z|M};*xX(U2b;{TG=bt}*EGs*A4nEXZuReYp8sg(SbxK`*V*{@$AaLrGsOSVO%W!c~ zR`%2>7M6~Vj~}XlnR8_009zRARAUC(1TyE~p&is5x2oDc(;wL9(X9@MYyL(^X^mKZ+ z;{vp;aE3TL8yP`Qj9h3iW-Kg{l3H7vn`2^(jC6H_f^auwWr5V->RMFP*$Hnhw8^Qd zNcsv7x3I9bMz^J+A~MpNc0|*h_*5#AzD!L61B;8P)YMc*M^n?^Q_H}&sjn|72@VE} zCONsXGC$wnA9+*A5~EThB1}xcqJmF2H5ExuK0c+TEiE}YuC4|KuC6&b*mr^yWow(5 zNTq_wX8#{AU`a`FF?`7A>e z+8PO1cz1q&VErc~AeS~W5{$a2C^%?SQq0Y@wY|J5Dmpt0XncRdDN{;I=|trZe!7GN zYiku1JG=UNFeOq_1O>q>o|<~|rms&|_xyPg8prqG01AX#x45}+ZOqKX$4f{E3WC@N zZ>yrBq-0>=;NZ**nXIfVDH#$19Zi1zojZKAv=?-TK@bMp0y#x(ZK|qg&q_;Esn4Hd z1_vp)u<-fw%}pdY2?~O;i~R~g+1=fJ@+38ti|hJzH@DeY5XSiVFI+(W96X;A5|=I+ z81(l)d)D5rrNzhydd90)Lqo_Q($_!f*k8LQD~l|j#YKDjvu8Otva+5(-`Vl~PeM>+ zHf| z*Zut}DyL4JI~N$Zy1Ks)nu@YA>N|CH$Xs`FN=gDT56m$Khpepj_UdX7Ae$31SZI47k zMh%4$6l8CYq!og>L?j>A&{&1=V%yqAM_1C0=x9qz3kzgj$~?@nc3Uh+#DLJuWxRSI#NxIpP!Z%QYX8*Dk`k4d3a=H zL7v^)Lk1}WgMmRq!}4-tqm`AYD0E&sJHx~N{<5;Vx&;Lw-9<#m%d4m)Cr?gJ(3~Og zX8QWxy*oZmCabE7in_YCwT+KwWkKa*W!2V(j)18tu}a?EotW_T1+_FgdyB>hCI~I?J1#7gmg?y6@tK?BeYCXL*r20aT)eQ**XQFSBZL0#^fX)_ax^tU zQquT1x@XeT!ou$E&CR{N@$pJZ5)w#18Xe9456=wmX@0)8*4|!8$<3{~d2X)09z+H! ztE#H$>82(mb?WHkcDlO@3*WvS7dJ4_-5n4hBcr60kT5njJRBXZsHmirn1~Eh z_`YRjLG4^!MLH)huai@6@7h{rrM5N~m$7kO-P~MNm7X3RH#cu@cXXJVva%v29!f9F zH1hJr#UPsp2eY$taYaXOZa#d7^)n-*v-9xq>S|V&xHtm?bR^5m1qDcM1ONT#sFRj4 z&&>^O)3ayy??W}HtJ~Q4MKCw->jQiC_U)@z4GiG9g0txSc?E^Gw)gLm zr+odogaqm&NojcaP`NuyVKD4+0^wX8@ajS;@7Sj8FhB9uOmT^p5Dl)zyIY+oNtg@nwt*~M@F2SczCccJ35-3_4elE zL_XKj5(vGVoJh@^pP!rz4CLbC<&B6~TwGgAP8Jd240l9Wyi8+4A!2?3R|Tt=KiGsc~{5Wf%Diwzd)y_V%FucX!*^fM?d&`1mpC zZk(LAZ|CH!u8xe5NZi~K5{Zd3GsDBkF%}k%i(8<%vSVY##6(4-X?eKo>uG6HQbIz= z7}(pRP((%H-vem@)xaxPOib?IKRBqVk&^oQYi;eJp&x!27*JM5ovW?w~GoRQmfrd>9x|Qv=Jhz8;+_ zL&Gn=;N~tZefxH6%iaC_c@~!Rbht`mW7*i)*%K0;K0Q80?!oozK|xQReD_^h88jQl z#v>!}e~OBpIisc3+YA1(oZRWtii%xb@83^P+t@HLC@MBKzJ9&1;Off6BqLK(^Wnqp zuAkqPD>rULM()x0^hrtV?ANY&dM+*=9OUNm@m;up4jX94;OfKM3AQPETkzOIAq(B4 zloXVi7!jD5I5|;=TUm*SU}dGF^YPi)`SHhw1|FVMr!HPhNqO<&^=q)ELAa~>Po5Gn zZ>6WBj~htK@PPN3puV9JPb7;I{x0khK|(>@>!c198j2LRgajfvEh;LGb`a?z#6I+Y z>b41eHlfG6(=Wg0pQSy`a{dU!x_iENnER4*@UYd1G=s8Knzv@|md3#+PX zYy@w{*x276N$?<1BR2tlrOHZOT>$~?LXa))fB^bq$VBn?7ZQ?| zPE4Gd0xefSKtv=m5@{7NF~Y)JT;PjrZIza4X`y0#|Nh1XcGkke&d$&qG&dUiRmg!W4qyquLKDti681ua2tf1hw~q0)x*auELw3_3esyciwD`WgQBXL0R#~~F zNg~i{0VPShaEe)hzSJ#=DqoeBTJ9m&@gZ)fX6Vf1X zmX43Rx^#4|UIlyS;X|;iP^~X4grXX$aS{@_x#$*#h9dbcDQRPasN9Q+#>OI{EF}em z8EE*nwkQ-yNjMwt-d$SC%|*t$xp`OD))tkjrNzMkHyUzQtgZR^O-$O_o;G@0W&iX zAHplfz+h$uFF;wDvN9_x{=)h?sQWA|`uc>Y(#nd1LtULpCGv52c$AgP${s(4rxMjq zGI?tYgho^rLPDmeXJ^C1IXR(EoS6atMNEv1&C?Sse>gRf>@+!feB9iup~1{-Yz*cx zwCV8rR98dwU}3>XOO8i|4AKi3X&Dt|#>EvGiEI#%fmm7n{paR3H`CMM6(f<5gIHOqtPGahg9pdQjg9K+gv;^Svz{IU z0|o{a6;O+jz{td;piowZ?!1EoBO_GJj~}nBz##}${MOd`I=a>Z0=V`bJ;L=yPw(I` zJiNJ?n=2u4;|2&)+uO)5LUp9S|M9eVpcJ!WQ1OmLg-?!tk_$S5wJmj^0SNC@;%DJl32adATbaiDE%JbVbB1L$o~ zT~R1vV#H^FS4>Xs#tl=`d-w1e3=LUXb#>d@pFM*r_0}z9Qyv~pOgKBUun>Hnxj7`t z^YX^UEiKK=1O}qYfSjU@jie+20X8;YU*rasmfpF;%ZrSNr%$O=6_uMet*q|fKRj$} zgKtbtt)^yctE~7wamoF*V z+k5cfWOUr3k*cBTWn~4sYJWdIo{8z=MGue7&3EspRB37aUtd^&4na}z;zb>u`}g0! z9~#ovzj#qr7FX!tpqUxr$$k2CYRbWZjt=>0M@Q@HI5Y6)K7IP|VMGLKs!2(Qv?DQ* zh2_c>jE?*Jsj1A&$gV^7VQDEhH+acYQ~&tK;Gn81GH1HG|M}024Hp-BdQ>)FzFb@L z_s6SZ-oQxC%PS}t74_i3#6(Dlpdb|T6BAQYQBh)I!ondT(2`_ks;J1xVfMlta_5eS zh_5eFn~I85RlxvlX_=iZEd{;I%BrDZcDAz8(2$Q0_2Sjlt}bV1adAV#%F30Md-vcB z(bCG#Us@U&2?!7rgmML`O|h{uG6DjqXTzx=FVDpVUe)8r_wL!*flF>?Mx{$yBqkzj4izVYrs?5|aUS45gWO7ap zl%pURBi}784H*mW?x1Xf@8|9gmjKkw$cG}dmlYLBNl5C(=OY|#4i2F0p%3WekbqxuWo3LkG*ns|)Uo+_2%OORD=3(o17}M_ z1Q`d5i+z1iIH{@@7NXl17|72rA(5QCvI1@Z_}M-_NJ~H}FDEC71nnuvw49tsv;~h& zUtdtr(Gjlx`g-sLtgPd3dlE93GyY#z>BY(ed%^ZDbQLFj!a&3?NO|(vpYA z(6G6AZx3TAs{S=KP!B-eDk=(^9TE?CXh(4H1T6z6E)JD`50AmY)z#u+ZEbOJ50742 zMsa1OiHV$?msfxP#ztKoIK5_OH8nFcRI0f-n7!rY)6;cz78ZPby1G?W8yo%oj*c80 zGBOm(*4F%dWTcpwv~*I^($d00OpKr){@TLAqesMSX=>WhvAm44gidf<+wN{>C!z1f z8fkpo*H=tTT^(m&Vj?&gy1S&L+1UpVFt&(`2L|@{kB%Y@OiIenueW!69M=!VoV#~d zSF5WH4Fv?i2LPSU$?3)o1_rRxW@c(@eSAz!u{!{L3!Vcw6q1s>y}`jPECh!itb7ZL z*x2gosw&Vii9DRb!l)=07jR5VXa`cx+}&|!Q7G^Lczegk<1PybaB>0%t*k6JHz>%? z4yjp{l|@BJe1jf=N-ZxB4K+12H;<04uWxEfOf)yw)%Er+D{E`Z%L5ViM1$bxXJlk; z9Uos)1MQcSlY>JO^-yq4{u-LV|8FSY7b7A))Go2kh$`9u9(9TpSc$aNH&) z68kB_lX2p>&B_8J%+(d--@H7yEbu9#qVTtnTViJy9E^;ij0`U?8yj!$%*@J4Fy8F! zyu30p;Ij4hMu!*~Tj(5v4v&5}`~m#@e0*4)zk5fleiRgni_z1{$dH!i<%RokZx3v6 zE-phuDix!NogE*ak`kFbI@;Fe?99t6EgcsJ_XISYGBUBTb8~BJIXSAT($a8#?eAAs zs;FRf50~2AyCx<~OxR5k9!U-kbhwt6A3VU?0sFP->4k;V)H`?Jv4$FyOulnRS~?

-ac3o0IeWc?XA;mG8fAYg16T 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 diff --git a/assets/resources/wav_player/CartKeyUnlock78.wav b/assets/resources/wav_player/CartKeyUnlock78.wav index 41bd420e96bf84ee8b3e3bc9c4e82c3406fad7ba..a82a45803783f74ab47779b83057d89fcbb4c651 100644 GIT binary patch literal 66234 zcma&OcW|p&b|==dUF9v?TV8utTFaxAb~Q7aaZ58j)7`IMWX_o&0g@oW2!KS+Ip>@M zK`?<43;+n`%t!L^<$m2g)6vkA#xknXlYn8HazrPPqgchly<6DsvCNLl(B7n#l!UGQzJ?CP`C`BMkAVmbG zS*l(N2Rjdw9LaoV^k0(46S=@fBj50#@4V947oW*P+&dU(*deGFa_J8r5(j4KjITypz> zdcY7kt|X*)p#<_)OodKCN)X5qd%+R6R>CP2m#X0Wl1Ofm1#y8W4V3WrmLI}C5eBMK z45>h&INWg26sAq|(N6+5giCJg<2?+A|NQWe4?hzWQEX6$L}*0h@7OGw#RJ5g10Eg( zjUAzj^4<{?1_R7XL5(m_@KJQ}j%8F8ZxBDoAJ$-23PuS{Opa=xG6Y<-kMyuUN}!Tk zScHvZFd?psBTKvxC>)R2#U6yWQq5a2l-Rn>FBPELm>6?fA}q0t29E)5!B8|B+Sdtj zTczf4o43$#K1HKNNl7Fna)d0Rp+_heLL?Z73}+%r1iPS&G7G&@1kSvzQ2=0UifqXjkOaob>tS;7)-z92#L z2pMujJGVF}?-u7BP^k$bih_rC^l>*OCQF?b*ahOZueN(F*Y_&R;!Jt*knS_S0-L4;j$9=K95f|3&}I7Re;kMt2+1Rpnpn=g35 zQN=z=z11Kl-?Bq=OZe}w3bNm)UH~AfB^{J3>3^M=@K&N&qJeva129V-sHIXIZW?91 z!!Iy!0uDv0)Kn2j$ln5D(GpDDDQ>N375JsVf>q=c2myLBg$_z!J)C!2gxM|_!pso1 zif2=>UtkquyJfe~DcUFr4v`jp6|fjlYVnr#Ev-^iiLnAn*e8y-a?xvnLRIh+L*BHK zC`xGzhMGYzgor@n_6vf7Ap%&?C^AqC*5G!FAOwZm1O&kcE=CdzA}Z77$E={s~{(cQYc~?ec(Dp z49Xy+aD+Z8MEL~@u|+s8QUn&15Mcp;s^MW0%u?Rh(};z)B_)hfVJWl(^0xLZgST{W zCFE2}9&hPmU63QE_KuqZqiE|b{UV}By#pySN?m{g_9`CnXiD+8*`i{JP7zl^Atb6) zJRz)$3JN0hd<&2geH4sK&4j6B?^t&a2vDRwM$-v;7hf(?$52dLlD zKshCWAOK8-IAA&jGYEkz5xmkPszvxfu?3GPfyoIcgeL9_$Cm&ROUMHSV(5|%uJ+b} zfEV=Mv0mb~2*#}=hZ2QC7h$7(Tv&p6n}j~f!uchgBKsCa08wX1*hB;^Fc+o8t4@JIE`riUS_EfbBaRWQAN04;~ocN5>P@M5p4lp03kx+RgfY? z0r7PtDkMP2u?R2GE7&XWP=aU*TtxqEm$*U91kn~X-r^Jm1VsR_V=+9WPhjw4q5HPr z9b`fI?SM;60EtSmwy1#zf*lH-w?n`MxP7!oygMb)pjri=rGip>K$muj9uWZ&V96H< z!=egBEfj(Qi_C%nJXoSnI3SRSMZ^VW`5hDvL--|RpXE^gqy_N7qpANsR9eLQwWXlPQ?9IXazM42i<}rAdp_s5)LWh6iEbHF!2^q=)Mid ztz&YeecMC{?=29HEC2}8#PKaS`aXfIR7`XWHi)xD8AT*Yq4sX;V=gcpfhLZ>2U##x zAZ`Oo)Pcl4fMPV5!_u~h2X3hVq0t9QU=oCNyNjVvDGosa1daNCOR1CxI6O#91J`?} z^UlXw@PY?K01!=FcKc8&z%Z!1q$%hT0is7}5DXC!iYGe6i6IEW5Q0Y@MGQt^T&zVn z7#>F!!I%VQ5!S~ywRlozH2*RDfp-5o>?2SOUMfBT}w`52JIJii>i+PJv zfPURjR}d0$+zRGhOMoXH1tF?}=)(g$OZ;35eFE`yXN&TRils&fF2X||MRm*p)*_xu z`vhHJz70U&1tCfndI(XlT51TPkry5X-Ypz31DFB|D&Q~;+#e1os9}Ag#f;$U1t!+V zx~NsOLHd}{f)*Me#>aVhrxL z2wBk@aS{Xvu-FS>;EE+AY8&-h!Yl#e8n}%Teat=~E_G5cgbGnj!ZC2L7p{$+F4AHQ zqAhd_1Zot67j+3ZSVe)T@j`>D6q>gQOyO-V(kGrcQF z9R|Wi9=8WP42J9DnuLD@k7*fA2D8DUho0W1v8!!5i{7fUYOPv}N$VCTo7Oz7mmhuB z@S^P}_b$p$9z3tSCY`nJ*3Y)CQL_BBa7tm48N~X4A!3{!oTW`uv*ZKXc6YvVwc@Dy zCF!Q)anokoYEyP}ld&@#;<YTp=?H=T&o}HR^KTa@#&>ujit2uQp$GS${z~YdmdUug$e> zvU2pyM2r*Rx=dz|(a#Am{9}uZEMud8gOuu6={OiX?LF;WC#Bo>DBFWOU2_Befm!y9 zYF6Sg$<+$0XwnjwFOMV!X8V^pEBuwAsg4=SoH*+6X(oqPn#Xe@V~8J8>fKV0&fr$L z6)u&{WYl>iK6yYIka?6Yh0UOGDg%Nk`rNo*VYONnZb4Y&)*21|mWyGjR1{+_ix-%O z5B4k8>-UFu=m#A;HOFNab?05z_1oP6MrD6TTvl@_PO zZtVVf<$33?tKQtdY6&X&Gn%l;CyI?mswr^zy+MwJ*zeD^Kr#`tW3AxBa9(T{qu)*>^Rt z$@OzxJiA*nJCUipymQpJ-+fH_#eel!ZPa*$QMEQC4Doz2hrw>K7_?TYSMJm6-3q5x zZ?M2A1k`T1UFwziGV4qZT;V8N;A9SW;WX@NGYh?v|aiGyM0B$W%wW!iRYw(PX#y!(`V^62E=Y56tjgt|*!X`Q0X@zRnd zVMOGVr~=GMZP4K4dWVwz`;;fWn|;Z?Nm7RVnEbT*q;{`%r+a%KL(5V8EITjgF^5fd zv3J-%ygIzc*sH(!?%%%mC07{{_zYT)JRtUq?N+1NrZd>Zeu-Tj6eidKh2Ehy>uq|y zM{Sq*Brb!=VRVQh6N!m9JHe;@>R(-zt@h?vS^kX7td}@>Nzs&Qx__x-abUZ9Yw)^e zuWqq(zGs`4naEI9+t=H6M%U;&FjuX%q!HHTwYbabBDCn za{A5aZuyg@^^qxISmIIZY}x=LF%i>Q1v8ZGj{U|n(q`XM@8Zyu)NhhZ4=gZ(+@v@l zhzVROyHV-a47{%0kR+^=dbdMvk~_Fb{w1BCVS? zGq6N`TK9SRXWu_-$u&J~IUrrX|K|HgompC@GPu@q*1AfY9$gz>psshWjZV=Q26ozy z8dt|-<3X**sPl{b{4hH?KEqB(0wat4+5T1X`cOow*TI;9_}&LWeYNgUS@(%d6~U5 zoUPocKdOA)c-4MgvGpMH=)C*!zU=_@eTZx*Kf%&42gr{_w9qIF{NdmsFoiX0^!dMzh&q*Vxo1gUMtz*bEk< z!DKKR;35xe_-3GyD>t&pZ#G&$2m~TCpx1ST4^O0vW(8&=KoQzx1QF;XY{VRr2!K7{ zq3Hl}#GU{JDar&4azvjf162nD7!TDn0F5%tMPmp99;g5#p!@)HgqlPwqW%f{$SI)^ zA|AAlz|a!~8Rim+AGLs;EE*ZwAny*KOP|FK5;ZQl4ZsCx9%Rv5kc>!g)*CHaw=5v? zs!bkogcajPBu+hqVbjC>H_JAazr_;*r-^qyzIK{ ze%7?xzSf-STBm16mM20yzrbyA*sU(Mi|!g*8_$k!k@BtUjfeH;T^IF_Yv58=eMUa& zK5Sp;N|F`?i}IAztu@HCc1c1t#hLG&>0crr_8n4=EAkcDvdhjJ^3%G*=1l!gTaLa( zTOIU`hq)1p$L_Fkoeam=GB3m4B4yh*S`O;ZyU!Xf?{C)~S3m7I>pE#(?U?CU$r97ZACf7LO{_JwsjSJ?=g1Kd8?%F4pf;_eL)p4(bo@yt;eSdP?5y3Xb_XA+yI} zw`j~l6F)9q7OgU8=#xX!!!x33okJEW3x;&US>~j~FLS8OFy32?@YI=gCY=So`ZoAH zOz_EA%tj-msuuWwAZK;zoJyz6p))v1Z{9z9l|X3mx9)WxP)?es`vWYm+3mL^$I>;KkDq_{AK&}OW=BBc6S<{sozH zuNCQemWegyCMQeIb>_;iA3f{5XgaIdt~sfA)_l@&+Pu_0-?%xv!&z1Csw^daKeewOZjSWfex@k`R>oWG<;oVTZR&16OIi(WVZvrnmvAUG9~; z^-ft}LV4c)=D}xoPO9?v&ZzsG4R(m{71%7|W$ql$J>nUQu~vC&^j!A>d6u%l-QZ>h z!yFec%u7nrLrWb=YN}&erFmo97#xTH?0nZHWg?mupPe0QyGqv2WCRr{w8 zu0FZ?-c9QX`EYo8+^JHA6!Ywj?oZ$QPxT=WFD&;WTiak9M7yJ2s zk;7<`!>K6z@V(YL#eSJj=TLk3$+0LeIcz-s@b$lW^Zt`hUew<-KC9a#Eq3I4H`ufA zqvV-cVNP1GIe1pTU%6lTyaCd+53W8w|Kum%`_+RVRiCskG;NTw<2mjMV~S(aE2pFp zlaC%AN>a`muLrievrX&eM~zo~&l+CT?lfkq&b!yy*&(}5V$^!|5p9^g*l_dS|5d#x z4vNAmgT*TH%PkhjKrN7ds@xj0MG=~q#&_D_vWxs-M?nS#GmYWimr52AKjkAvD=V5tU6EVuzJ_t6i^xp-b$Nxpi8LMQ2g^ zB$M#z2YC^t!O^$v=I{4@`ryakzxnv^qt9!9^ysg@dwu80ofq|w`}WE6)L{SoKw2JG zd#yqbBgkA}?vS69pWi#LzOH;#`~2=tz8%PQuTBKCM!i`RWzG(+_pH+69Jr5Rh7@L( z#$+}d74eCP%Aq$o)D}gUImetE56hf7qex6)lvW<=oCCnQm^QX{gersQdQ zzV_(hR@H9r4tt$D$?@|;qbc6$$N>UC0d_a~FPfe|*e=U|eAD)-{YCo* zd6_)V3dG;Pma{i{Av?B+riYe?=J{cM0P?ZK*zCw0J<0c|JZh)L;FiPPo7%3oYt06$ zB+3aYY+8$59;QS#+n-Q&J5mF1KB5KAM&BaY*OeMg%jdQ#QVTJU(#P zyH7p6_w+9he(>r2UseA6!DnTM_s>5$VyutuQRm3Z-3zjSC8E(<8CFKB=Z1RNb5eCs zaq{S<^|JH2W|h23+8Iu9SC~<{uQ$P1QqF7qR&jvk`;K^XHCk2R-aKG?emRHav>w)tPUpz)|-ynZtC9r;QZb} z#p_2uu64Ne_g(qZ00ZL><88k@lYW3|>Tb?D4iRY){7Ha9vo5fOWY3GNJIf#p`3 zts0lyVOILpHoaBhf`Q(pHrfmT*PARRRX_kAh0bDEdSy11)nt=fty+gVAPVsU?D%M! zwm3G+iu1#g2rJ5uk53C=D3ymebBu(@YS9@W3Gs;hyu|SG=#3yS49J~2o5tvn!`LAWh=MYY)TOp*;ScWB?T~b!shS>g zEwkPVmp=elqwA6- zNnCDG=(Ng+GA^B`&h#($?)4mzk82OhHXokXzUq43bX~nuzf*lc%?<7j#6~6=agERH zwaDEQ=J6?Jj+dqFwr1M0)sI^*TA$zFt2!!w+H%%$)wI)*YDo95iqnEgwOMIWtHPS7 zYN|InG)c=3?F<|?A69Raom9W*eo^zX{GjHb3KIK)eNuQJGCFDU+WaP!lW*Wfd5h|W ziG{)B$ZYRH-+upLd8RsF_UXeftA6y!>yJ)9x~jNodC|2qG&LHU@Eh%Z-E{XtW4dFv z=V0WZ?yLXuT%$6(_>;)B(joIJbv^-%sbkY)snM9kp>yb*a-YDjwChY(z22p^tB`bB zJUjaB0iMyau--h;uj-iwa2hD_HSb#DBm&(+1LU|0gGkMZ zOJ+^;{8V3xwAi;r+o9}tE%l}-8OD+zP0rLUHo!fYIj&3a0>cSPEGn8+#+ZSL@YoW6 zjhh;n8c4Kl4D1abcb{}F^d<*Wiipvt6*>8KRY;ln<%Jo*0#jLUGv`(c%3E8F^ zh7OCy#f$ObB5jgICuZrXkr{SW>@eyr{7Lq-C@%CXoa|)hLTd1;?Pb~Zo$DXIx%;#) zOIs%g24E(_mzSBp*F`(eUE#U;m4*{_q#y`1_B)s(w*@)Uer< zr7iJRc@c?^G1E6IoK}Vn0{u{6cx!x*_PFB7$LII1s!!pr;k-82yfLsOnAAHpN{)#a zw1oB1-tnW8=> zAGbcazgw|hbJPBL^%swJns-{Z2G&OQ$ZK7}(I_`&_PcB*v3_LQ%T?UoxSq?=4S)vZD-ZT_pd*G z)wRW)mdPw~t;_1P_$&$+MkKvvcbU4~wBNZ*Pe5SdvR`-1>rC^C zGz=Dj;kDN5w&!JE{@K%SeD$aQaObyoe);`p_bxxW?pou(RJBfHlLR>Vp6pnv-^*~S zz3!0g^n1w^B}<(bS@jmBmkU4Gi70D+XqFvRSzIExg_tFv$EHXpM7-k z!QTC+jaU79wDmzZ&#u!uQJF=vLZsi(rqwV z6=tp34)f4{txxPwI1L7~&7^f1MbU{#g=wm3-aatfzR5fqJn3B;O!TbxZ4GZza)WlI zNoS4fd`9bpmuVea7G}hYgX?XJ9l5T<-iyZ5+Eiz@{g}2lxZ4q?JBPwB4KlB`E3_J| zUY$_;jqY*Z;Bwyp&50!2FH>o3#)vLxkjad^i2Vm|#?9f0pxodR zdDJE=GMq_5>;x+b*I&2F1|wWR0@rewDHg@q5lN7d8kuIz@cat9#wU!lGXhJKY=Y>Be=&3Ma#H3A{|WFvnF>+{Ko_NObg~^Lfj&yBFoz`opes%F~Bu zj}FU^JM;7v_9`Xb6CGL=E=s0lCcR#5^l3a^quR{z@slH)jMVr7Wv**#Xni6-u;07d zm2KP|T;NX$f{Zw2Wjv+|tGpJ0ooAFsED?Q@Gd(oZyWg=lx=CK{O}3pqc-4BASH2XO-}gPS*$vj+yiSA3a?gc(gq|!saIiy2~(47Qf}AlVW>A) zV66zQFi5(kv>F{c7XWQ)hZ+_)U;)XhvuGXgb@IS8w%!OcSUMZbIU%!EMr85_O;)fR z0c+8mqR`*~BAF=gaScShkg}k>QY1zL`z;C9El6>B0d!lTlwIl`=c9+W8Z_wJPKyX4 z8r&9c22sHRLiy-vp;L5_+*wurs^5Jl-#kP2ip_qb-x#7rM&l#-;jN+V=3MQ1<#Fp} z@6)D>rhMZ;!~Q^?wnK^!BnB5`F|*I4HA!S@t088Y;xF_qkupseZRe!3Px2LT2i$ty zaaq69n6BNTWZ2vE1&)X77dqS~uhBE)ABoT*>l)f^*{$EGJgdFwdQttXVz1`7;xXx@ z`=n{UW2QSTNXh0zPNi0^a!RMv)9j`GdFl#zzxS~F^ij4fSN^Q^rt@j#S@l-cUdtZs zs3$WxIhKT_h>+fE;u+Z{7$VnrnXX)Gx^chmobd`3F%eth@v?(Rn~>t1&}uRm$r>`V{LE25BU==rhUBhrfx z|NS?fH(I?-FB@J}{jcx+c5sqA%Zx~^Cb*A++W?p~g5^!6RcA9=(H%XW!qpu8v1R8_SXz~TeSMOc^ z$uGb6Z_9sm?`7p-&34ss?{W9`sE2Eq`fvZ^ogFsZdV6^Z_9SnHwlp|1J_)zn4z*wE z(zp~3g&SOXltDdAEg1|Bi$M*!@#rdfx8bb%=38&r^y&6o=hM#DbvJdH_Dt<1>9X#N zAMRCTnsV$_(W=lZg{3u@))n)qrx;V@jmp>cUp)Hqdwche9$fY9jjfMO4^FnP^=uDb zlD3#|DR6UECl*9uYJ}zxEQ;0`dv#~!2aopKk9)4_ud0q}4ja!}Z|Yxsc>2Tr`}_RL zH<%UYBT{5pM(dUbG;p6}QTn;_ zb=k?mtE%&Xi}KHYa8`C!f7pB0d0ut+(ea0m`?t86@g&bFv6zLk$^?IMFfz2>{-pWM zgI|2(i*KHO@Kx=b_E(kXRlE0IJb2pry8QX0mG-3xuTPf{%y%cd<9(1R(RZ4U%Xh1< z?tW4I`Q0!6?Db##;yXWWzG--V=jmU({PWjMmz?I$x@P1sP2@7bJ)TXkGiyvbvqc@^ z!ijL>oER5w-NZ14shzM^Vm7Od4uu_Vstgvb*~y=7UrCOickhz+A8g-0uYcC^viWJ{ zLFN8~=hbh@KC91mr-l<^ufwm`2D;s&GtA?W$1P7ky8hto(ewJ39alAncP{TgyZ5T~ z#lzQ+HXdwtXQcD8gh2~KKXaWiYYYgiLW>Z_^YKhyu4U=|dBvxVFS<`#x7xC`7p(`h zJ;}pgc1)?29;FA8JB1adKlNUbPZf|kG-|IfJTWD3IC)b-KaAp5m5(2k`e7kd=@dn{ z9%+Jh@V(DD@)^qFuuTx2SmYhHU&H;#am{J_!N6WaiX83EkL?ca_b-h4gj%C1;PGlh zLkr|JN^X2!Jl8qbbnsojO9QBJ@d(pX68)OqAyPLe)5Hp_`>AXm4j zY-*U$9FM@7pGWPIO*7`GOMNpuFU-3^K5z0crv|jI%YIXH&>Ljy=@EXKzdxAdTcmoM zV$v9r#s}lndCJjnhPBk6q|Q_xb?mcOczz=cmnx?X7OQzq!w-Me6nAi8A|%Y38(bQk zWBK9UMPo6-HO~QSwQ87|h6QxD#v=$xY#N8rVDQVLjM0_Zb{=FKv)nmZ znmNOW52kxI=u7-0M`DtzLNGFW9TG3kJTcE)lPuGhX>rCR^{D%_=j_hGgUy=b{;Q_1 z?!EZI^KZQ>d)oZ0>9{#xao)T+oF7W_^g_8on$RTFabA!UZ#`-`7~Jn!>z!mKSkua+ zBsLOnSRZPA)*T445(2NvDRHUIaB&Wdt#)sBWXYS9^zdTuX8+<4P$O{xOm6GdVP1?q z3*X?m(Ih9v$h`lvL7&H((7J7Qxt`->td2dc{_9U3mmk)gbewfuS3JJEUy~VF(1i6N zol+rivX;3}8PT#`yHozW_Ep)BKe_nuwCb?pP0f#Lt{PSv7duxtxv_oP4AUdE_#jEN zYxFGV_(K0l*AeBQVZCj$=Az?S%d^V!%Hz7d&iq((Rf4c;Ov#L)gNBaYAnrbrha4ML2=0*qA) z*Kw6c2IGSazUnQ%eek3wHR0z3SwW4@YK!zN)^AlkuYKNeU4K@!Ren_Yr2D!PijX=s zTK1_~`d0T;Z?Hebfw~~MTc=Pe?4nuyJZF_M+q=?r+AXHym#^O$-Wkl&A|ug> zxXy2Jn5hn(L0!j_U^pwY#-YZ$dt7$+V>EGd+9b`H2+EBZRx? zm^P@G>Phx3_Q6VGH!M3=t(WhS9#funoHXq}I;%KuKkeJ<5748-Nxj`?gO#q)K<`4| zt3SNqcvS&jgclYBz$r)Bg8MX^NB8ws^jq1V--zdz44`gZA6+im+n?L#d+kf-$ z%Z69w$K@9vziQd1Z}i7~|NI}neb%$UiAaN-=y-zf*X!(NjYV(J* za6Mehu-jn@IQ@$F=p=Q9x<0WpwAZ-Sw$XATBb-Vhi{aMr1 z{cQbu$4TEw&rw^dJJGYiSe9&X-8zFt>1WR>;U0V3OH1|dk1cbTAcWQGtJ*hhFU!x` zmMJT}8JK#!DuWrGj#cH9A~hZx2@gaiG0nWpr`PJ_p`qQLJ=$7Js%5SDvU{JtMO`8- zv@P>0KBq*iN{?CTh0B?rH8+-k0xDQO6vmiKLvurm6UeHKJSg|7ta>BN3H~Us#F>sT<%+2;L30ymI;|pu(9T(# z*c#n$%aJyl;O3?3$A9(Zw_kt%&E4lM7tL37D^016b>6CKR&P++WSX!vWD7IngAw}l z@aFJw+ts7(2U`t$!)Mg%_FVT|@A5!Ka{q6t=e2T=Fv?2upoT}_6UAA{u_=Cv4#heX zuy)9prKLvapd`qp48lS^ECl%_K52|GH@eb%^^O0%Yss$7wwzY&RGd6~)_zm<`G=Pu zy}0+X@zvd*e(&Z7haX<{rB%~9n?+<&7$wu<)W|{gVa4M+pFQ}+Ctv+fzxt=?ndY;W zRZdWCaA>tQC9DzaVK&sN)xzDIOY6`=CONW1U7;n#PQ69zlm~cWsoP-FIHh)-dg{S{ z_;22P>$E#!P{;KlUSz<@ni|cG9u4eu%-7^APWldqp4H`>*4j2{8?2S#g#kOm&!5sn zjWFL}^f-=hmR+_!h zw@wKUMknTFD}t5L1?v1j3MP`*6lH%?p47rxJS>U06i&U-tcKftSQR(OT`;BQ6-Okn z;GuFVEGGChhxiT^EMYVv-%gEPwEdFvMt&AtH$T`r=-Qf z1V5DoN8Ymyy!jenHh=EVp^ly?bI3s3K&Y}HH+NE!T3mMc#*v}dS16m zU1{EL*_&9SW+?t4mpI@K*gaOMo9z_Nve($@u9dEp?!C6hBq*T0yL0cj@;T*6+sn$6 z%AM*H^8U~+d3wwRGud9d*JQEswF0LkrCbuO^(?n%s-KmA+W6$b<%6C2-Jbl|)d@>%Qzn1>tE?fjwOt|JbPw5Ldt#ns%MTL7Wt(PxMss;0+x7SWOa=L6{NR zfUJUn6efN^Y_?cX7Lo+PbsQ9hx+OrtZv}`tD)CkfkuZlV5N2^BB`P?l)D@O;LPCV) zSx9CCOaAaQ8Z5}Hn@t(!BpK5jSdNDITa`oQf(7H*k>#PJz(M|b*~!=jFUOdo$LL|T z-RZPTU84@x%<%r$78LHRHEon#lF!LE4Li*lnEBox-0oeX1_x&8N!^q>V3Zo=8o`WY zR=(P|G?41uCLK@@TMjDHb$bn0J*Tk#w%(R*-lT6#lW^AV4Kjh=*8T+(dm?us3tTI=J zw@GWQo2@5}H*HWm(~|6%A6VgUur^1NLn+eQ*fM`crFOG})NIFP=UL0mCnq1CK77&g zvrqo^JD+~=%>M?R6M5+i4y%`%F#dlsyD@n2qT=B5XM-O4vK7`#Mt7{ zzznC>YL&;i5vf~YHERNb2q(;q!T9C2!f*`5q6%ef(0TlwfB0Ab+xLD{chMCecK0oE zmNZF=I5FyG&q6%&Lu)Ooq-@J|=XL9gyNCC;?w^coN>;`b9JmJ&2MvL#fA;_W$Fv) zt25iPMq7jek{A?B2V`!w*59#h7>oDJ48})S;1aM=ztObVlcVj8WV^T9)~ffL4@YvN z>&)2bd=JcUS@e-%KgS|XF}B8X155oe^1^VMdDZmkgPVKjRXby=Bf0L`-c;KLYeqdS zh>FcjkHBq<2GlWbfbS4Z%4da8>_Lu`H<&wPX*N{X4mSS!hkHz~K^f-FaHC@hVF(r$ z{6e?dqV%X-P@?P-hq(z!Na9hrpqd}%oFIFJ+A6QgrFAm4{^HXWi{3%Aedl zytiBSgmhE;McGmPUhQ!e+*!ArRpy#AJP z;~G@gKdpOH^{Oh{JlC?!%y2WpfLdpigvT<}qxO@Bt9`SB2jug{7k6`w>-Rpb_)+Vp z^;?wb-b@$eXJz@(C_6DeBXYo;k6G!KdDO6AqP5thL1Bosz>ISON}C$;C)j=v5d=6> z%$P7Pu$h$+=GLG7A7u%(X@mK=;nPp{>M{+xgV~8}_iA5~n(f~l&GxQSU4zMqgbPYw zbb6IqCr>ED`uNa1X`}JF_IcHBzA-yJnC?$agcLq4%u{(_-Vh4ukU`p}w<~=7h#<;> z1V-XlhQ?!(sKBeWLotWJr1Yx1qL7UKx?y`LA~CC=ZYihN#wDHUWR! zMmmW64o;9QoGhC zm>ydhm>pgmO$fqTn>@^#p--_E2UhR=wAE*Tx+>o|Y^9-{wywcy=q!I)xk=q?$XA|2 zHRE;LY294MbkBw)rB2FhYNc8p<*xEpX{)5Q&Ygzq_KT*g4|hM_fAD$To7S7^v+|wF z!`5BeLEq{K{0t|8W~dmjOjsFiIQ1>4w#qea)}GW|c08-RDm$pWD1Xv<-hN)cT))`7 z$=l?md47>q05gSQQ=FR^iVZIJ?9%qTpz5}K>;7}n)2^%5TuZjiap?e?99&Gt-R7Iv;2!VhsDs$)-KFVtd3qI+JH9=mQlB25XE@v%+L z{J`^n@^iY)1x0>7iC+PCtFZh47j&dFnz*1N=@C4U2F-+kf|6E9m!R$lX0B~e%867l zp$wRWf@?lp?lJv>9;%F4Kw9j1`v5#bP;`j6M7Tuuf%j0>?FQ2&aCi?C-$o(EO0ifU zgS{ie*q8=thzJL>P_SL>qF7X|lm|3g7>@BE9fGeZvdCBx9wNf^2(Kub06_<>NQav= zNS9#FqxYA8^x0n>b{%qZoEeE-VwLNHu(>Tf6djow-03~&-*4HiUWd{9dFQLfC-vKP z+x2?`+q6A$Y$!I8l!r_{lg7@nF(+ARVRmq@W4$?Fec5`^_Vn(4`F{EH<|iGOjl0du z4Uo*k^7D+sA+gEz0X0+-(IT|i(AMbA@NQeaX{Bbb@pRy-=zwEuNe^Rzre^_-*I_}$PneB@9FA5hl2`StHiESE# z$K^2i`Bs@-xX7F3t#mE5Og9}t{ppj2i`slmu4#8@cOc)F7>5xnqz+lEf|Wn|a?GRk zi4v@+Q67YPJJ>=bh1G(tjedAHT~P8Gg4-9<}FNCP}H`by-TDkQhw@2O}+xxm{WVXPUM$x~8+}JY!ZylylhjS=&Y9 ztVqY5lt1`M+pHYwNW5zJ*x~j9s*kKHy9QQ!AZ^mYOg^x|vlAA#+)!%nQ8`VJDGQ^+ zTcq^BO3k5FzQTmHl9t`dY{hQJ8TF>+rhd0>uMsjC`d-~?)o$HBdrlcs*pwQvBFsz4 zSBG+4%P_N-8O&2o%eTs)6rY-(SnbQSZq#gdWLTSU6BuE}Szf0$$(@?_Ya2pE2!fy_Pw3)bAB z93JNOEhd{q5fDK|y;EQl|Lt)cOEpRJ2%@-VO!)`&3YM> z{txW4(j1QvY93TjaphBohU2tp=89m6wcfm3moC5PJ?lU2-t1a!+w9$dU483={_(Kb zC!bZt1rCu~1shoMqPelHx}8UlA3djR_GBrmj~;*W%ey~q*=Iib4}X3$>~O36lAr+I zUX=^#s-O}8-CAmas(EBvDbxa3VP!&ZutUKTOro1CD%b`ojI-t*y{0=YBD>JfoEgoG z?6n`>%a`r9AN3y%=cw}?D|LIlN4@9Nxv?PItG0&p0cot)OZN$uL@UCj?sUUe*^}n; z{)@(3^H%+N-LtwU9UJWlC{|6$mU#0*t4e1TC8Z&=SKynNqHa^RMsq#chK-LNm;LPF zU*G?-?5uch zTPLBwN8%IA-1}*hO(D^7axjs*+nsM(YR%W(w0~OhvI2?~yE79joF!VgYoBSgM4<&8qWh z%r?187=jz{usEnu#MqO}h;=;u!TrZ}N{b|!t_iD>-OaIG;1+fci z=AjN(4a@TuShO`5V4YkA%X3Z{vNu8+6@&yn73{u&+IY3at}xrBQPv!k*C%VwCg=&y z&d^cMaryOwt+LCCo4zaZPG_bi*O(j1!)@wfOSU#Ywl1ELc;#{pYlgET-s|2X&y&;S z-M$0zLB(p#cFoJyXU)&bV3Dh76>6DQr5+8;cO$h$^Ab15$ab>Vc`Lk?!9}Q>JE?iu z_*v}{%qp!nKC2XNjeGQF$@Jhld37uiF=|*bF46HV5W=0<=liv~E%p(rLAF zogkr@m9IeU#X{F%>jhXn`DCl?xaN8H1?jYXt!thHzbw(B$R)KysiehebF1tO^F)xp z2~+9W&b5|}%4dyNT`%rkJ~*j(TKT-=xP7ZV-V>wGY7-WlhZSe)ZFY0&&W=*;RcT`r z5w+e0H_I?< zIf5}0W|3ft4g~0R9xna}0aGUM{e=twk-{|q=OVcQ*dQ_k;)rM?C|rQ5l!9;p#wtOU zkc6oM6m^RNFj2uMbO|tKu%J>fhd^$DFa-jLs$sgQEd*X_6zQNGLIFJ%8{7{rEr3cq z0*_!r5i!g7Zq$Wwm>izs_HfIK*~|DI!V8;{B5Yf$?S7ZJ+7bui30UsJtd$wLYubtKX@+ zARYGYb}n{J_APMc6iKC1uT;q`f;ss-Z-uhhvrIl99e15n?LOG5xNN>5-87yxWNUH_ z`QhE6+(?uWW5!foqt_revn;|%;fiQ=EZsLxg5hRxzxS+Zqdn7fOx~sKl9oH;-6;la zX_Lm}7O_#O3>hNo;-;bu3a z)0j90c1)C(Wcv3S*PC`8!kR|=>yM7gPVT>MIUm^WN%bZhmgvj+gkKh7_;@fFSXaOG zOsw#lRN>JyWd-)>7|d3!N9vZ@p$rASd+`0WXrZ_oDfPENu7phWzqbjr`0)LM@~AOpoRMaSyfTd{ph)s_O-B`* z&BqO=qpPeGb!NQo=auVhxH*y9jgUXU<_DSCEevR-K}moeR%^nd0B?!|shUphfL(EZ zQ3CeG>AlP;j#uu|`#ZOIP{xp?Ei-m{p8X|k*RMP5gwceO?Mijcb}z}6`77Ko-vw(g zHoMWq@r`%{licihx_<{2WcOir?dzsb@7z4tZ#eAT9^CFow^5&y?so+|K0nlCnK^Qm*BUX;auUp7&kA*&y~$i{n}eNq>k~QV z=5UOX=w4 zvDyCo&_UCu@1K9TQ~jgz-~8}*-}>@{$9EsY&d@#bO6S5z0``fg#C`|MLrwN?Gfo;m zz4Nquui~ohgmhK;9i8&Yse{%m3os#F^p4Rz}!82X6{g zmP%n0DEwalvr*~eCPx-g;AmQ@v{H? z@0*udk&&grSq`j48?+7?Y~g|bjt~@1^W9o2dwwX%nu0kluW)ia$(mHah75@ZDW(d*d3|Hzf!`hV>{*6Rp zJi(ZTe~$pCKfW+A&0OeN9@zZBPln}A>J(*@v`0M{*lJ7m#wc6NbA?G+ImV_H42c)&`$asvt!QbpXs>w9(w4Qf9hCABb5BDBE?mc2I z%izB=DBwQY?FtAIgR!A>=V|}e=z3SYV{&AXzb4ouA3nIK8M*F@E1YU457|3C$(kLX zVkCHOm<4AiIdLKER+K_AEsS#G+_(^uLABYghJ6}ZxK)UVyw=t)%a&*X#gby$1phIE z1(kaqW1KbLG7rD7L&&=KAdgwB*`*%RE_d^9R`nFH_v$)!3keSh?*3wAg*ryBQr zu1U`;&ngdKNY9L~k1Y21XwiwJIbe0Tg(k6uy~u&O4Ol6d?%C=%p`3Sa)z8)E8_q}f zDVOjMRdSVkl#{-r!PLNXSBA1Eo>uvl8m3vInN}~rTzRs87Iq4qbUnZKxcvChtM7eP z_Qiu&<>^O<_g;~nz!K*S)5~=r<-Ar=m=PIC_vA+Nq@$|;kFNKCZY?{{LnkjUYdtUL z#ZGL;mSabXG%6!ay>sXGd)a&M4J-gi0wg-o8$mRpcM<>r0xVz$d+(RG@V2>kiZr8{ zkz`4hWG8ltUmWXYB`-^U-#I8GuO=kdIeY*6-~T=Ufph--m+zm;TrS&(cYD8Rp?tc0 zkv=XQMIb`I*r+j>4TI`Iwv;>it$n4?D^>XDV>Ca61vVD;CV^G=z0E`j2 zxCnpmv*AXk)yiy&0T_I>{ezrH#|YBYY+AW~kQ+k8*Ra?~gwaclgnEj&HrS@L11dOk z@z24oO+5(c9~Ep?$SN5`Dvv~uoMD-}&!!@R-{d-_+6Egq9Ld-k>EULw!dwX;3?*`Y zpe85>6>3}&GJ*_9`&7W-rk4(?^&&(P5>7(=M@$k#pCEEvjWG+~U_?M2LJ0AAGfFLD zyTF2pNW6x&jDS0pMgx0w{{S{1gtJShFd!`-uEhaF0TCI0q|Ha8CNx6g5eaQBsh>cD z!-NaQpp*u4PMJwH$)V(UO4TB@qVpXP2wI9xDplwuR4ZbTNXmoh4u%O_oeVc^Bn6sF zfBDv<=C$6X9vFDpW*y9r7QT&%-5xi_nXfy(dhq7SYoFwOod5ls_{iKm$%pg5dai01 zpW&zsm@-n8SSA(?t3p!5WKc#MS8H|}_VTxGEo2>JJ*<3C{2+TVcQIp&0<;rKnChqc z`7VtUsdkJ3jvG6cna;&>=v_GnwTE?&v$hIWbC8%(zgxam7%Ge5t`g&q_8IsCe3et} z(}dfD)IjTe146(nH*U;lFBd$ld02k0FkY~jwN6=~ZBs%W!_*<6UGG)s4c*#K2RFuz z(^qQ%X_0lWVz=?$?VX(EJNNSUo42c1Do64snkUqw;&HZz>TM1Pef!0X18V9@6BCa#={cW+%Fg-%|2%KHK)3-jm@^QgQ&1QMH zEYJ{UiCD<$lx7jg8xr#Vfq1VvOMt^h*pvKX0*(HHlPZ zg9wja#oz$894a*o88DthLsP-8O>8RgsZ#?}L#uB?}*I)hhxBlwN0b;H9%U3C@wF^b_nY+1%jhmFw!M2IQc*c|Br&#MhOwgUs+01^ zSbo@*{gnV{qfb+_a0t-p7&Y)y~4FzVEeSJH|^!G zI3YA@B?ALUG$wsZK9)_2V5>f_Oz9B15SoJvWvSK&;LH@7)<&u>hVz`BFu0v$TO;=)7*f?R9r+2u=sgMNP)(Y<4hN}balu@3O zV+AgRO*Yi&=Nn<2;CooygNErIyHu%^SXpD7fXv-A)I3vpQhv}BZw$6udK{cd?sDU1 z@y?Cs-+4dpUfEH(o9?Afu%es@3!zr}@9palL*^IhECQzj7wF;yk=O>HBY1t`IoBdk z2_Ps)zEtT=yy1??s*Q@JrWn%d5xdnfpBsqYgeRtIxhKRKtDnf+y!5>M9xToH*L7J3 z)WaG(BS5$IMw)kPkFsC9wg2Yb%MaSNX&ZIpt;6l3@Lc8mIA^*~iuf~uO@@#wJkH>4 zlv(*UA<|IQ$Z|p~yTFR47xKS*M`=;G!J^2>8)4gVhjG>{I>c*@_iujwjmK|3eD8T4 zQnP^mu#vmfJ}nB z(Wg)VH*APG(Xd#yUb;w$;jU{^;KQOb%8V-2AmTKUwJfx$m4iGN-zk9azh{&=)Dxo3 zf9r3qP0(-+EW64t^UF0Vo~dS~@}&Mz&Oz})9&&lg?!%1?1cJ(0fnVnw5L+m&t^jw2 zKVP+<1=mB~N!el3Y+|=y!gr?-|gQhcx#2+28 zXE7>` zas%eb@nMAN8=psL`>-M7zO2F%1h|XPkf6mONeLc5s5*r5l2kQFbt9>DB-?3Q4559T z8%hnIBc+9;8_7YUpJ)>F-%ZNPlX0%et<}lK$0q8 z7{HRPLIM#2QWOO8pjDwV3jv!V)*>dY&n`FCKe;+m9qJhE3(9?Rsi9Lxo#SFdv z_G#5o%~A1c$$0Th>kMnL2fGoTS*EqATuNuNyTt1y6W_C7R=hxtc&%LSYH zyLEWVRg%Hwl97HdGy##C@8*nSH!)KktDh@5M6Ua8_Cn??oODl09_8)ktmMUO=MlQ+ zXAeSQu;}#`gNbG6)N`V|Iaa(rUOJbzlXbW1LDs_?029dFrObE7n0khR>QYQ|HX0Xd zCz|xWng@kXGr#z!pL8Ob#wtflyui+L3V=RCXvg?0DUH}1!W9O~IW%n~xSF6c^$6A573&+84KoKhgOm|oR2mUE zuoFQdPoQU(5-*r5+{k{K`$6{ecTcXb-QI?A{J3F^?qY;HA`&k$PFh(0&U zTnNhm^bik$Ttp7mA`Zs^X2#mMv7w%@wXUFsJ z)jX~|s#>Us*3VED+L3qA>ggEb+HD$#($ySp@FPC|S^o2D4{qLj>(kdizxC+$)AtZo z(1^PqW0Yy_HFSDa0j(L9e%cUYtaYXjiR1b6CF_NEJ7yU00o0AOj;KeKL9t%wY@KXJ z%6b30fAiK_j~)O!HlYQ!0c5Wup;u^w1;r`@=mWkX$R~BeCpp(R)izl_(>eh^m&`42 z3r#xq{Cg)NU8H`YGG07)=lwe$X8+*bqr8>Eji&i-e8dZVZ7$Woh+vAoUJpa-^KwN3|oPLs^Wu>#CgJAnLh=$1&|#5UN9pjWHdRQ~x}2b@u* z$2ee+vUPm*uxy64P%%vjQfAv@yhzhTeYk$PVY}+?ofntxzH^v6BNclm4jZI_MBPTyy`uXCa}|>X3vFwy`>o^c z!*tkS^)|iS(W+;ATi0pp%}2KmukXC|EbHT&KYjJ{Z@qu%`K1@-2c-vD<0Vs-OTE)F zr&^%_2FWBN&YEh0dy6_>cwDz%wO(vv=(qv(&_Iamg7!Pcz4_C^kWyk7_9KI*E5ZiG z6;MiLCM~|cJ#IXwoidxy(>>B2rAFBv8G#k6#uvdSwBrG!8LT{%XhZ6d#M#%62Z%)+ zLP*(2OOP^2U83xktX7ScPE{^+AkN>#M*y-l>bHmg%`V2K*wsK{!XruYK z?yzjNEMB_Wut1+{^|v_~ZY4r%ZEC$xDc5nQIWg80B}5yh%rZuKV@=WOK=n%V8vMb2 zuCX~%zDAp4$GF%CFo%U+*|0{g>g~r)C(4_z->5<&XW@R`N$&UGJHB&p?M2o}$&1T) zpNi(%R+wuYQ+)Y=P{o`UjB^1gSF})Yn(?^o2-!9BnRko#o7bCXDML&f*90?|&eAzT z^>wXO?=~+sgj$S<0XzMdhdS<%Osj`+*4Zx@ubrfhb%p4_Qze(Y`y zQk&Q+@H0kwy<7(nwUE5h=RlyI+KhM^V*gFVBmt$5Fnc7F7D62$ylzSLA)y_RZaA`; zI9wb(V@v#L)&HVo#ifd zF4s<1OqXobZa3@~BmA@k7N3^&mZds>Ww2>N6cdd~EI?KPv;(|h-Cj7EV{J>cmB!Vg z$^3-^1X|P|mM@jhm##F$sY@*(JmdWQp#d+9{#-R1DdwUWccy6qiq%@}2Ed(`a_0-S zD-S6K`pFyj zEB2ZW8%LOSzR@9=qAk`IfHy-K14zPTPf#4{jxya1F6N-!5D`o^OqHx=J-zlm?jZNBKKRy;zx|W%{_?di zUc3Jq0wqeGHf*pJ29+V@9mKNqLi@XXm&wxN3k7$%* zl#0!N`gd<`u@DjA;d*&)G48V5K>Kv-7(Lt-;#;9ln3c*wz&JYj0R|jCOphARFr+?l zZ4y_1U$Ase*%Z6|_9+HvL6+dsLqUNF}%Df4MOQ00Y^uBqmG)yH{H zu5A=9XTt{Q7l}Z)mmz3ZYE>wly`xRbmrr}l0p?20Zv8^ee8JsoAC#PwAC--? z4%g2%t+eeoOt4&RlTI9E0o9=1*D}(z(y~)=eCytuFaF)9S3ZC9@BjSsw@=@EdL6lv zZL4{+S<59Ga5cm@I^4s#L1t7q*)m@~*EHF%(Xd~Bcys0EV!;-DQaHhIv0aTrLYMBH zU)-5NCIJtgKxAr}5I6-JJI^KbaLog16>v*sCg6Ko)JTT!*Fi~9LA94#02E;s2HWCS z-sfxAN!8k|ws8Qq+&RNpZQjc` z$k@wzSiIA)mGS-?hTXjN>L~<7Ii<)yFo}!^%)yfZIVMJZzlj4hFy1iFs<8B#aG%p- zYaw#;0N5@w!&<7=*51tuwpnysf6GeKPT4wgMF4Jr&BICNV%c!*1OkL68N)(jPoO!< zi*Y@Yer(qzBZ7b^*cz>I*FO4ZFM8FEJ_{@&!@U3i;QCnZ-as#+q2PQJy10a47Jdt( zK?xuay^-%@4%32sH=JR9YNU0d(z#Lctn{Gqpkga0TobEa>YWn$26_UGULulO;Cn5}wP{^^a+zVqU> z$Cn=E-!HwNJ5?WUn(Y|pMFfK?zLqju^{Dto_6Kh~ef$2~KX~uQZ~y#{KmO`x-}w2T z{QA%S>#NVRmb0gtr&*|@&^STyvh`NQaKEF+)NSpI(PG>XE86I%j5g1*kYhs`tBFxY z8I#@dnlL-(C%0m%-U&ctdWC~BplX@bN|(@z2rsY53B$HgsYghagJYAMgch+4LB?u$ z`H`jyJ8Jv>SAUx2YE@f!<1llKQ2Z^H_J!7+&b5k--1VH@Y*^r`H%q6gXR8)_M|wvD zY9&ugU+7(`dw%utTc3UX2Veiy>!01YpA|2R0}5e@vR*k^Ii5e?zSt90DSb3&55PFM zQ2cAd&C?CLl+}iIbvk?-H9T&FOD53##4&kZ5W1oDK zC*qEXCZPxsd-H^ugm|bUppI0RQhGz;oKQ^?dH^wv&;wrjO?OFUft+y0LJp=B3Y^K2 zvqO@QIwj^M=A3IMnyKT3VPa-N=!J_TcoTfd!HcKF>q#-;Ri$O58YG3d?i5o}KmI4Ia{#O zvEI4dJ;ZQ#xm2b>lR-F0voOM4%M3&ep_a)~)^W{A#iPv4{IyKv_tfu}#fw8VG3vB< ztZxWN9!iDSuL_9Akx(0H*sR6}uwd(YJYzTiQO&)QBVcF)o4b9Dw%+cejkE{F4!s%h zy{)D$w`fu{(=$^wmA7{1LCK?%XE(RAmaA7=R+^UqwmP4=QnSLFkbC8FeIMV5ZI?De zHS!QPO^?&Jix1x2y0l)m)3M*MT`^lSn}@KDy6v**njqCTpmWOt-9Cy@+;4KBUdm+E z!#~~-T3LP}a@qKLR0V-W)6g%|z$V@66e1*r@8%g$X+a822H+thj3D13?{^{e!=(@p zGlI0fdtWq1ng z+UT?(N!ch-4R#&B@|(-^-CmiQ7iy2TjL z-Py`pL`{Sbvrg}9R!_B#bBBa}uD8)a^-0`1bL8LtB4=EngJsi?vNA&gclSi&e5+q# z)F4?}WgM_7;jbDn!OQIux>>`VA)e6ykZ$0z2?6`>X&YRtx}W*s8!MI5C0mpQCX&F* z$FFT>KL#@I0>7WHXAhb*ZjFs+>-4p(bk1=@oq_6T&fUxx6(`j@Wow0V6>D`znV-FR zaC7T6AcE=hbPrdyxy>NV=ZT;@StI>eYqL_r_yM5P!s8MyT0|a zQV(uh2ypJ2fISru#O}56`Q1gZ{LiclrI6*S?5Si1q&XpTW zhSq3xsKLs0YWb7=I0#A|mQS>g)kiAhlG>x8HX5RZn$NnWx9T?`f1*iw+~<2L2}$N zeZ6J8!$}KvPH>UY&9s{Tig?*{+g$rZXMkfCSVT6dn{U#f(11l|=S_X@|GjaqWtQiGE`V|{Ba%_!M8#Cb z()9-!pJjh>=Rwx$?FZMNlpWM;myA^VS%Wr%U*_v^HAX7es*mfQzx%6KPJ0Xk2*9@@ zOkLbBw@G0B1?06Tz%`f@KZ>1 zQXZ`UiV||&w{jM%s#-3s-8P4RHfZ7?sL5>t@=fnLdGDr802?i1F$-35MCg zrZzKU6>B9M74yw8mR^LKB$q#?hWyeL75@`wb zIMt2;y}oCFZ3mS7IBT|UvNl?@Q48>;vJGIvESImeFHzz(o<@8aWkI=LHXv6Dr7r1+ zBHT6FHcW+Cu5GbpGi#z`viPuRuko;SzA{=i+cw|5Mn^m+Fj=hz=YZANsp-`d<%9U+ zEu;0}y6KKN=5pgqZLEBuX02_Jx>PYzj+iITB4fFEreeNmvd6781}O33nYvlT5Ptak z8-vJ)z#bVfq-G7aA}&Cd!Qg=mb*M+MuEHM#Q#8VY$eTCeuE)8indIEZulFGWntNy#Hw64VqaDV&s< zq+JwxZccKMg!sg2X;Nw};_69Zsf$Q+oDov%Bt#HBLc=8NNMyv64)C1@xfw93lkfhj zD5e(J8$9jPP1~*Obz7NBnJd?xl)or?|IQBZhW5%g+EyB3b;Cdch$wt06(m#()!b;` zBzLg}fVJh@#fMe!CD_;Dcs3yWtm0ae+#MfY$Vy@==14_c)LhJovZtrG>N za@TKfzWsjwam|O9k8_SLJu29*-LGCM57q|TXS<2`?LfPoB@giDxzV<0<7nGBGFP{Y zkKWz=7EojIPs_KfXDXMj9yBk|fNfM3tBSXbsw^gcxMZ|st_D_snw7e-R)6zq!+Pzr zH=kWSyz%Jfy~c&k$yQfIv~;KOpnHnr;F^22zMhzLxMQL^Oh*oD??V0Vt<5)2U;o+F zzr6HUU;W{$@4tOgwNt;=GFdrQGt)jMwQH6B8b1Qk@76u6y?g84jkUak+#mhxRkec| zrUii5i@IGZJX!kjoWa8qP(>=eK?Eoi(VzzTT4rQ)B7IAt(<29DP~~HL1lXp&_nSZZ z_3J;(S#9z%U|}*@jZzQY(>2<#S9P3s`tHG{!*@Tr{mZw0^46n_mAtK@)vj3AXs=ZO zR27$Ck`~YW{FQ&aI;J2Z;)vWfI|A%OH{S)w zTzCc5O0$kTOw~`6AiN@XD{qgoS-DX-p0`;F#gn?w=I>Su`$iGdzR)~XHCuFe`)SEZ z&N1Aho7sm5gg+`=zq0qn(_25f_M`V6--_3d^9IeRB&VcX;DFemVK3M?UKgm^uGy)3 zerLB3(dUJ`HEY$YCE?mA&1beaB!T8w(djGy<2)w7omPPt%MXYqbIL>$20kvpmY7p=~ zsS3*Q^x}CbGl-Orm(LbWqI}KWs^jX#lBwdcmT}RnV3K2JYt-!BSALy24r>yoid=mG zp_4t_F^Wiy058-%(lG>p#z8GnyGi2Ud0Bpr4=|rd<`tu!iAs-Z1~+C$n)W-ksmDk; zj#Z$1Rog0Mo;uX!?e-w*(~O`zK|jwc8PP;LqV3_r{c9g)d~)X`fAjk3B_Ms?dH>pS z(OBMU(^4OC} zZ)3IFR~BO>A7B63*M9aVKl%2D*B|G7eChP^RK-N=679Hdt!A>`-QDk1dJRE}yW2%y ztOh*Vt&_{~>klsd#mygIdz^7lT>Hc8+ik-VBDN0dJ7SKy18kE9(AhwWw{iU4<86L6 zVlt3l=f>HABs|={5cVl^bS)I$`~Cm#>#<6^q%THWrOi;nZIU+jJCpocBcdw!s zd7G&JXha>qfA{?cr`pvSuOEYx5`_l()poI6IN0azh1rYaL?|EXOC{gO~^w0=q$Lhy&GUnqt3Fe;v= zP1jFW&zA2ZLiF(FQr24bgUSTclNRlD~>AmOTy)&l{4%~VFW-eN(tZ{hZT`d)WvIyvGRMr8ro~2_LnX{eSZQJE5MQgW@GEQ?}WSrjI z&ELtoU$RY!H-xIaG$8dGTtLeb^fHWk6GVn*Z&+wEd9@V5yKw5?fASJMo&u}788$_a$_#=jh!32$@lAOt5Vj$Th zCnXrt9Hi9LKtkYoQi3lfoa9R!NU4b>Q?t`cCke?*JX`9eD+F8mFSX9g%;%?^8%r*g zn4ap8Z{WF0O7t&EzR)8&~yMytjd(|jl*Vx1V9Q@7G1bvL@&5Kcl}L4M|9;S6%B z_L~op%{^bfTn5~gj`>Ch#oHcI4y!!M{((MqpNAh6Bg&>Rit345^_z8@SyP2`1;-6Y z#j1=~k5$Z2rr0ycM=|wTMR5MvRCcO`X6=f1Eq5mh!luV2n&#@#Q;%LplWH?aK~6JzJ)cYv36@df3J0ezC?sgRZn#W#9@J(ZS5Xr z4l7+M=Rl9WHdOei;(q;3&1_?&Vms@3{-d1r(!DozGp!*3jLjlDGHdl30w5ka>~I_- zLm!56cw9I`y)K>$4@<8MW!{Xy)H7%#Lu{mm@PoHLyfRxo(i&ke_6*s&Jd6-!vw5|3 zrF1@fylj@b#+_nBn*z-fRq@W5?&&UP4@%pbr4t=1B?~ztP1CfaukMJ|x?m783R|pOhRmO^PR) z0u^(Wi;RdAX~$i9wpMAjTKvoa)7TrX-ELiJjb|+6tmmB6ozxuOLA9=&<@zQp`{j5D3snXzKb<(B2*vC3f8TE|Yy zL6euKYeqQM@{!i9#%{?O*W0P!~7|Rg`@BG$@MnfpvKeX=ycI%;fGpkMxoof zb!1&Wx%9K&ADKXcz!*Km9ziT0%IQ$1JMevHjsTj>h5x2S31o7VH%1^WQY__8sRD() zk=r6fG%Ied)laTW)~+IO6yd~bBdB7yQvaavS=nakOzlirymO8@ zs$^K?60^~2(4n?ik3=-_r}t#MNrp?V6FLMAR6)~eP_Esz>9Lt`ljjZt?cKe2Q_!EJbi0BZ>??rPd=|0!Ceh1B+>x+f!2v;*i0!C z?NiM&HM6zj?4hnGHOw}nuA9iiA7TQfK>*t{ki+mbmO80J0R`?yYm4-qd-Wb}xHV80t=XXNSKQCux^w@HPp*CX?vKCu{Ck@>cgyx$_FI;k zeQkEG2B{62U$v}dk% zp>+K#|NWJJ{GFrF8^3&evmr(a@`l7Vg<6H!EhOC#&TU{rz>a|jJX}3^UK7u5s7?vP z29cSrMQ$7kn1pOvEwTS4*b^Z%r1^trlNjEFjzA<;6VgasAgMn9wGvtu#HM_7Im7}4UIF<-P9XXTal{KF zMUcuK!G<+3mRN&akyz&(Gch5d&yX#0kRbi`Z<<6l(!Ip>B8LsJN{AUn+MgM=3Y6MG zeyd~_@1`M{2+u2L7eJTMG91`HO1 zg>6Q`(-?h)zFxagw2-rL=NK4?Pw<_`3KiQ;nW%VdXsZ?2 zjy28pM!A4-1b+ClD?iNq`1X@q0E})NQAc`XG-vx*ZJZJ1`}>7jmK3F(R+tO;{`;F_ zxsPvs{`xP!d6F~NG2gOX)%v(#sTD;s`7WLrg)R96z#<}zkwpX#nw-cNPyy3g;u2d` z_;j1Zo<0w%AEDGps9}a4yLlIB`gQ{Xbe;6M@}uI@+@0*{n|m2Aik{Z(RzxcoZ`{j! zP`_92??Lq@om()B;-iOY!*UIxae)pXGYj=f zV7Q`?8@^}I-SoH#qs*erBr+f_R_5t-4)8r(W8+6}PnV6<%mdiUFS5%d{k={kA;#%5 zHB%LHWqXx}0Dqe=o&^f#e%*e_MsfVsUhZN04n4}U0snhQ?&!A+@CJJHykX%qd#P^g z&Qiu^-u;?qH$T3zcV|Cqr)`e8STU77pNSd*j4^>#CDyY1)TQ3>9t3MGX1s(Aft+gH=3?+=0p3`PvDF#lng(QTBm@I2C{udAKeC{p3cx zG9sKsZkUH>kSUDp2~Jq(?i>Qx#2RI*W3_y&aP0bn()TMqzI{Jq3%KFSooj8&4Hl-8 z;gdyFBRx)-q>75S?pXIGqEkJX1 z_7gR?)KZnX^U1&1vNcc8JW@MRL=O=ro<19HDKdAjm*WM@fn`8#=ePk}Xb}xDEdx?> z9}qN9>_KgSSwb|_MgJsor_-;R;kf!Vh{BOYXzOM31+ija4mUt^uZlH>OSbAZXd5U9 zF;|8Vi?X|Q(-bS!%nYk>=k3$-`kgnN-qj7TOG@?jLFyx;up|N7dGU*EqyQ$F9Z%nxY={_1ea zYQ<60M%9Cxu++8BGiJKR%GNKh-~QnG_e)O-7CNnL6seI985~MU^mqQ-wWyXEqr>tl za&`F`qfKj#vFDCZE!U%QF^mqmYdMS)nX027Evv-C%C#x4xk1}4j*f?9Yk-uKC zjIyt8A?29m)tl7;k*(X-XZ8q#>}c~u`OLNFmw!+Qn2)lJoXz~r{AWl|9HRs~LSmN& z%AJpCX^NMf)*om7r&oSn6{gP??iFq~&UCr@khZNAA+G}=JZ37^Q#n=(fq<~yWX zrGq_$Kwi{!gK~{%Y0ZF@-al0r;0bsLzv`P51my!tiCScm22~@y3)Lt-RJ=i%>Rv91 zRE(8xwykw+HpLoY2B*xi*1E=6HdH59d4PrOt{$y*Gvm}X&Sb-Eou__=Iwu_ku$P%W z);=m4ptNzr*PTHs;tPSEYY}RI{G_((P?8MU(?+FJ zXc<77xebm|z1|4WSp2($4g%@DeY`!^v{1X!VQ<~JvXirjJ5Hq6)r}I=3XKDXL~E~^ z=4@JIPfB3Is-4b1$UUsxE#AMioOP6am$F=Yl(Uq%ls(@&%Ub9i?)K7$xvqYjPL8~` zUT@DVd#Zc3K3E^g+poD>{h$zr!t#wWpr~P=94VivTHu7mL6MQK<7kX#yEfX2NRft_ zmUzoX`PQAe?46AF3!W7`%!`-K6wlL_TT$Vl)5pN)%wk0BSdSVX(eb`Xb_{y&Bnn$j zv*OhgjWZ>C8PEC2Y) z|9tmBXGCi4m?`O8?XVylLQQ}`L;WY>jFD7I*p!grgH#-Rzwi|xj6_gl^lBo38uep| zM>!0tP%MyhMm*F><$^enYD7|PA+!Uc31x&(uuy^>zn~Gi5lJDAgyukqNeY4pLI}Z? zkVaalh-5cXts=CfBm>EoPRycY$Nfy|>DAC-Pk_#_*!%K7Ux zYm}X;`PxX+7;Qv0gwj+(NnbxdCYt0;Q^uPjHLKM-4F~yKnJak*c_&qeWqbKk$Vy)B ziT5H>hGp-u_M^g(gX?MY(j%?w9V?BSfB~Dk^AOOcr#U+X>sbe|@od)2R|OiQ9TE76 zy$YFJpx}li6GBwAZJ4OuD1jrc2)odnovc&92SP(Cn95tEE;APy!yJ2$T{d9VS)}$> zd*?{U24U$gT+E%zI6~OT!`z*MwVeH;?WXO@<+5PqWb-uY@I<)AK9x{mSNmlVYN#XB zv{H!*D|!3Zmu~OheqQtl0gxL7vxW0;H81tJ`iv5MW-J=J*wf%`3DTC~3tXv~t(eU{ zuGygN6>ep1XB`)>c23e}3pO+RX8McNGOAf z3bZhBkRMN=$pKzRVik<$tVpX?uRqM*X<4RC)tecf_BrZ7;pbod)gQc{F-Hrl{K&B# z5ZJrMyO#j&(~Yz%%67*pZMH2|xp4J^@BH;w9Lr_1-EM_erZFo~)WJcNO@w$nx=|0p zgv=e36PDOemdo2`={E`CKM*+CetN70+ivAx^`q`t87f>3^qAYM-9R&@@09Iktz_@z zJSyKU-YJ+V+%7q!M|!arZS>Z~BvS&!p9%)pX1U&J(JR~yKKf9{25p71QXZ*wH^h5a zXqzwtMv!Pf&sv45HCDV_yxX?hI!CqjXxJ+EfUn=r@$|TQMtdh)?tgRIh4enDUF7W< z?m-A+f4>Q!MZh+MeF3#Cal1e+2a?lKAP3>!Qi~9tMIzHfgCf{0kEHoY^Lj^^ZQ|;C z1`T#iNF_IS1!xO3C*^mG7js6+XUh)ScH38Lr>nyRMx1z(<)ZnUraFVTvuk8Lxl}2i z;w+)`<#^3_*>=rd{XxNUKCDPcBR{$Q@W$K?d=MK>>h@6KYJ_Gss)F39+DK!VHr2J% zvUBTa|L3-yX*0kK<3nu?g|*KH<<2bvbb%7lJqG04;x0${ba3l42>^R1HEIx8?`GRn z$^p$_f6a%t<_jn4L$p!Oh+>%6&lMVb7idJK=SOcHfBnUkkMo{qKfQUBwVi!fv%;8c zw|2N`A=Gel;L|1GDeb(dV3{&kkKKIDX7dVV_tsp&Ou;_t$Zu4yieEtB;Cj z8^)W$V%MP6+HR(rX^Y*}fUG)w7)kH=n)! z;PhR`{)n8ru{LcOINTa*St223Z4z9D$+8b_u@_+p2tVy9pG>kf{ zLntSQ0&Yf`hXI&?J}1&3G)5V!rF(=PR<0*ZPZX(YGxbv)K@^)t(1^^%3em^g z5anA<6e3wGKKi%6%+(7~#c(kf3FJlhzxVjh|KgAT%WMDPofo(FvzH2{+E?ju=mg`a zFz-}&2BpJQp`w}c2aShik8bVeFJAiVf3o7GMVXd|YTd15AN0b4vmFwy;SyZU6%<{E(yTi@(Pa>KPLr>YgVRvs^b@#~Bn4T1|S3 zn(u70(nh%`Yt#~}nW&j5S!K?*Zso=^_ijF}xLb8tI$q~3o1(AwF7QkmkybLuj0=YQ zhMQn`ZQ5+!Kt|_A=G;x(&>J7+ALVb{Imq0vUv1be!4AA?wR@^_y35sVWa|f1!&;}z z*X`~OHZ6C|(`SpvN|*8;)nGeavX-%Pb+_PN({5X&%O*4p2**%^Z6a?vdnfC|{8K;Z+-mc&#(UcJ3oEx<3Ik7fB5ge_r;xqa!<3bb67rX><=~0WiOUbGbXsxZIhI- zMwCWr->BNoh+jRt_OR%*e78BobWq)bpeU>^zTfIJ_KqU$2B|kqFx#~()J}I!wYgFM z4~`$Tg=>=zu|i#u1|XzX&vgT(vtRD$3p4%nnW`}u!2?q5pb%dqgv!~0CMP$FUA?1> zvCcpbzPnO`US$=y_;Bfp$Pcv!Fh&TXCxDHRDgkt3vkV&qK)h)a`c_Iez&X`S>lEh{ zpR+MSMIn1hU4`tYD5){xOlmL}c#~7pvoCU=lb2{F*$Gi+;!;B?@u`05ObUPLK&q9A zLqf$&FrT~VbF-6c5dcoGK@mM16t-Uevs<6P^(1GuU?y*=dzm%Iajn zan0j9oB8WmDA(Dvha|Y+%F(td$(YD5(MjY=@sMmpHil>MXx&D|e#2hj?zQ;!ot$Uo zPYVERnYRS&itXlAiof085#qZLqOCD>={h~kIBU6M6NOh+?mPgV%7ZIM*G_JpW<4yr zn|(K9IWu0d%AVqm^UX57Sm_Z&*|XR&1Zs}+j+>Y0K6XEQkhw;=m-EH?wKlrQ5 zAC*pv*ki?W-Rj}~f>ose$TBhlvlo$fdg5IrkhAdqLSfNq@UT~Fl>pIE>VeMyfMzA} z#w$G?02QLGymz4G&ofqwPQLruAO6kn|FAJ)9Fz;$TFN|QjXNeY_c~dqpX%q$H%F^c zG$#LH$tgT_hwrXleOPtB>NNBDP5ye_1l^_VN7+i|5Rys*3=~-C8f}fyfP5%&;OXq- z1-k>Nbna((Eij$PEN=?gX;P}YkcUAz0vgV&xGJSuvUvtKx0 zbXdC4baeap_C(HZ(JDVQXfy(@QQ12#n2^piPSlUo?dLqr`0Vw^-&@U(m!FpYAm@Jm zQs#Wo3@6@nTIJ(uL>9ehR1iY_Tst+|v($c2eDCJTyGK_~${!U!d+*`fKlrL)sdAiS zbo2(;0hEh}(+ztZL*Iat=MebWLF!m1Kx5zv39^BX2Iw*X+5isPCQ+lXcAwdt^L~+8 z#de8jxJykdg@;$~UVEB}vIiN91uLb8#Kfm@YcP0Pe#zFm7-C_1jC2D%Bja~`N8a%bo$r=;PH*FQK z742R*twaJ@&Qiw4yPtknv{x}p^C=`gRQ@0=wPfUu+Gc23VgKyxMHk#O|YyCof1CaS?a-!AH4F5daJ!>NUF!} zTrdEr60wOu-LU}$LPP8}RKRr>0tWpB<9Lezpg|g%B>!+_CWv(cabOvbPceM<3k22?hy9*#7giWru?bdBH`#Yk|vn^}Q8?~{*vC?QS zg0h%n@P%6XE{&BD9iGtF0x$$8j30>l|TCS?t6PTj+=Lp@o#5ZnO0$h6Yman*|{)pnNi)&+AWoF^mf!c1VB~i zP!j=CGTpXN9j1*_Mm6Ssr$F5{ns-!p)Vk5?W0{0ngUV(=1Or>E8stX15cmJy);kAp zeUkZcG0K(RI(+NN?dO+&di4=3*;Acvn{1-^r?3_g7Ng(%li&RAU;o*0!A5fwk*uAO z>yO{~*`NO7|NWm{{n79J@ahmN;E}JEZ(e?K-NxEPY2L}UXvZ*$KbjOoogQ|iW4vV= z8HGcvAr!R)@T%0t3L_T>I15rZp^5tt!cXA>ut@ND#Z3{uIY6kuI4#BZ+AhUJB9RX!FFY4XYZqj| z2}at$2xl0c2?U@HFdy*<#gTB25qzX=jQD;ge@T-Wrs2t!P;L@WVe*LQF^Hs>kFbA{ z7Bx@^^^15^Cx6LPf{Z5dmtsh0WeGlli}*V~l`!$)2XaXs;+(L~k<4jMqDRcUcqU&t zIgmai7|s?+$~|YYJ0~W^oH`TY2^(MXRT3-W550tx1RHVviJ#OooQWTBC4O;AykW#Z zaxP}12T9t?gw$oF*iz>Nb7F4lm>xTOn=i6~A;m+UQwNfokaB^P;yyQ&mOxzCm;QkH zJb$u%Q4~QWev;D2MbnZ~mynp5I-XI9S>)WLXi`|3jT}n~OcBqY$=MgDoF69J2|{v} zq%3m9vvzVYEr=XR4yRv9ay0eYF`ST+;!BOE)=Qj!`TxjO8D8;kn5jc;7f)` z9&&2Bb#@8Fg5-51Ny$}7F&DT<0qKh*C6VK#fYb--ODQR0^3upIvE(_4iRMe!{xb2x z;ui+bR!ECLgM2CJ*F#Q751%7ra$2 zaY!{&=Y%Y>cdmK%0@LEomIZU-@7#5y2&ACoC6evL=Qlwnu028gl4D{dJ#faDnw!47 z6i;gT1Vf6NJg4R-1_@Szl000PNxquIFwsnnC8iKF5RwE!vD9UXWS(Jm++-rh65vsQwQ z3DmT;zGliTR|E3x_Y2o04^blOz&{m#9QHJw`Uj z_b1UxTqDsw|3~&Oh)ItU6O!yH2^YHLVDcK03@JWhS&}y)>>^WgUgA2^o%Ga1<81!R zmysM#^PjyOf^=@dGZD!}$kR)Qv(*v2ek;x-kn$4a zDM{q{f`sJdpRpybA~oRx?@L6C5r1ixWP?1V$?3*RE2O&T)+Z*C)!wFwq-Bt0R?cm9-Yr+E`{6VqNIrlg%?xo}D3 zGHH2d7nxcvK}m_e*t;k@xk6gjxfN5w$O&m#$tyiK;fyaimYRS8;$0!flT7EtCz>x! zxiFrdllmVdgk6-EWID%7yxi1`&b5sWsCBU?k_J z1tc2jE+H$`NuG(*d19)Q9wrA9y_b)Pc}a=q2UClj2_af3#uR6&pFXG7K6gel{c~PM zdd`_xkdoAMgJ)Mjh#_7o`I9E2k!+_X6Wzog!F`^Q?3||%qLS<>A^$u61pVb>QW!x_ z|DESa5J^gsC-DkWJm==8NQpB!aBei&JVzl{OifDBiGGUpx8A3V@5UMD%RSwmo)}MwI>(UWxga_@ zo?MLVrKXUyGfL`mh;xbsA`=Y%e;rk_YF8CRANUbYqay`OwVqC6s*r62w4?@9vz|U9&-+~8c;i&o{$<* zEyH4N7dskG7yyz_&mY?}sfzkJPo0w=jd)0-pXz8UMVYEJ3b5+ML>)9?LFl`C0yI-Y z_$EY@g!T0fpu-ku^Yy+E@=p~Mz@2lD^WczOnu6LI@B$rGAyOi!6|BS=ZvF5nV@OpF z!~xKHVvi<0^fLi94?7zgPJ@<4tdEf%OPmAuQNzHFafagHXEu3gMY2kRKphOdIX#Zi z?m9UUh7^7Y8ZAooNu|5J7Wo#xu1p-})_a0gxb>m>I~97X6r$XGw7bHafS6}tm2b^0 ziKgUJL_)&n>l_VA z8BRSI1?a6sBs@YC9NDJ>smdzc+tv-AjzFE`F{nMv;bx_!x zwTO1GBxC|?&5P8A29RN40>f8<<`R?4E+mAyKEUkNtbFp4cO%Vl#5BnPx1f|;RNeBc zAs8kF^jJDCx(eGl1>Nwn+#8g=ALqy92;b9OOGc>DKyVef&T*clbm!a~KQ<~~6fVTD zH80tiGroAC^>~B3NfYck;}aKzfm|S){@5G4t9TFya`x*1$q^Hkj- zvNU3Qc}C50IL*~9AxAV}#=QQrRjUVu;pBMM%IjEHj%NsVD90UI$&@JFGH@~_12K&1 zz;-C&$nQKwb`Yd?0?t1MsIA@|ZZC|BP<$Epw%7=8y#5MA(ECnHZ?sb}&xOP!J z&wAAe6Lye*DTb}fJ8S~MG3X-OZKVZaz51}$)eXbtSZjfE^tn~m`!=O&Ud{I0#-`HY z#_sNPu}T~3=z~>(CYyTarB0K=#(fX^l%un(Fu{X0I3O-1aq$<9JmT(Ars0}`iUL`O zR9R^tpwx5vwuM(rGWh*6#543+Gof&0%=J2kO7$6nUflpKR^n|6h!|2_<3XH*KTO^0 zb#z{Fhu}rwr2Vi;a6co)CPky|Bgz!k9C633|EFr4o_j8Yx7B$AR#}Y8s+Y%24RT$# znpFWeVS{>l(JVws!Bm_J74HFNjj@5XcC0<^CAnP3Cw9D%%GgdpYcw}Km=dw5H$P7` z{c9qCOCXS{^i>qZ=TuGsDl(Tz>fM#JYTv3o@n*a2X3E%5lZiXmAs~lhl#!0&(Cx@O zHsm2uk@B?Ah-q7RjXKF0T2t)P^lrTlG?7h1YY)v-o+1X%kfbzIRW=$Kz_SxXM>c&D zA9n}DfTCP6`2P8mZ_4%4^Uu!p@bJZ(w{PE``}6br R^&cPp{`cLVzwMld{{Xdg`{4ip literal 396548 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%0D~|;zp#+WB>o~j9n3;PtpdGUYAT^`WU=z{GBcy2 zeSL|9qpU3AOow+1-rufL6x^D~NHEc%Ut=<%5}{I|aRVU`9OaKJ=l}ja!5fUfK-@yNuZy<)d5&LZH|8kPzU%R^F-MxADF~t2J@BP>N|NFhfd%%DH-#6kF z|Mz?U|MhQt1H0S5-rl`$_y7N=&$s*E{_p?)<9GhQf1kU5v-=4A`(MA~|9*Q{2fllD zfi48+6U=C#5CIP!T6N6!2u?gWrO?n~<_wyUwKX^}Q&Slk%F6uw`udfXj~?~*Iy(yp zNJ%9nVTu|RCCW|b3J#u~-P|lHQc)4-a)+mYHGq; z2DQ}n>mcE&s=_mK^QNikp+gD^NEHKf{>YK5SIf%YyqTX53=|MRh5r2c7iIbYCbVUC=TAR)oe@8@^-E;3z|l;B>QntJsL z3RM9Ckp1vG=;$0eq@mH+`0Uw@8>Xg5jwmTrRXu+WRVpv<)vFxNt5ao137Zf9eR}Jbo!#-{ zP$_L~&CWt`a^*^P_S2`29#N^{;ukNnSgWfmD@b?4?{M$l>S}uWg$u`yIXT_B_2^M{ z_N7aQ4_jN`y7l5kW22T9a=wV9K)8&IjN04ZycrvFaS;?$QmUxf+PZVc$A^#a{CVu5 zckhOTAeRu{#D@>5R1pyYfzVJS-{j?8yN1s&J^kz%(&~hTv0uG;)7-4Ce&`VK8Q@+! zeq2MNuI|Ybq9ZLYUta$5kM$6O)pH@`u=$goHvu?%!Wq%gBJf7wT>3 zM5UyT9I>^9lDocMUH$lRe1^Ag@y(n*4eIxsH>0DN|D#j;>eb!5K0f^X7cZu!E-ynR zAuKE|4t*lLa>Bv_0%2kE^G}~5$M4iB(9NDbgR@9jm_(YMe)FcWQC0QeL41bw_10Dc z172QD&HDPyO*lJ`9aB)iE1>Qd5xIIbHx~{(_{re>+T2`SWiZa15f%;$d+=a=9iIVx zCaCSx(=T2;eAv!zXz2O#+FCU=_*`$^+}`f%v$25#51Ocv5l3z+ow72tMPQ&~Z-uvY zdO9RTRP_9LXe{pEkB&x{AuR0v{q^2LHzwCB$So&^H*2z-UU4y)bEv*#l_fI2?;p7h-`w37y0-+ zJa#|B(W8!z@LE<>z|Co8H8}Y09ln{(5;n+yzq>8`Hc*!cGC^t8J>FYm>R85v-@ z2LuQRh>IguV2a&t5x!K)~<6A+Yx_W#2{{6r}QBfp0Zfrbw5FLH-;2PMksy=)`A9>H7Q>WPM z@4kEfJTj7(m!F@>+}{4{Uo$dJojQ6nB;@JSpMQpP@XvoXG#nlM@S(X`M&>VnQC3D? z1TptfQE6y^cht#AL=Hno13R+TwHN6x;#+S6ID2qiEk(>s;H>E zoW*kEW@Q8g6%-(c2YgNVqU!3(%Rv=&b*0k_3-j`#qA>YovC7Ng$+5F@cTY4BO@G+o11|F znT+XeX{o!rjt+@bQ86;o&=3%ysR_qpLjy>sa6d8_b#-NBVAL8L(`ZdiZEa8?J32BL zjg1{#E-Su28V$aH>}+puC_6GU!2?GoR~T2V1)et&hr+{)imIxTli@@P3o9vUZH0Hz z$q7A{=4Nz5%*rG}UDV`dY2AgI+*)ex(6 zLhFcnA38Zq=3-*PxEoa1$y~=NkxGXoW+IgtX@<%F%qSrGDokd4JZk>kH9XYZpoJl8 z5jsogPN4*at`mujnVCpIfC?3f2uNxS2w*Vq%`+JI7Gh#@xOocb4SIP+N9W|^=f}mN z%Zu{__8$)qclX#>C?P^Z;6ICwhGMq48Tvv!J#g94g@%rR#Tpx%p2mbmRh2@4v#O-T z(h@}CrY88fkn>?^$Y$TZ-PQ)}fR+}E)z^n3MOj%(i^1sa?e3;=sqChvnVIkyGZ>1B zva-k!c=#|cPgC>UIak-t&Ib?RWD^xNHm<2zSb$#V>{%6+?Cd#iQWuS;tgNAtkT5bb zG!z%7rKPPM7dJEn^@NrdJZGJqeSPuq8X7V(zP?ROy}bzu$Y^$RYi^#LEGx6MRZwtu zZ)lkQKgrWAEpu}%E$;4ea#mJlW#9{XdS1Gup^=(;`*u$cnJg`>pg^bJy4BYg9j(OO zXf&jUq^9cVC@V)qAf*vWZ!$7|e%G%fhs4qnGb$YIrKR9Y+uCwCgM;Pej*ego5=nW` z^jKKro~fyV0(iQeovW$_2MY?|?tvO>XsE2r!9hj_ zvwHlx@ZNxcGB$?k7#P@TY2edFMrvwmYeUu4-X0yTt*xmU5z*QT;vNzr{QR1mhlcX< ztgS)EZ*E2^guA<@CR8HR(@jm@-f)T+6iiO`_EIQHN@{9U>h%r4E zH6^IBd3hclMn-;qplP?XkVt3Go;d@3+WdTXcSwl3x{gkGIC##;5wf@U_fJj+>l&^v zBGCbEGos^}k&%!97aEakit97_iI|pTW$n7nKYC+7W`Tr-Q78mEkZ_iPuESy>=bfm} ziS8Z#m#EmG8pnBn(8#87bK0Te1_viKwXhKRY3}YIcNZ0vlpq;`&^KVt0VSY^2a{P= zR#L)XxVyu70{v`is-K^uqrZO&*XNBiD0tOy#YN7OzdzC)h}2g2>d>i8OG{2h*A|}@ zeOrQ=h^a<=d{)+O2RS+#+H>S(dV3RH+1y-sU%b4KZ2_vdn;VIQcT-S+Yoe|$lQ}iD zz7B@v`SS#~^6uT3m}}Q$Wx>%{Ue3)`Rz7>y&F$vR<>kssZEb#j1B3edm6gs;te@rO zkyr$OfuNw67?ZiOg8BDHFGN9sii((+r{@qijk>f{S^4;J1B1Fcqz3Eh2?*eskRoYr ze)6P>N@eBCm#9%LUQ|%X%EE+{!GNARKK{;~sVO@B>Q&76z|Ukbgs)c zfBRd1zlsVEkCYVjvyUHJTl4Vn^5*CN;~($dQ7B)1#mk$P_RB9n{z#|q-TPnv6&(#f zcz*tuU-Ix68ZIyI>@+nA2=MU8%TG-~pMV6obLaZGyzCb*934OTL`0;jYG-GAJ1FR@ zuLK0Lv!N`Gjy`bU&><=n{ELKy6DPj@+TZ`tqn9t!(}jii?Q?SjMYp>8>eT}WY;4BI z!M}i?)z)@ogz${dvZZ0K-{bysNz1_;{>{(-DI0Slo-Q3Qeh2msproW#| zmXrk9`1b9wvG{mZRXMrPP^f0|^DQk^RsH-L8%IaW%h3-bk&272UoS59@&ZvmC8e@5 zJ>At+PtVB-+=|>>FE2ww7niK8t}d`GY;9d#)6+XUo12j;U}cq@JTfvflb^4rCn$LJ zYIJmeKavLF-gkDU)ARDMehCeYh(IzLrZqu9aLPjIi>V>}wov54*@1aoQ4uk9b9ax5 z%F8P&BN8mgWUSCID|B=7_J;GVurMaZ&5bznD=G-ZJJ#J$PvM9}wncF z<>to41qKr8{avyajh3Aa6&IOIIPj3vgfj(Z#?Vds`@6aZ2WMuMmS$x^RpsqXtmeVv zhLf$hxS)VWbLVa`F_@_liEYGRK&?eoGtgqfr-zCsH@Bn&o?0(2WJ9HMNnpGFVuGm( zRvB@IBvy9BS`E$>Ot0VtB+@E_gRwS+I?Tq#z<@+5D#9uvJKN4qOUuKfxVXQ+u`w(R zqzkNc>+0xqB#n^CSXsx$T3Q+y1P6m!k(1-(q@&~I_0f^)=2lcRFo3KHU0o#EAdw+3 zP(?*wA4%Y?tz`1mtFp2Y5xu>AeN?K9jHD!)JT$bpSYB>o0!KV3@f{tGj>v~@XjoqE z?e+AOl+@D7&PK8>ZmX%KrQxa=5FjO`q?DR^@7}}&&N&JSEY|(|b8{>f_!l(V?ChgQ zsj0}7^Yk1W1EWV)_Rt|CqmGW%)w((@Eu8r}I>6mBF*$r#QL(=M+iw>Z0s@4Dq^0xn z;JTzx(EmwFddl5ctkb8D9rN~vpA6j@US21s{{Dr9tSo71xXFn=925=q_M@ZUeb?V_ zYRb#2t=-Y_-FJ8HxVj!YcJU&(PE%9p|DZeb=+V7<6bibpF)P*)e{oSq)^CqqJ{q*PUN zbC;Ix-i5mdF6O&jj&e-Q<;!sK3=P4B3JM2_bp86&RA#1%il`{q$@BBbp$B~(tB{5U z5=l)Bw~+zt;UOz)V!~#Rj<&X9eWt6MoZR2v-X0vRq@<+ff=G&H28-QAISk(+B|bm@|tnup6?)jEC2*Sd?mcq`y|LBRiVACMX!4)}FD?!Uu(pOS zqM`y6JO_usz?_`wYPce7ZP5*=swyZ54~HK$DGB=Ms3>P=_-Kh*&dm+gJ>0B`i9SBA zuA!mOHl(DG$s|%}XnHzy3lR~>MFW{5DG9Ezs3=rKNXABDEPQjwkAr%ks0evhZf>@= zAt6OY@ZgrlsCvjaljg53V5;H*audh!_^Y(`C2k$R66&^bDRdRBW)`jFQf@T1p6Y?wL z;xGdSX9O-i!sSF{njlLY_k#-nuN2PQbb4T*w|8`OK>@r;78bIymX_t^>+6pmB_&H@99}p)!E5nA!z`0$*o&#wz;{2f~RL&+uU4pv%9;Z zqN8Jd{o-POe_$ZEQ@HMqj6_Fc^#p}Cs!MHcDJf^?`uef4k`im{D_21Co}5HiKtluh z6N7^{ZZH@oCg`Meb#->q>AJdVYGiUplhcW-Y>iY+%!29AN68uX2j`cPCOPfv2Z|fr|@#V0CpAilL#IS!`@eOIsU@Wn*Jx6ckie*4CDjLn4`)dU|GOH#8I# zg@)SO!&g8!7o43vJfPS?ULvv+>2#zbBmFQS0MrPqmZ9XqwHEyobPVY9H10;^9TJrV zp`{25BS_=~X$p)e)DCfRM5REG%2QK`TO-+Sn1BrI% z+<|@L=VxN#;*y=+)6>up5~87@s~a18^CsMxy1LM6f&@`qY-J@Y>*Q2jjWZd}MX26} zhZ`DveTkg2uCCNnO-%_de}8iFx8LsEv9Z~+=k#ed`-dOiykRila0?IL+WO^}s;bME zj~?~(eDvtoUpqRKl%US*?|<{AtxZ)GSq9L<;{H!Rm6z}C#xofifel~FO@rCm^7Grf zS5UC9@Q*(}e1NO%tFPkYfBp53Ka!I_{~XCM>+3)N%w~V{4c5#{OX&8Bit_L%Dx%xF zxTvqs!*liOty?=go11oafBKWCXnp<8&g<9S-hcYj{{2ZwKm73aZB*1g?iLyO`t@J` znvk%6|DXOuB0YNsGSh_%pMGj=JT>*}uYG+wItLHx>-YEn_~Xcknc4pR*RElLGd=C; z$;T%pm6rDC(acO(7^YBk`kg!X?jeUB9&PYlDl084u3d9-s;nFtsjBkvF);A($j|TS zK&KFAySTWbBKWiH?6B_;T5)f0_JG&pmzvSMOHQc^NFSX=AmrL2tJ z&){HZCsgU^-t_g|x|Nuyudk{~CO0+p_oE9*Bv$qI7IIUukRXH4l9%W1P9)j%^dQMf zTiebK`8X{t5fKIkrlzs6ZEfxC@$u&7dU}C@<>l?|xw)R6=H|$TX>KkmB9kpF;KHk@ zfEUie0W0l_irU)LRP?Kn^aw>5j#J|MsI0`~+uGXCFEg{Irnopd8eQl3cvR3(Y@yFu zP*7csYo)!tA2(^Hq9QjpEQ~|~X$uT3bM6)&KR&*`-qvPg1GXYT0D%KoP|(%2w|9P? z!@*S6&=5&yWo7#MLPC0awYBT(LqqT^YHFebIyD84o0b-MuJ`Vd$pQjmVqk=AZpOx< zdh_yn@ZjaktSoQ}EG_Td`}ybA)=QU=qA)bHvokkmWwmb~jv9DD-QD-@1qoqi=iR&D z;Q##3&prze-`@V=hqSb#M?d}4#bsgP+i#1D#l-gPv9r5>|L33E+Z7b{?K3tW9sT>? z2L=ob4jThbmX?Cg>g6>%`|e#`ow717ueJ5i(A&42ojN-E_bVt=RIIJ_ar1{y zo|KVcvmZa6m>R%|!(VA4hU>W+vgBkBGn;BPj{ad32yZ`l``S1~ZKy6+v?r78V&9 zAD_<6k)=}6@g=Sav9ZwRA_EsJC6L(Q>fdD~hlHSO11CQeoy1?$XklUD;j!E)5R(}l zjs7;eQW*ZtJ8@I3T z;lq*=Jw0)8q%?1AbaFYTCr`@CW@kTmFf+OaXDPMzo_Wmz3bOWqze}` zG}!Egg~>@O6@CHKgwxYZrn0haU(Ae1>d`S_%# zH#Z}R51n%@t+24_>YkqZdOS1ILm3QAY_RGA+YBkzDJkG2B2xxL4b(uW8YtY2(2ip5 zhKUq$G=fV;Os$Zp3za=|x!|LN+<{C+(9UynSuE6Agho6&J0ilvgW!%K{RTp9u^f5s(Kq6Kfmnk+SP171>6U5)Fmot?jb zMn-vgb~awa*B58i;^LSXbcd)^;(X)p@97yHo}P|%a7c*1KS7cJZ_UdK`uW^kDis=V zA0H$D_xGo!B7KoUX=u20tFn?z#@sVKy|oqn97jiIXE=r{Dq>=6Z7nTBLfC9Li7hS7 z%_)@9(wZ73)5gZwm_li692h7r#)`wor=|wJ?|=Yg?81xQ*%=zDs;a9SAHVAjQdA5N z@97yBfdBmBMKIRp=E}>>&EYw3YXf-{Qy2$_wzjo3Bv79{tE`-vdFRf}o4&pmE+E0` z&K)p3L`5%MN=$sjrE4=7P^E>0%*{Q2&fy><3tgBkF7sVoouA*vW?&{Mn{euVP z<&jI=)MR49$EU7dS-G;(-3{f9l2U#B_unrsgGGG)d``~Wx6hw5nb3(b7*C$OdX<(Y zDthoB=!Gv{6c=B)a^#4$HL6PvM?nE;z0&EGa1`2gfkd&o5n)m4&N+W`@O0=R#7~(9pz0N{X(oh6Yl^ zCns4furGsx(0ffw!`U9QM@-oC^e$Wg&;Q}W+FEmS2?;Z^nwt6fo*uAgO-&06r=~hO ze0}BQjE(d1rlz{P0|KsGQBh$s2e@3&urN7prfft+7k7hOM_nC`ZtyXUjdgTrw1I)i z$($TxV@$KIU%zt)Gf8=Q7Z;H3N=j^PWo2z`OG|IxuB~-;MuHbQfS5gE4UhTh&70ul z8X4i5h)h3AOQeq0)HF9E%T!MfzS`DSbflD({ry{7#>OxKR8X+DFDV%v#a^tXWoyf3 z4-U4rhJ@hSoSHf~c=IN4CJzp7Z0zb{vEXp?^(`-NZB0zXc@{IW{(c+}sBRk?hKI|_ zplk5<#_SOtGet#9%lv$zO9qxM;TaDOmXT3bW->w6A)MP}a&Pb5yT!$pmdeU-aZgVd z6`7bwOFKE?ncyFnmbSGmFNbH`&=C1-t*t979UYdIe0)d=c=6)?{m@Wx@oU#IGuPLb zmg3?rayKw`S5}gfFJ2T9^6?oR1s4QFdaMQK=b!XsLnv$2ttIf>p?5wQl>hkdL@zvCzl67?C z;W>UhKmVtno;>mN{PUj=AI{AD{r4Y#jEVX3%P+o&iu(53-+#}`MlNQCC+{5gu-4W^El82aX1vZen6!5D@`)cu|qH z^@R&^a@cnV25M^PbTIBPJ3+sW$xKd8Nx=#kt|}Ib&4!}{6k|}Za&zI8#uOzX0Y0ht zcrqC^HNg%>UOl{OSZ_y12Lyn^m6{4d7KMV8IIMb^%%C72Zi;wvGMh~}Zv6e@;&QoL zTpXBMaLJ{mF&GpsWjrn}BLjKhWbVdbK&8T9`1yHyQYdL@nCDO^yICb*n)>=eKLgho zl?v7hlbMd6``aMok;{K0+i+}$=ZNw+}zdnk17UKDLZ{txPZ~yCe@IT$gV`=}( z$NKTTL+ycW_nGm0|MPA5YIi-d#2N_GS16Qf%>Ye>*iKk zIy*Zt!DL>$CM;}eSzmwueqEiJ8FH}-3KkXy2fe&RL{P2I&%+IJ`n0HMbo9c)#zt1w z)vIUEhKAn1|Ktfc*{4pytMU4EX{ogI*I%2M+_TJ_xF2vh>NSJAU_)&2Qe`)%8`H; z5g{%vArT!7wOwYWy!`p|p`ply$j?_+78m#TpPB;gR8H>jVYr|kKd!BXngkqS5Ov}E z(bjHhAsk*pLP|>T^3Bdd$t^FRn~OYf3PnszQZg=XcJ|I4DixV=6w1iR=qS>xBqYMZ zCMM?QQc`4P&z}zto|;-+MGrtqicFrIeD<_uhSPoF+`0GFDW7=uADa?YF) z5(*BUnVFx5pX|g5(6OF9!?YdA31efgU*nrW4|Zha-Mj8?IJ|Uq+uOIdhlg!#d3li+ zwVQQ!)Mm(Z)u4`lU;vqVQrpf1aCr;lelH zSXuS=udS7o;5RYlW~^;)!tHhBh=Bo;szyic?F9rhG#VN(p#jT8QL()I)vNpW{rpaz z6cYzoSS?2kVX?0K5>Ffo|}966x{EVCrPB4nb)stYc(}bpZ4&W zntJ!HwN*=tm)FDuU7)@`GqWQ{6csBfHa7bEZEfMYD=B%(O{Ig6>B0p#2bY$pR57mm z5w5${)r5o#7sSM}F+=c7l>%=-H93YM0Ff(8a{ zZLeRCj=H#Q#*Us$7gEl<;yy*6Ox}F{wpMj85^HCp{ZF@v$~3eI*^f|CmJ3mGUM=@%*|oX zm6iq>o5(6Ve?B&LX=!Z@4$kAp3CCJS#-&TV|IFuDUIsho=us=HTesf40TWG7(AXG` zwc%k$M-dTS-KM7P?THCj*W<^*v&K{}Ab_7A%GJe1kmdvgKu}*?TwP_c#Kidd$>awQ zwzi6k<>W*}{QU0UfBUw&T3#Ljm)HNW{rL!+eyDSJjnpr$r7yna10Vr_lk zfV6aZ`TO^ai-Cds{F0Jv_N!NqABTrSC!3tSwe{>7gTcpl=#Zb^?Ck0)2;~P3xVcSE zZfs;`iiv&omA(C)JKulb-mb2G>=^P0zW=_j4=H=b#sdRC{x~{{DYuMFbv5=eXXitQ zE?mgXeez^_+QZ|}AyLuP)Mw9@m&3#P_yh%MwAIz8Pg7GbT)>%fb#;3?FAs_`xYypi zsj8Bb!%^AW`}ApDovP}QBZh|E-LGCH~X)AQcFH*YE{Wn}j6M-mJCaq8;$O^`}=>z0|>fdjI#H8meTEG+o@ z^Ycqd<>b70v9uH!iLOaX%Cl#in{jc1f=7;k4u(uf35i3892|y*poEi>+P~l29L~Y+ zZe+a}8FhERe?K=c0GBbr^mzHl9PA1TSCHr{0A88 z8yh=2{ryTxUwombH#CGPvZW>V*0#3aetYsHB;@O_Km8PEX7sRwgN=JR(*lc&E_s;BVp0$K#3Tgdgamdmn#E)b;2IU}B3k3{q{P zqT=JhBtZsNXec@}U=8~F2M2?_hztpDZ%_;K@-WLqGDT=8K0RnCp`m$sot-T$Nl7j) z*49BmY<5{0va!6pF#Cnh0hH^Ml$I6_$JrV4R3>wJdS-^nl$Mr|pimYTa9&bSfJ$_3 zZf9p`NL~GtPp)0-?cLc~Uk8Qyz=5o+-+ue+UqeE$Z-j(=_ubA;LBW9oJUj*l3ky3t zVc68vVac~e9N7o{=UtX@S zH#0kb-oc@>b7={>{xfG>UB}12{TA%aFTPMw>FN3P*Xd~k10J3eC-U<_r}p&Z;ra8Q zDU`QwfBFe25AcGnum9s8t*!F%IQnL1fBUV!AL;}RjgF3Y??y*)&6bnnaMssHMnKVn z)^2feeB9q3`AuqM#~ppT)mo-GCh6g4$)>U1%tH(8v{ z>sc&EM>8`r8EUfta zz}0nhbYlbS!ehsDbZTo?S9^P%oKBrmQ9-8Yg9kJk!J%B{CY5Gp%E?Jc1O|?eudS7p zX=xE^;`{f@%FyRVzSe^WRaMaR7#TG+flPqJSZJ4@JQ*GJ@DLUT2LbtCK|x2jR9CF+ zR#xKUPn|k^7~cBLO*Z@D#RCUyZE?*)&X=H|rRB|=Z{GCvA-BxP2+TSn^Yq#^P$7_| z%f|;Q#O&<&IGHRYboz8e#GO0PhhMuUDH$F9(y6lncxdvGTIE-nUOoEI0Sfo6XIbnA4}vo@FwhK7I8nJNqIxGuPk$ z!Gm}2up$AuvZ-llskT-}hnH7NtEL7y*j832Phv_5YDGlErAu%$gId94Dss2DxT&c- zcaoA+RAgkr!ut9~M-vj1m9Ja@*LQFb-kbC1k>N2nhq$SD|`r_hXQoetG z{kpF1H{YnKA(M4(&e{3PFGWNMT5fpwi4%N$RO;H=vu8<3_+Gue@m_0cR8)|CFf{b^ zX?;EP7S7IneQRsTjvzPyj~+EN7#P6OQc-dLeoc*m0oHKs?Jr;AyhP9}-oBljbamz9 z!#w2MZ|CQ|ybc@?5lKyb`4UvlmNR>uHMwNwFOoH=(@GFuU=vG$j=YD!kahu z@B8}l@m;!gHx6}7UmzD}p}^6uN`;c@@|%a?36l5(I_+}W9#F*W7k z`Q{t!yAL0NN@-~s8Oh1k={=a-)k^|iORg#~UmHL=+~J{A_< z-XP4wS#4$I=LZ^oZmy3H9uN9jL4l8tiHW;=R#tN}hZ7WpgBoltQnC+Fv*viIX|R4N#TOeUF(1i6e1s47E4-Q0Y9;^Hu&pwqp* zJw2nMKtN=%2$caS-YF@;!O->*q(Q=g59Jo}=8>sFB-WA1P$ht@79WqZ7L|%CEuHS} z4)wdBKxzx+~HCoayz zqpEuI=DT;z%~!8}@`Qq+N z+qY|L!NCU(96!!rJbk*l8XCHP|G|SXG4I~}@Iy+<;lqFVi;vIN*3M2<)tNJ&f9~#% z^=(VbsZ*G{uB^amCn3SZBO-#e&B6lwdqr`-+C_c4}&l9>p_Z&C}Q@BErL?rZzS8kAJkcSCU$n_ z=M4@2{AU>%e72<}D4&Id8X9(Xo;|~>9XODa^XsoKUSK_gIr1NW{O|*X@~1z2`e{te zFI=}^X6E6;d-jBf|L_Ce#gQXCJS5Wg_Rda41#W9=KYX~e)7Az>w2BH8(GwGxDPOpN z^pMq6M@RhX&CS34wz%lz1WtHO%`d+!FT1$>F?jadq<^y z_8DFQNg_>65)yyWM-w7q>~WJyVFZF;(o4-`nqf62-Mc{?x=$x7+zAt4}vGMU7A zhVad2XXocbVdd?ObOa73D=R#lMDq2;S)+hUCiM3waq}OEyeGJ2iJ2lXQ3V+sDK->} zuP+qa#l=LfF1)swJHwlga}IQ3AQuD##Kpk{5FL&42A$5~l$F8H3%W&NA#TH^0OeJ6 zHCUP;U4Yz?n(E`@d2`KmUmnbUM_>+1UaDd-wYK;wWow#*wA1JvsTuAANn;6R%zC>G}P4tlj_PKg7jr zYk&WJb=BP+ieonWZ-3j~rczIw5Ef2N{qW(<8#?_Em$w}j_UhG7Kjr0}JjugjYz)^+ zak04gU;biZGBNSvkMQ1p{yCHmpmkV-FwRyVu)$eje4osOW(MB+|o&A3ju9%gErHnVI?i`_4{n?UN_1t&!Z;)upX{ z@F09}=wJ{O?}mo=@5jfX#g>$;sQB)?g#~x_efva2a&o@^{?)6P7(PA`k(8A8@8OF$ z%uULPhq1@G#Cl*4BIXUcK7Z_QxMM zF2DQ|I+ovmfAIq38(!Xogx9a1Kc`ZU965L}G<0R<*|Yd~KEAKM^7LF-`0$~In?@}l z;NpT+RYiraF1(2o6YJ|xoe2p!Io-So+O&cK_-BMZOim8HEqu=IZXKO(zEM?eX?gb! zD-vO0Ev<%z?d_>4H@A}~uU#uDdj9<0Jzw9GCnY43k{&&pob>WKc<{stoCDU^S*){X z`S{4>`T6Z_ki9@#8ykE1vbELJR6+tO-qqEX7E4PpF=OM(O7N^59nYWF(<>~zbEm!C z)m1`5O%3YaiHYdwYu99CXteS1+1Z>NJv|wjfPjI4jg9VZP=`*R2F+l2xUDTYIUoRP zjo5z@OIh4JcN#5}OD!dI(J3iVd_bR>mIn4Gg-b2Pv_3HrDYnGi0xAg5OrX$&ViOrz z=r*ADfCSs@?DTXREs(o`PfYNULH>Ylgy>L&hhyS^X+BuG@Rgxw5YHukAWuF#JSGNu z3kCyDUnnSu{B9R+Vr5JWhf`UJn#|5_*SCwL13y0|lg-Y`ijMa71}PQV2dFWEgBc8H zS0W=(|HsGYzQ~3oxF{e2Vq!uNOfVlomK*%E-~k2&ff7sjeLXxv zLf}G15+^c(*zBUBxPPX+#KhFsx3$H^sjHtq@5oJDe*Yd_b980F-9o=wU0p=P+`O)C zcDBCW-dP8Y%QZDIvw`Qet3dV9&_%a`TlV`6UJ?C1y#ymCcZnZ=r!nVx2`)YVl~7>vou z+1ZQ?EiE~@@bIy*#l_-c9i4OM2#sJ(jk&p)n1KO@Gc(iL>gIOgf|^=#^6+qXH}WjB zwOOp8A=H{C+$}M2aBz4yIoZ%qLnA!AyZiR-oE#&gD_898%gRPZs;iMVZeo&_23JdX zxSpPo5uM)F*434kh81ySWO;c*Lt2`bmx~LHR$1B5kdy?SHP+dv^xfRxiOI<5?rv-h z3c7q*NJv*VBLfqb;$ozk!mEjE6|^eB!SFF6vVL$LhJ4I^15(AK_M<~aPZcx_;>>Y zZSC;z=H~A1^mKTM;^Nxcu3t}1wzSmI2@I^N>h6XwT~*b=Av+tX@_21)>+I}-0c84U zYinsyD9z2iy*W9^tPKyZukY!JmuY&dCY%?D~2l<;vbZ zKEA1`xj88bpMpkfXz1+B$Z&Mj<7QJ86d;Gs%S%rW`B!iclgXysWajwz`g-W}?CtIB z;I6E%&&WWX1s*_oIhaz=s^{mUw-gxY;eizssyA@4LPD~$Ih=$9Bv66w4Te=DH&cy7 z%E;*9ZYe2-hUd?#sK9kSIf;ZWBO^mYWcq?A9v$uMjP+42mv;__mb<&JFY>#yvvIx* z3W|+|h74KZ=!)W84w_P6Am~*Y8KA2`$&OxC%s(>-qoSZ0PfBuiwczFsayV^m`T5@7 z#>Q@LIXPWjwYA~lCMK4a@E_FIGnsJ9AwjFEipg|zw6>;D;5bc8bauA13=J(SgI@+N zXVlh>jo^1#S-HBR4_H`;x*Qo*;Gz*8JoraJm-qDa^#y4j+I$xmbjIP$BeeP9;l;%@ zH7O||c6oc_EKyK^)iW~L%F8P&6B3AST6Q*Q4Fq8xy+wT6IIeNc$jYMA0|MYN!1W9% zpkPqJEd+mrzdym=B9pbXRaFB5IyxRd1{p+BpKFDbFJla;l$ zE-oG$Yi;%Qm6gTWwXYB9^ookAsx;coo40NyB)}aO9E|jX!a_Sc9i5Pn_V(Mi3k$8R z6ctINhK9+>%1S#sX=xL#uYYK$uFl>4>Q!);1_zNWCnu+_9v?qCIzFDBZfg2}u=k!( zai915rjj^u5?iq?Sq%wE2nhrT5WR>hdheq5-eDMe?+m?{p)>T}d+%Lz0RmCg9Tj)W z`NcbTzK3IHchCRD?*GM}vz{0BSf-9<=KDOK=f1B?SsCXagM;bm#>Plm<7Y}q(bt!g zb8;#v!E@cfKvveyuBfQJJtaj;i=Y3&gA>Y=l$4qpNFT$)aDZrP1_!scqJ3jxqNL>H zR9M*7hWF(M570%csL0B4cQ-O}a6ktlBg4}ZTs5#n3JXEhcXLZjEGt9j9)HJiaWMLM zd+X@v>!W$x+6v|s3k$N=gM%|OWo6*b;C@1!9<2LF%j?Fgcla3KPojm zoL~#0_mY(4;)01wa&kol_Eg5kpw44PbHdci%%suM(mXve#Z5{oDM331&rV!jkl4g3 zBr@`3MdIcb9i5wtmIj`w;D2BqVox_tpl4H9h~Auyjh|lz-3>>fxVhQddV8m&VD{qX z22UJLArcea+$=0SJyTMOis16Lw)XT)O@+4!Hcvbr#dMMoLG!~;f#)(Zr-UKF&kwc) zT#4{go0>X1<9(y32oqyVOPptvm;3o?X`#gpGhbw+o}Pw=zdy3@2?_f8N=okTm6bg` zIXM;<3JSKiFuBlZFww&ZmzRgf1x^T<{1Orr6*)Qe^pJ6`t~N12(<32aVxp_d)03ZH zOe{Qne0+MEaNh(5c6X1Az;&;o5fK5GT2hj#DxSNro55HkCg$SO(13Qmii)VHn_Fk+ z{5%?HTwGRGot@j;b#)IOaB%49H8kw*g2v3jp`ub&wzD%dWNppJ$jKQVK07-$=I6)H zFDwjlSzn*8FTA%&Nz2O%3lR~bqWA7aM?ZYHyo~uUJSHo2h7PRsH*dm94~w*f1WrwR zde+uJfW^wcukY#8rY1~BwX`s!?CZlx4VcT%p7r;enldxX%a@lQ9N-N7#tm`t>}>S? zt*n@tB_;Fo@za@^UAVx@o0s?1SC1e2`d+`z&YqC)?AhTV`YCK|F)>e{!rgND^6As| z_UN|4Zg%>#iOD#fLYke;#r4G(CMMI<|NLi7&Hek=uiM!zEq(iKef_U|L|d90p~wguH@%qcjW5&$tPE?q^5rV{p;7k!ROC2 zGRDV$L%+qvF)*At(x#wsc+ zYhVDYHc3ekE=EQ=I{f@(WY9(L>FMkY4Fx$aBBHNva1igRVq%`29Ub7}>F5Xx!%_6bjg%m|$60L`0O8om{njd~o)LvotF!tas4ab8<2_M|ZKlJ}u4ISXfv| zDK2hmYJI(;!oosS6wDa3kD{Us=(otomKLlGZRwspU*FQwhKAHs;(mE~TN^ zB{LK4BYpkM%$}aoQn;Md)G{+?XW^Q?+ zcXn{H!^9*bQ&_mU+1H1a@4b6jS=-yQvt%+FI-nzigU!jw%Zp|Rj87~qSFVsqj~=~v zk)D2w?qkx|$LyrGmY@F*f57ba=by*NRaO7;7k>VRhJXKich|}3+&NB8D)s&Qy*-@J z-M*cc_V(?wXF);d&wu%)kI&A|(NRRi`SXAMtG)f^=DT+}In2x-ePm>W$!c4hnAoSE z>gX&kq9>!K_Q@yG(!ITyL>e0Y{qI~{B_;3QudG;DeDVo1b9(yg*IQdIE~ig3FvP@s z_0{XwF)`P#U%ng^^z7OD_Zb;1EO0#T?*8;sWhEcqr=JoYsp@KB;lKV>ULJIc_IAud zV5)+vUPI%|8E}7bB4urT;ewzbjrQGlPo9v;3=AwR2?@`iA0CE>GcjGh?CJTCp18v! zbMYcb?K?YZX*@g(3|3YX6HlKaWyi*5Z$B~d{CRDyk`gnsx%v3`yLX+PIP;Q`sjq+a zYGlN~;OtpGzP!Anqq#Xp$IF-R+(}H_-{0N_F&{~gwY5i&C=?-~yLSTv7ZyN>!rv&8 zbsHN=Nq6sFx&(szC2S-4_%*_0JSlHdWFtSfgJ$x7)&CSiksoTwEm5z`(>rOpLfVFRzD3PtW8eg(5C~=Z?L7SJ%o) zSsBcxj*d-DGc$#SaOaXp{ryN9Xlk;v>*`ikE-%;D!&R@LQC$4+VN;Wd$*o(Ug>P<7 zPy6{{z7P{LGc!3E87U)!73Mho79A}oCo3Bk*5BXP7Z4y!Ckv9vU0r>BK|w-7e0)AW zqoecl6pDxlnB{YGNUkX>v$C3*;R=jbA3OVl2Q4kf$Nl{{qn44$&0Sh*Z%4M^{{76% zg@v{@3p!_7N{UJaA5XOizdPz}b0jZhs%iLNE#^ zC!aklF6QSyeOg<4Z0v_0`udcVPM?;NYHj`g`-KGyi%XZp#LzEYUbeRW{PWwlv$Ed4 z-P84i0kyoh=Kwv z;&@dqF6!xh^wI6xg@xaIv$<(!_qV?>FvP~#4W z`G+4`TJGOJcTPuVWaNh*Mn`pYKL4DbKR0)GcYNHzftk)TOH5o|9v>%>I5_zEqobFX zVT}+M=jZqJotQx9UR@pb*q)xn#r%A%+)p})1_u26+S;J|D=&4G#MGp|KCs?fg8Rae{*W{YT&{rXd$d>8x) zuAaYLuY}SKYbSkc-2d&$=YN#|M^60L{5Ue;#8)CtiBHo2-sf_|<| zFd{uYJv>4~Vf2iMaB~AkGBYzb7j9oWyP%-#?EHMf%!*uhVxqmhva+cu2%;-1NlD`3 z?CcH>!^6kN&CS}{H*P2^S5)lof}aV0PiE%&`tY!$BMfaRDLB!0b7Nw{>Hgl{&Q3(c z?c29*g@*3!y?mLP%Fcf2lBeh19_WskmMJNXj{fjNXD2cp0s@VV-+ntarKRixtw6IrUU;xQ-EK!2paA8Ab$mFnuFqp>k$Zjp9bT^$~FbtSrQ z4p6n6e9*c`ZQZ^u9c6NO|BSThJKma>hP-`S4Zrm_2X>DCz#vT*XPn=`}?!A9v-Z$0s@JNTU&72v9aC0?dv-; zv#<~v3J)1}cbL1v;RRAAc6v%mY;2eZpmU|9#K@?p*Vp&YfA;ojXX zAKc2y>?}_ASy|DvgME&Ri<#NOV}AbNprGLXeRg&im#L}aH%mC1dJ33lkhQo`WKPd^6m+)|I?z?v*BR4nEQG(wNUlqRt zW*4TWy}gef<>rctGB6k#o=m5S-{HjzOr(+5ZD@G$0=+&=q^Z>XeHgZG-h>ZsWo3Gr zM7nvCojoLEd3kj;Je-4rnc35GVF4yXX=x4)qWxG}DksOnVr2#YRb!*7D!P0%H9I?P zZ4Vw?yCy4JTKf1gX5r`~QmF?An3S-w2?-@9@9ZE^#m&vj8x{5F(biUMEEgA2z>|}h zzhK&9X4c!gv5}oEDhe)QU*GX@QxmLOdU~CmFJBH0nw#Igt)bD-@cjAsIPp8==N}x* z&wF^>yopK4%F4`)m)EUZY;3{7=<>nm1;5?m;=w^)o}?r@yQ}NW%+V3fq+w$m8F}`s zzFtWQDfimi-CcAl85w0|aeC0(YifG!nuJ7QA<{dZo~*1wLaC`wo~*BjhH`TwA+|}s zMMraT;%|C-8uKK)D$UJ%d)L=9GlhiELA!2sH87BqQ$QdgVS9UREjSoVFMt1s57*bDqwn0ge%;x5Yz!wl z=qAE5cyNG@=(THBR@2jO-y%^C%GTiEyLSTvdU{9@*Vn&(JuzWwdj32wZ+7;C&$zLq+3;5NE=cpJM*{1=&Yo3JsHq{ION@+) zij|d^bL;6bFi1+$XfIzbEqQvfum}jinzz0l87U|zEF2Sq-VaFjoSdGXBO^;oDJf!N zEG(9mt*r|SIXSYjH*bO%xwKSWt*?LQ4z3#q2h-E=YhJr%ZeCY6F#-F7u`!72=<+x@ z+1VlORa6xDpG*~uSJ$kW6rPCXu3|Qd5!kA_zVp4h96c zxOjMkho_~brba}N$zEO|Ar$&8EDVzyBIkzgJ2*VZu|-7%2g6#0_8C|*$ftpzL{Mp9 zfd}^{J|5|JFE5a)b8};2aAr#;r=;L~)yoT6jM!KjjbQBQ=y-bO=c5te?*8C`wKauO zQBhI?XPK=n(j~Cbz*z^{A9AoxPViE}B!lx}C#Q%ADivNkTU$#@JPM_yV5{2N`uGr! zg1b91E8x`Ugcex=H=DauC9iU(#wmJ^WHr)ls7gaBhh;G^c)*oTEYs8lhehezaJ!9 zF)_4~$HtyKL1&GV)7rYHXM4M%0_-y@tN#9jgW6hUWn5|S4u@CPg$pt=_zvi;o;f2R zP*U>MS1T*XSR!%z;su(%c;4scA{z_W3sS6a-~RLya^R;l@{^PQ z`d3GXkkF}9Vq!x>NS7PY3Eg~rb#?##_md|sF6Ym4aTOMR`|bWd((;UqQBg-ndwW4a z3=Eep1q6U22&3ZP|L*9xzmJ4GKmT9;qOOjU(D88%jn6;7f4{x`r=RBMb#+gjx_&)B z|Ce7re29oRMZXCMVCsve3?HAUXi!jlduwY%#DfP024P_|T3K0mIPn}QD9Fo00>arj zDvC;l zSFehS?%lgax1`R_zJ6U%Au9UWXR500?Z?Lr4KgzS_y<~N2M6u#s;ZxTCMH%}`~LmX z61v!ef<;B|-tF%C``^07!;_YVo>fo~n%}S-?Cnt~Sl!~x30HG;W*i;I$2T|A(**^Y zm>eDF=3cxgFTa1EiEcuenR)jPz4;3lG&Fj9-@hLk($@a`a{&Q(q^72DejqHImbSh= zItnT$&NAUB4hrJu7ZrtZ1?@En2}#MAm<9R`9Cw^c_4gzDgzJE_b4SO>NK_QsYwqqt zLn|veIf{yWe6FrTbO#lUrm4xt2SVQAVPhkDp&A-kNp^RG4H;?x+|;tN!NIaJTU#k9ef{+GZo2!?!9hX-mWS5X(o!26K|uwD z`1tYh;o+z#B_(-z5G_YXQ&Mzw(WLC_o1V_f($0IhmfWrY0nWv-_bTB(_9F zO-zc4hKI|_%*}E7lAk{}*W2ssDo|H5(F+3a*Aulf{7Zx@=JTsG$LOcSEjdOEg zL4&1&^S+T0C#Or7KKo2Zr@8sblkxHJaAZII{BVUuPs_yx#O#EGpdgsc(CPx?4_8Zg z5h5eKyg;YJRS6a|7*>%^1dZRq!qqh)p}06F2Yw@@B(t+&F>#^aLPFB$x6n{`cTdl# zsLV`Ur@_LFib8@gBEr_z!ouGlp47xdCnuueUQ?5u4Oge7Wqf>XEn2#8mH7A+71h?J zraC(t8xzFb)Kn10Jv`9V%gOQav9N$o4JIfg!qG~}rc-iVrSjfrAj*i62gGg|gn!30oClg=Q+}z$CE;$2OQmLHAO{JuFfxb1 z!S?o!j%eiN=0-%I#p3IWL@`{MVPUbc;o+{XpsFL4+}<7?t*k60gbc;RL`8+EDL1!* zLPo~oA~H(6^c$EqtE<`Bii-UFZf?WFj~`c5XlZeBnwSt-ZaqCFCMl_`ti?s*L`*^= zIeBibug}d56y3D6jScwRg@o?ii;G)ZTVIch6Bg#>#g6FlW31^xW$Et+pF><6M(LK8 zjg68LS=p;sb#&U=4iB*l0&j#^-&j$g>uJ3DuEEiDxl!2#pp(cO*Hd3AMOUQ5gR`q^3B2jR7{ za%QHuSVM!ARYRk!Y<0D*&Blh0PeY@yaAgIa7%?$z?c(B%jgb*gPaYn$hnJUUXG22x z`T6+Z4kZW#*rkt-;?YK*#lWDgZGXS3%hD1)L42kDelIWhHB(b3C%e0m1rQex4D6ua zaF%=To|hNWnG}k$vZ$!Db4$y@LRFQGjkL6x8Q#g7n|*vVG|bF0GWz;P5@R1 z)=mV~02~8k$^!#IMURfgSu?WN*k?pXgLEG8pBr`}Fdu-MiM@-ze?R~zA8=U*(^(lf zQAQUByOXrExHxxrkO1Hu=;}?C5A~i;DvTJ}|JZuD3ToA3bJQ*Sx%@ zCMp%5P!Uo0^(|L3DIgRZ)?rr?RrTdO$!qJqs5SqN}T|9TZew-`Sa-ZewF$ z5EzK>k&|O?D+T; z6w=Zb7RJUxL#3tV`$#KFPI85V}wC|ZF`Oy1sWYez@v z>AbwxuDQ9bu734ZVId#imtR_1uCD(4b4!b`@E`vuBQrSo?|+YvD=3^g#lcZo`Qwi( zD@I0t`V%ZYKmLez-RaX8FUH2cd-wKjZ0yym3=F}+&z`+~o1A>(#@Vx8UPnhi{+OT7 z%>2hc>gvwUzI#_z#>x4IKPW2?55Irk&>$rA$3M!-qWd;8qpgh%f9$8wRJu4(s zQ}eHXJ$d5ldiE?h0LRDh+g-ef)Yb9v))tveWG|17UcE|50ksR0fa7BjuWsG)^Fz`z zKmYFCGiOXpCMRFKpwS4bPj~m>VHMq5a{j!AMpxJK=kS)CJEy4F(gJRqk2uZV{>*M7+77+&)3kv`wy-aWo3GL zSbKMOZ*Dd;n3=)!R!}fGSzqtyC@!w4nV8twSy}1khQwi9Tt^3p7HVqh>M)Xn^=)Y> zOg9Z$TB04>(o#|q5dnvd9i7Ebqg7QUCE44<-JOzBS&8lk`UUav)zu{>{{99AXrWYB zS5-wspqUXC1~N!gl&PtKfxkcU`mwRd<@@>Jod*pDBcq@o8m+cAF3!vh+5NJz#>S*1 zb8}5iH@AX<#>Vt?^bSa*l9INzoE$qltUEI^>+5rKk$QJ>%FL{-&CYgo1fL`$qoDx? zI$d4dZ)`+HQcKIi0_6UZ5>HROB4cBlnrdo-g0$)8uh3A~jN;=>O?7q2bwQQEaTUvy~ONCuLD1xVo;ayn0nq zA})U8h9ljY^Ur@)R^GdJ?wq0F%*>BJc6ZCjeEhMP7*;2PgQ}_@f6UKcUH#p6%gbhF zpM1i`R#5Q$_Xh_g(uE6bY&kjKefRXKm)Ggj7cL|ueE9IwPtnnz(8=P?&M#j4``=kv zUwm=ul#UKs?G+Ua45#RQdXUAJmLw$Ll11nI=~GkFKmL)8t-k)3UtYa(a{B0_OPA8q zKYW0p_R~+%=|*B3-P@zE?M~}eP5fuFR zV|8`#$|oii760@nUf!yzufLw3H!*>kC_DS*%k_0Pw+k1Tm?9(h_YV)_<1rh=zL98& zqLVr>aCFqz2o@H|Z3J-+X)}HO&d$Tb@$ukb1qFFJwLUMewl+UMBm_=DG8tEx)KsvM zz=;AI30Go*l^q`sCn4zC;91ATg@=RK4MQ4SJ05hVG`f#4t>bzf76zs?49Re}<>XMQ zF)?T@`T3=%Q>he+ySts8j}IsiQBiPe`TC-91kbB z-|6m7{HB-yc+gwuxF&*TjmZFog6plHABaM5h-GDggMk@CVj_%U*#8h-XLMmtEF)+D z;@TY$01gI)g4VpNYi1^1nXax9^qa5m=;;1_d%LkQBjcSrK|#~g^Yci6;v9gO*J92r zDw>`B_%V9sTwFXn!NGHLtE=hhaJBpSjg4X5k9D%GZAZt-N>P!D3M;FYR!Pa^WKof( zCI^SEZdDc9M8?K!Z0OYO?2L`MyYukK$>rtk>^yuJ5+W!lE*>Ahva+-UA{7shw>K=! zAfU0ZU?sk?lA0WqzrgluhldJYdeI|-vAmAbs#)n#kT z!9m!8dwN`5!Muu#o1uFeLqa4Z;MsxMARz&J@Q{$f!SV6c%uHI^+Pb_P4K)`RtQQIj#>bnR$z&xZef`wbla?Qn;F9%R_V0 z+}zU>J4T{UM%c_SW45ycSs*tTb5nd2!B+xD%GA`|9jnXKR5v#h6SRWL%k%QEm$b8s zjV&uHDhdg~dNee&u&|^gCdR=5#@_5~JloOVA$mPn*`mLL&Q4|~dWO!q4w|S*N$9Jhm0C~$gC6dur4eQtteCMT zPf5YF8J)VavfyACS#525d*8f4znPVljV&NxdHLuFJ3kH%J3E}PwX}dUsG$K0;N+y8 z9TSs~P+Hpd_QHaX4?Y7tChTPH+_`zv-F;$WV*{j878VDGv9ZI$>S}d$4h}T)U%u?> z($~LvQ&qLL_UH&F8#iwX3nwNnE{=}+_y`C{NJK}EjP&%7$-=_-?^7tVvv}T%ieh3k zI|~z(l$4N=zdt5pd3o~kY;4$dp(&xGBPeKYj{Aj$Fk~ATl$OrU*4NwHOGp?P6co(N zw6&2)va$~zWM^aFjOb7I@-ucN5{{vva+Qm zHPz0})D*qY_ICLAjEsDJX>>N1JQ%Sd?zk0kZw_e-!(NA{Ww=w%xJQ*GBUswa&ZX`PNUneqoUm19UcAr zl9IBr;C&)Y4;dNw3@lVq=J+b z?4}$X?CpbsGBfk?(2zDa_wq_hD=Io+vt?$MmxH1Rr*Cp{Wo1DDJljrA@$sM&fyZWH zal&JR)7RHGB_$&xDvCHUf_DK^q2%P=-mx)E7VqCTGt18(9>_?BL zr@g(gU-9=J8yg+<^@V}R+k1L?c^NESA>$SafuVCY!PG z_3L0RKY21TVrvU$V`}Qk%J8tWGYgBLU_!#$8VsK7?BF!5uWxN7CEdHn!{g^SI}3`f zloS`2iwm4HIXQB2ckbBOz-yG3hj#=}3RhQ)iWC)Z-7+%5It8bgY;4-v4GsJIICE!V z!8!Je7gJN%$K1MwtNQ=%#4HxnjzpFT3Qa$&4%%S;Jt{QN@D3s)6BN|lJvd0AXlkmeq7y$po|uR!Fbt2&%UM|7ekDJZzOl$ZDRQmJ-!nwqYz#l;;RRH~B` z+St|AeSK7_nVF=dfk9^Gz(8}epP#I(n3$PaTwHE0Y$wF&DePv}*1^GWNhc>`zvJeH zi2|r+pcEw~)zqNlWoBk&g_R-gczK2t7U6=}Su+8^IIM)^>L&;v#%I5-%yaZuPYGr^DW^u#F@ zCOaUo1_j}q4h@0OP`porUJAm2rDa4!d3kele!iF2g9q4!W@VxAZeV~laX|riQEqPV zdZwp0HC0rgpYPxRUKAKn#>QYrwYJvP1qQ0Asj3D9)YrGPL_{bnOG&xA*VXm+Cnu|@ zz>io}wXo38;OK~VwX(9gxuzyZM=>!yy`rL(m7yUrSx87qDmr>>Y;Z6r2lO5dHFkctgSmc*VaIbWMNE-O<} zxqe+;y{2Y+yS3HO5Lu;~n#054VXQr2y?^?2a?;)&d6|+Dq$up{h{?vwmj?&_{%6mA z_L+wVEZ@n=OiZ7AVqvko{Oz}Ob&`^oE}582O?~rCSC_i_wQDE&F9+AZ`}ghbY}+{2h7uP<~2Vb9xf=z&mR;tH8npU8!IMu z_pY;ZQ&VSWP>`6Ih={*GUdK^UVq*OKpt)~tVdX9=>ghQ&1oOPMwt#@GE%I{}6*wa@ zF@ftiKOdxeyu*)<78U91^YZHGph1s`hm@4Daaq~&a$g@B{;;7DukZW!#l*;D%xe=8 z)YYY=TwK7vNKVFg!y0>RthyTOY6FAh1ll(x3|`V%K)1^pHhf~L2p zXI>sM2_TZWxKODL4H+2@4!XLy-`-B6fhFeRlAGH}H(`UG0rPxwb3+4Ob()&q-dOX* z#26ZaGf_|g`lx|{ii(?CSy@*Xm>im#?(P*80|RiTDJeNP)YL34HaFYb^YhEd#Ka5^ zc6R#tNlPm#Mn?|}bar}sV}cVFHZ?Uj7Z)cg%Ebk`KiHJ$C^fna{=l7K))#`gB)q>IbVn}ULAY5V(|n~{;|fF~q8e!R666T{1U z_ijW4IBW?CoSa1WYi|!MYb4QzhIV$)1Y}?^F&P_s{ko}1QIUZ`M+a-&{(f}!)YM=r z7#`Nw|ME*=;i96Wqw#Tbb1=U^GY4&+fq|PlJ^ks^%}q?eSXpCYFbj!`yK{$?H6&zb z=jqerHMq{BwQ%!NL9g!a_m8&p+4H?CyU0w7yW%x>OPQ^UM%V#2}#*Xf#?w{J0TyKsS%6VsNJ6;IF0mv7#Th}hcN*a!%?e*N0D zfPlx3(IDsHVPf*|SXp`bvbb1W9DcI-d7M6riJdv4uHMn{>=}HD=g&(?Ap?imC|nPP zg^wS@GkE*9gao>+^Yd`HN=kzJ3%WKRpMU^J+sn%_F#-Z?Y%VT14~>Zt6l7tsx9{s) zS;@|pmS$zOwjLNbIB0Ct(*qT$rw62VV`Fx99i678r%#85%+0S|6BEnJd;EB83~c+m zcaxHcQyxY}X6CT4-QDBk8WGac@f#9V}NR*V|cR&VCLIRz8 zq*1Yx&7zx?r>A{=1q5VeGcz|faDv0jD=wa#jFpYA@0~l`+`+-q(-RZ0Ou#oVGxP9a zWF+>`B+}g6?rv`G{rfyTK0Xg0K79&KE-R~p!_?G^7d18V@>j0t=yY}x^w=-Il$Nfl zIzAp4Ff_byL0GuB_@DpW+Vb|ka^>1JKfjHQr%#KERa688-Q9b8k&;qZ7ZP%CXlk0A z%*s+x!G6EAbZ!n;c{VmaJ`$<9xxYU%)7n~B*WEoUi$+UL1xE-|mfYNg1aEIAC!{7Y z#Q^UL^BQEKBO<^n2@Q><-*Ao)5PzCJGxr^%L<6bjC_0|QM>t*ycC14~m;(b^hB(5b1A5Mg1s$VW#vHj0ap zS#osj=vZ5;szTP)&JN9k#zwRaG&E?mwYAn(Q&Uz}Il26NkV!2qnV8VY*xiM-iH(hi zCnjcte#82oj}O_bt*wLvUS3d2r>3^I(T%x$*~qA)V|5j65W=&5bc9?tTx!kDuU?Ih z+u5cdM$jw79vgtkA@1ZPnLjVNq2rD_dW0Z#Oq*XP1-9$yr?N?6kAPz921aWd&<~ zWU*snkXDF`LnFUs+yjrQq|NrI1CNz>Q-0l>cAsG zc5Hilc-X^3SQxC8jg7G}FE4(6`0m%%uw&xo<>vPB85xU0sHTH*Uzv78N~u)YnH4F|xB)SNr=34}5w$ zDAs;{=#@l8A+G|enSel8*wPXb!9qf8Z0_!uOJrqxexEiN`SnVE5L zC@NB^=vR7p@bco+acOCKIy6*FOiBuTkGZ+1C{a=HOTdMJX9kX&=H|)Cjb zVIR@dgjQC07ti=V0Cq1z|9TcfkGJ{@96<67AHj` zBLf5e{zCMdzyHwC^mKBvsw(F8eSK3?IKPyW^Y=$P=6Vl^GlJ z@o8#iWsQtr(j+3HqLQ3EItuR;$XD_4Gcywt5fKs+Vqzg7u%@J^%gG4{5PzeEg^G&o z?B?bj9h;lw<;u!%IM8UTtF^V7nlN+}72)wVHNAdaS{kjP#YJ@7?%gXb{pK4`axPwE zW=>B2=9@QfA|lS7{p1sG?^mxre8|Xv*}&L%Zx2a~%a_6WhEe|EL-aFPSdhec@xs91 z)TzsttEzta<=bx^9Z%6+1Cfy*K79XuXy{-6`q4*Z^6S^%f1i-><(D6QtFt2VzRytA{G*-U{}3=4_?)kD=@AdAJ^9_Dqg&ZjtSf?3JPciR#$IqR9CC2f~C~e z_2$j5zOk1t=jL2o*x00`^7Hrh=I25C5)z7yotqmTCXu+gMMPp^z#a7QxqBC{zUk?i znaD`w1frtw-W3^n@1BTASlImh$_jjsm~agZJ$#s)jAyjHJ(^$H+33fC=e)C1S*fbZ z!h#mq(NR~IsVO@<2sucl;ypk{hDzPuo|=MblAj+*_}*SGFJ4|j!Jwew;nC5sFmZ8F z(ZIlwk-53lR5dlEuMn)u5j*rvRIXTarBlt-r zB?1DUe4?V#+xzv`0|T0xUwk1ZR#Wr!*K2EN>!7Rq;sxeoOib+T6v{FE787&h#<_D& zPH1hWrLnVr_L-sK$jI?=X{o5_*|R!2LqqT0_4L5Mr>i?M^7GGAQ@XmJeRl6&OUp06 zJb7Yk`|-z@F2%>cdGqX9K)~mppFT|{A054apPI_Z_~$>Hn{RFsOf<0XhlhXsab!eG z>)bgxxz^TizL}mjH2lXuI5;vg&|7tLyLy$4Eh1uh8Ae4mwmWx1LswUE+9e{w$?4^V z_t5-&%(n6Fy|R**r>MxyZEM@rwZ2ZHX=tD$Sy~DXsg~BQTgu9qsDTD~?%etF*4Ajc z*3{V9DJ$FC$p)O0 zfR*d%85^6OothdH}F=( zzU}N(S3i4JT)eva+i!PweS9!$jG+5aKyS^45c z^n};faq5VkC)%TvlNJ_?j7YY9{q^>?m)G^{JUlr$@83Uv9u{`x%K7tNUfbKRUS((B zy?gPZrRC`8LSsCglcD}jt)*M(03aiHZ?`VJw1JM zb9B_vk(n7S%+1ZYIWif2+vw=+?VTNv?YOz&Gkx+TJ6l46gTvh&y=NLtM&`;DP0iZc z_4S$>cq?RO^YRuJnwr2Xy>~A;d3kwmE;?G4ehUvD8JV8W$k5kUQ-djNWF$RZPfte1 z*}1A}U?49K=^Yc3%*>9CvNCsfH8o>nFl0+hK^HePjF0c^M3YTROG6_nYHVy}1s(EF zKSfr5bMvdO+S*J_Vc^B&e`qKvNnc-4(ZK;Z2r89KHZyZ{jEgHO%FTt_5UCYB<3dA` zSc{HMNuf{z=w?5xuW>3zCcC-@1fWkwtclUe0BbiS1fNTo0MI}2@Q8@O*8%+>Zjmhd zEi%%>1KI4v#N_0N2r%7|mp}&2)ALtzAlwMCv1w^Un#|uHIk}h^63LlvJi;?OJ|4z5 zPtWx9vNGh@jEx-}5);eIb92dLg6vsdUQ`rBH?UY+6E>F6P@Fu1WQ|UWrKPho$i`V& zZf<&dHa4lLNR^RDpcf}5wzgJPd3&p>>giD^ot?1<-}(8aCG^jcaGssr+bbxLm*?hobsZkw+$<_W%AZW0n%dhd zEmcs!d44P1I#^k$uFlG;r&nLUwpLMrt1IlBM@PfMU}vB`v9~ufXm6jJOG}fIVqw7=4hA_L9h`qxRz7^#+>Di!nOSl1#6*3)tE-Zd zwRK6!_;_tC&dT)k($c!S(c}^r*VWC+8Xc{x!x~mnl0uo8nVO1;k(HH_!qsnnJ~LBG z>;8QopT0g^<@EKD8K%*OhVt`41T{A=DH$F{4ns%?ovZ2TmKHZR2?;f|q$K2wLPOQm zbaWCD1_rviLPM36RaL{n+S*!M0|RAc@m1^V8yb9lB_zbf{r&s;#>V2}q^0@zaFyTO zq|vms`S|SY(5-K4gV#}0v!US`-Mxx@6c`z+t5Z{CG6x3_PgvO8-1Kxn00#$n84C+w zen?8fOuevhcvx5{DTzJ9?CjgOu&?06fkxZe>FP2v!pbW@|Iwq~UT}xOE!o3&}r zWYE36b`6Hwckl4dclD}-L}}^EmpFgI)jTW=j_aTxZf-6vbeiVoaQ?>5?%^>uhUbrl zhM=Icb5GCeYFU}Cu7H4*6`Dxc18{RIDS=7b*y!wh@1C+Uh0@p8-0bBgDJd@>6*WAJ zR3ZN6+}!HxvC6i$2Wh{!cwnHS!r56)4lLJ+iT-|MEH7SERKyN!VF8~bEe$h3YinU4 ziDYO9<}PvoI9+#krcg>sz!tZ#KuV#YAU{7i7|x`~NFoD{)`YJw`XH&PVEa2cVZC2m z?C6Llb#ih`3ylU}tC?AHa&vQ8nTLmpih)5wLVJ5n4VkR0tf!Zh)YsSAir${0Vr1mN zz~EqHq`bVebWqUX;M7!NqMRIdNL^ivi^TfN(h@9B8ckQ1i|fIIii+jsh6YnpE-p2- zq9Wv1oSk`j<>XRR=jR6pabk+p8xoK~L89~gsg;!L)vsIwC}5Kt1cvte9tbkx$awM|d2trC8fMP zD+|3GaLqKR>YBBO;Ka^Y?FWN8c9q34i~#wxOZaR2?1sd2jFZbXpqn zM5d;-wX?HDMOs>S?&#61Q(-#kjDLmL@LF!eVPXG6LF$n%d2qdU~y`M@L|WV+Rg5_u!zT zBR99aJi34A`|nw#_RC@H0XIxVXK2Wo2(~TAGdy?pIfjj~5qPSjfua{?t@em7Sf8 zjD-bKbaizO4)^YMAFpM*GFEd zp&>fj&`?v;%d4!cwKXft&Q43q(J?2dxw)vw%M0A^O|2Q&EECg63!BpVk z5f_I{rjHMiNiHY=6V<{3jhW(NybluTW*FFFVz8nQ4u*#oNlzHt($bLehgB*lD3$I; zij2hlz`*3>j0_?f8Wa>CpO_dHMyB70Gif+9{QSbgVq!37B@9b3F~PxJbn6I+tI5e& zr+IipN1yyAa8u^xVTS7H=;VYXGqK+D_Rh;g-b6(Ojw6`B*VeqfFJAoYGfT_K$%BL3 zTrRGUKUPs08v5y{u`xBZKmVDFtGxWfhr>fM`N9Qe=7faTuV1~QP;T8~Wc2ab+IsdZ zHI3w?bg7fMQq9q8cTn>SrunwrQH_4mJf*Wa(H2}*Ql=Z`-w zE}EHr@dY<`Uf%QPi;E5pmo9N~qA|O??B;gq5-V$b{L#_l$9{ec42+DSp@)YrUL+>o zzJ2v-K)~VQ*I(!4u(O{%>*%<(_1$;H#jp{ot4~k=^ixZVpx~)fQc{D1pzDL>fA?;4 z^Dn;~9N60a?QhJ?d3hf`ynN~Fd-g0NV?x4r-+l8lPSPa6D^j-oB0b?%rNg zleRYevDhg!Hll9}M$KcoPZ*{=Ny*YuJW4JuSo7rMynYR;1``uAb7bW4@$1*g$v1C) z{<*dF{QUFhRO;Qkr%&tX^!Fdp`AtGXpL_x%=ll0~>@QqUQtImZ@yEG2Q_~9!|EXXnWi3I#R;AD{Jg{Mz5zNG`71w@D;0%8{(SeH*^5xw*VNW#v0}V26A1q_tH;XUi=(5U zprT@O^7wdXCy68~DkBpW)z{b7MkeD*O`%LqV(up;B`FyeHa3Qaj)DT_%$NZtCCSL} z^18Yrn?csNIgAsb5!bs2h!4RZcR-S z6Z!c@Mlv$4uIQHIG5a;?Xlsl7dQ#HZSXUP;9+Hxwp_oBnUMVJq&zPGlE5lmS+`Oh{ zb8~12g#62w_4Fz#mX-zvqM{x=(AFlC^YXxOfK!c_9HgZow@)IW8-UY0By7QD0HJ|6 z*Yoxc3W|*d=O#21ES#_~3MD>1Jlw|z&+@3Kq@=_|{1?6kP6IJD^Y#WiDxQ8ry2{5V zB!qbNU=9}?OrfNt#Ki>!fTV%R!AZLU4lVFAU{gg4oH)}1F9P!m@T{z@VUvOz)z8ny z23PpJJP;=EEDR5a2`)U`#RaxQaDcE|v9Lh@ysQk*+mqK@Vxps?kr8pWNUYXfToMyY zOLKDk>Go(RrHv%nS^)wZ-cyn|=c?)6FeBJe_`vi174ucPFmj-~-`= z*U!(v!P?r-ucTyPpsg)3Qd3hw!Na4bW_TFgTf8Pf2&k_ok>upm)uW=?+R%ScR8&%e z{i(AvCI;?5GP$7vX*LrRnC5D02L^I;&CQjRoSiEw&<_KdM_)fB1^W;yD_L0+6IgJm zRBO5g&fL7PaB{Mx#mh@xURO6IWpJ>)9UWkN{P1v3Pe_QIoQ#a0UvqO;S6rNymVyF_ zR9;?J7Z#?brl8>MjrMv%g08NjB8gOAKRB41Yh|Ua?dR9nI53czX=DUud|e%!fyTxX z68LUN!W$cliNR7hK3-XAX(=S6shN{AJ&k9ptgNuqf%B~49fwE6kgRu>m>aV@Q^tm$d+ zUBt!j--j1rXecH|QBgv|%L@*coE)4@@bdckb#}t-f_W=iUeVFP!T$c3ab;%0#0zH} z+F~$!+u6Ch6J49|a5uM;iC1_yI0KlpqHE^sixfJ+6d;m;;M7M&5hw3BK}S*!Tvj5X zPNCoxgDwu-o(Ty_Ns*C0KJM-(eWJiXx@6N8?X|SD*jQXKe0<F1vwhqm=vGc5xrcLloV>$K8Xu3}-H+aI3J-^;6le7W z2NrWWZ*Oqnkh@2_2yNAn5Td7q$H?bD^eGDEWP%5)7yNUMj+vS0j=Q@nD;pT3r1bY= z8gb_i4^Mdb?(WxLS62%NoH~VG@zbYR!Cbt^#6*zwQc`$$(6L!qc=f8f8Wv7-a}YmU zTXl7B-!?F4Z$CW5Uh&c;Ik~E;{r!#(Rn<>E;pfL3ZhjsoTj$TGr2PEzk3Yu8GcbJl zWpMEO_rLs7P{7Rm_rE(kzkdD8FC85m9Pr-4-8(TMEq&?~FK=5L&dAi%PMx}Tt)v8J zWNvPM|NG_3DJkE5_tjUSp_eXQy&50?9sL#;clPW@A2~RHNn2dZ&i>cGK6rrnb3p+& z_ots47|hLm|9xYlh{(quD=CeP{Oe!i<2bdElk4sM*T3fH_4NMsH(uV_+FyRz+jDS$ zpMpYp`Eq}sO#a6|PM;=`c6MI8NJzMP^>2Se2LHS78XM*1uU#`U8y|o3rn3`GKs~*# zuGg=JhLFo8Tv%9Dfe}if;H)l1H<*}M0&ofi)iSh ztqwbESQv@q>gwec76w`og@P6@*hL8m$;s%vfp-=a1zHB){n4)gf8^wui$)iAEa~Ym z#ksp%T7qMS=>~RsShrMH7Zi}my1K^3QBietZ&XZ-v9Y$cmsfFdLqmGHqobjrheuXc zMMYW~+8`uSMg~}CAd`4|Q>h>ffscW%Uvo41eV`7&UE$$Z;ffZhB*M1V8QGJy+Mp#^GUH!36~^EYMoa%+%C` zWu&2DZLOun)>cFWvw-F0_I7w_wY7=l1lY;~0u;*1%G?~D51gD}5yPm%%ge^*<%P*- zNeMcfI7!>us;tBW5lNNh<$?mNXN-+oTQ@dJ>9kq~1|_A63Y>VTsa?LTpio!$^5w*Y zwKWryghX-i^XH)Ka&U-?=j9zAudb5GVEQH`93JlMgoIqX#=zj^wYGL}kd$=$_Sv(< zjxsNgpa0xBkTKr9Yim(kdK$Vo3>j*OU^UcM|Sn4Z42HZs7d!eC7xg;e?N(u=0`!6iOPcJFS#Dx9I+8V6bFc#O>Z*0`o zg4U+4URQT~49^TJD|!KFwFd_B@`445Sq-MWckYl#XtiTvee0I7aa|o8V#>;?wa z)tF~mSO^H{>J}9(E_Qc&dCAHe806>A&-eAgMy9HokT5cW<{ao|@L#sK!{a6^>+1_s zY*?6_oVYlN)ZRWhnVqetCnbedBG%6j9w4{W)`m6_h`V^a7ZwHvJUqC#1O$SE$H$Sp z5fek|X>9D#BXH8VxXjI4S~fQ^fkvwqR6n$gFI<3=YJ42o8)s)JDQ)d&y1TWcBrMF? znM_VdfHxI9G?*Nz)UYr(ctPF3ii{v|8En4j!dM93v$eI24Pm9Eq`0^^If2`XlwC(h zH@CF3`}eD=!ow{r?Cerg@%We+Gv*C`=m!syk}NIF%@YzHKJ4sFN`iknA_Di}oRpLd z4qjaR__3?Y*!b*ObUoo3kBmfWlb>II|M+-P5-N^fULgGB=j-ZOI}Sq2AO3Lb*2u`;{x&&jW_JF(mewO?V%ExvtLxRPDk}HyfBiMfuauwv`ZZXs z7ca82qdva7iwe_~D`8=Kd!V=p30=D6;{&e`7{zRCo}Mc!fBthzi>m6`vv=+c4So95 z-HpUKP0f~;moNMJ?%X+d4*hpf&h6~Zo|TX&Eq(v~>C^CVWT|Cky?C*=7Z=CRFDRIl zbZ~HVM5FQWoImg8Ha)$!$6$a3Z)*$HAgVU!&RJTHkN^44cvdzx6BFov#>egKI5=+J zeDL7IhuK+Y=POraWWb7ET%=I0T@w+9i7w6q)=dixgMvh(NhtVc(WAK$)x@gnH9ubIh~wzd~9%F0$$;5!Kp z=HtVBf@)J#6hFU+2#t2gyn&Q+ObuUvs^6N({U zUjYGJ8%IY_ata7YOT&$~vy+$z201)_%gbP({8m>%M&aQBgB)tqq$KbqLPFNo zz#vyr;^Xu4TUmMc4znn}hmn!@?>js7^v<8hs{Glr&Q22(s4f~CkB^6kZEdezQ&z65 zJU(7n@b?!Ix^V*u0E>$uAtEB^mhA0;^C~1HAP^EVF#$!bgoLm#XjaqHv9WNIQz+BZ zhliL&#l*b4#>QT}c<{i~6x}T7JwVJycf6zogg-w&Xvx4iMK4cYUQRADa&&ZNhECVk zR#FNL9U59#fO1PkB{+C^cx?@lM@KuRk5 z@ALDl{`=wK>S}nnkdTZ_R@UL+)2H$A0s`FJ!NK$MYir5LA|hZmjgBra(`bZhI6l6+ zTVAfOFE1Y&iu}0zd`(R`x!72+(29#?WG-KJbex&_^y%S4O-*o*TUu}yJ2+gvEFl3d z{K`sVA~JS^g9irY=Sxbgt&vQPbl;*PYikV+SJ(UZ7Z$p^0|TX``1s7sii^j`hlcX= zLF_`RcSS`>2{ap^^+1J@mIm)MsnMjTlkP!$JbDeNV}^yr#H6OCr<0vAQE7k9UBhnn zpIv1?SBzs_3^h7#KfljfH=_>1dc;1s(h8;8fZE)|g)LU_J(5I6O74jQs!?3d^zB#hL$8X>YjEh6nmv{tG z1BL<&sVppq9qVtHVlk#q9nZ{SOG8Ble_?7WcJNTs-#_y6Bgnh^`Lq4}6MjA#KYzr3 zy#Mo~^z-|8T z2vXhMHa4Q7AZWjSjfxsKH?pmckLTtn6fQ1S$`R70goQ!DLZTTD4=CtcTPG**xPcWx zc72M9TwGM@>gvajP%aRz$IF+^%^DhK&KMZr6|h79>MJd+j*f4>nVZ9USxM>My$>H& zSABiK-zqJA{d!{~D2RgtdrTxA;ng@fgM;VhP%Y%=XJeyMXJ_HAQ&i;U27mq4tCkjH zV_{)u=i%Ykue-ak7cgcfEP!+4>WZ3OUf$Z;@UV}MfPk!QTH4CW%nUT3A|lbz%gZ}E zIXSAT;78(FOG-2~k=HUc_2LCo`*Y`z@qTz%P#`LL`m~kR#KhaT4GlMLoIii}?#RfW{?ylJ zV#3aT_wLZpci)YVTQalWG&CMO`1FEato13Ab*RQj)M@R4P zpPZl9?@dVmTN6}7&Ost(vO-riGFaMy{7o@eHUkB^_6)Yjg( zapem3_ehg7G2!L4W+rKo-8Vb?&6_PPNRxAShweT5{2wXXn$W_4Oc=fp@mEgKwRk9p0rE zFGfe<*+c#wvd`fN($p+0+}#Di1)e?RbuTX`CreAiBQQKXKcAW^CB?<%;Q^I0H2XX} zj*d^B92~T?n3+jPxVj>Zr=!E%Tu|`tT^zCgen&?E0r)6aR=T_0+=PWuYbAAVVPSds zw6wJ~yn>>lygXhF>58Z@hlkHHGt=_&l$5x+9UVc4gWHLl+r|cZ@2V<2Jsutli_Xr2 zgRU+pf{`7yxA)`;{Ch@5H8oG4j*U?$!oo^QnVBmq)6-#L;^IFYy*JvoWo0Rsa;K}W~_{^!phJ~TDu;W09*uLoPz+M18= z=FO_A?d|@4XJ^#%Kx|!Dz$+*!W@c`0LlrM6DJ7MZw7ChNtEedU$zWY&W`c9<>N+&E zwN+TCtjx}i-ruWN&_VI?nwvj*bbJg?oB%Ux02IryF=uBE4kaZp=#VGK&CIbw79aA{ zkgov85!Bd%f;fxe=Rg9Zs3^4GM@LYoB0p_>{KboghMPAzIN;LS*=cTu7zYQ1LK5e+ zwD|bk+{VXWy=rT_eH%GsPo7|8xqFw5O-HA`{_t>U2yS3SMNoA{N8R1o*u=!>^rufz zW#Hif^=N13@DK!AF|p|At*sX?3Jc}sk(V$#i)%(*9jerhj-8#xMq^_T32{cZw_93r zbL;4QK8w}Va&o{Pii-m$3SM2LNQ#N^^Fv=hKTo5{$?@}hcyxD9PiJPTtBZ+wdqZhn zTYLAeqN0b#qemMXt*x%EYHChSs5rE?y1OeXnwS(6jE{G8`1*pQ2i19dyT8AZlD2kg z>gedeKvWcXw_w~(Owj3uhBt2}CXS9Ge@0gq{NKUB`T6{OIKn`$hmP6ENI}8V^Wj5Q z&mVoSjSc8t6&0GIVELkYGedHWGShzuz>+6*(D`K zMT3JUCqW3-(}S{Rd>k2Bckjx|y1V!Ez@cqpBO-G9_Pu-C+qh1Ig`x6aTpSre-v>Sd zxMyQxL`6}BURl}O%geiQLqZ}fY+>Qqvyu|1osq)z{5jOlY;5oeJ$>5QX=TODt)Wp| zyuMC)SE{NR8B0quGYJXm>RMW0u8xd=B4=bor;m*E_eVymF>g^({ryu@(C7*XC@BR5 z)YWx&H#8t8&C@eDI5jmp8}%AciRerp8hl#NFy!Z#l+b8YDpJd!u7%Fkoyp}zm$#xK zCx>X*Qd5hIkr74cePzsBLV_nVKP@^Mc~~TYCGH=r^5kR>4<{#dT^bq~3{THHcbuIw zGao#tuaAkbut2^;ZEb67P7eCFR4SbKB_)xO9v&15)Gtu4LIV^Yjui!|DFp|IhlA9I zPHI>fv_7e+Pz1%rK^1{o3o0V4nhL3WL`Or#kdgwOLPP|ME={MCRIS9s?Ch*8xSNOu z4>|~V_mFRk6%ElU;q^j8gM-o0Mq&n)3bieiw<#%pex9D-vqG%{pHoUoQ4yUE9lwJE z6kG7{%E@7DZEam#tg1qmqoHAK?Z!rDr>!jykCs+t<>4VxYdno{N>fucwZOo~ zk0&My3M?!P4ddbl2dAbO3`0XzRdoBt#xgQAHQ|nLZ(mxft+lX#`oF4bdYZVbt*i

kmW##^TS63Y!XXnaF_@sk^w6*p0!onIF+S?fn&@R2b)6?-iczW8| z1q2iqKYUnQ3oo{;tcgiV3Oo?i)uEwa%0@;O7FJefXCu2TAb{wc(VY(pii#qYN>tx) z79}LGYG=@45-J7BYl9{;CI+th8}-tXiN-^20bTnhT<$uNr{RI z4Mna9(Xc`1i98Xg;GndDh6p)vF6Qo0NoF9?nF<4U3C|IsmC^(Aq$ElavJI zKuk<gR;N2c6hj^#@ShoNl65O_uf6MbJ6|&^y%fx zj12H@eS8)dp;0z4K_3N+a=iwEDhT-+=r6nqrogHb%dwWPi;^N}t zL*mo&a(FoUgF!)90T&g?%EI+JGlMSO&71I^Oig|GfGpzc*Nu%kJ72vT9JI9s^N+AS z+}s2Ov0_|Z?eF*Sz}g`%Z)*z-UMVRR6}Z%vmg3`Om^b*Op=brc0u1?;6}VftxNL17 zKgOQR$cTr>#H6VSOaL!0IXSp;kna`}0$piw@yyKQ$5g6&{;ixd*_@){oA+e5yRl$4tr7#ZM`h>KfUA%n8B)7qMs7o>{K z&AvWoXI@_PYo0!xnu5|?R5UH^fO$(x6JzrHLPIw;UcIWQ01eUAb$tBzxS;_IzgxGE zUsPLr>lQCB7^NE<_4Nh@pgw|QJ~83K%qw=Ai*9WV zY6u}ALBWWK`FW(~@$z23jy~YYNl}rM)P)Onc9WCnU*5edD2T4s;UUO~LPEIz;sp|@ zuU^GI0X;NR)8GGIRJ648(Ww5$%_}Aofj9s{~la}U;XOx zW%N;Bziw`Jbkxyta)JUNGZWfXj5c&S7=hpgBBwMolsLCAn?^+;B^8g!%#4X4RPgL< zmWCB6kjRV(4vvh3ZVw{~JR50D4K3?_YAR9|@pJHVSjp@dF`yogV%{*mu&9I37LqaL z^H@f-<`~z|D?kT=^cK*4AbcHo((yV;NqC*OIA{=|It&OPL~|OA)ep+ZAf$8TA!TLJ zXz;F+(GjB*G>V}6Mn<~3lhLKJlBA_#r2!3CKmg{{oSfof;wO)aDk`d|NKN(fa$)9I z6&F`jq@+NJj?}8GEND`pT-n*#+$5YB%xNU~=dP~Ec87Wis-~Wv!@~y;z_K+kxPO0h z^T7i+#f^*_8g_R(JHf}-*GJD1nQ7Sh-Me>mgb@xMQsi+XLz$gjP!QKEe7M)Ib8C6SBek{IyLfmkE&ch=9Uc1m930lxLqqT1_b?TFNSDTbzo&;a+HGxh zciY;sv#Y3-lstPjJL~U{iUM-pk;V+K7;@4!HZn7nl~GXu8>X-jzExY>j*ivU>T0YK zq2pd&Zf>@q z+1EEYnVPDo$j=YH@YYsiqqVhyf~#vs$JSPRyQ`~`lC5oHMNLkwt>xz<&)wA(%8mQ?jf|j!s;ymG zYHo)9A3M|ab?k#dMo zU*D@&q1HgcIxC%gVd3bgrbb^Mb5>v9@-n;zl9JdRt*%y9-o8z;=;r21N_2I(xZt6J zf(CmoU0skq(5uG|A|(acdgbM6YFu0n4ugZpGqE5ax%%bpwn}5($X;Z2L)l}fTS6$B9fB8y^fEE zx)=#GxX!VXiH=T5VRdvdqM$2@{v$e;tQsLIm8|M3*5l+pOU;b2g>*>qC{l-HRTD8L zVFmVi#0d|Nih{-%E1j?~=&K2(8>=mhMObaII-XdkVU2{999#h*A<$Om<)x*ChEgb? zSRiMCB)uVd2N}{NYZDq(Xs6+sD=LD|1nZ()W)4$&y05Q`OK50eVQp>ZKN2$GaHy+; zl3kUV#f%Ep=qQycEG#RVkbt#xdb+AAnAFgU=jOt(@8Z(gxx5UGn}C3Y1?UeCA6i=r z3f{VP@7~_t$cT@RxVVl^S=s(Rxc(v{va+eENRWw-7Z&E^^!6SaT3*h|LS50x3DmCY zY9k{tF-J%EwOd=^=d`nfE)W_{MMVpX+FB(4L3IUM_4qh6?vj!^I`FDIdgSRTDypoU zmbSP!I}3j*X8DB$@U_&{Q5RZR08!xPOs-pwqcz-{f)P{z_!gudhR)Sh__pY=wQdvhv zs;g~mvSPl><3xVVBsax&6xA|k}ZgoQ&wrl!CJ zlak`#aCb-I56C?H{A332?6k2#5;k%ddwQ&`xwxo1K0o^7vwq<3=o`=Hc1=IbgqH^H^l&){yK-GQbjE+uw z`^S%{{H;@@XGD%KqUS1WIqN3-|Q2|43Cm;Z{iMl#1tv~#MpFbjEbaZ8< zvC-e(%*@_CIyx_}un;~S>{*BxAU!=JgS9$9mULz&QlBUkBdh{OZ*{CjX;i+xVPW9kU@XLalH!Q_p`kPy86)v?uxzkb0tY9P znU$TN4~LqMPeKCx4Y9G_-abBX@e~&)CxaXq8k&_=#7uuD#1!oOl9MrBVb_Qe6`rOv zW}bao8XOu?QJIF_04lOO}3&?x+ z_U`GKokg}gH@Cfge?Lf74h|wB#>VyaAc(`UuA@^_w6!%egB=7Ydy9+n^9c!!ar8(+UhPBjOIv^4f2)6-CL zOG+jrpwbl|FD)%00o^!sN*EDBLdM3{){2V_4HXmu0zmyJEJPlGg9B3Rb92G*wy^|k;nACHW9c!-D~LmA|tXlCMpo?dqL%*^0mc=(MQdU_cdAf^QbC@5%YVI9%k z?d^?T0V>~M{SihXYHY~B6&FWk73(>Y>HFvra>Q+Hkab&7u&~hA7Z9MNq^nD#4Gunf zgoGbm-L$mn>6sZCO;M4JO;g7tL@RrH zcmrx{k)-PEOrfB1mXi}1>Eja+keXV^%u0)n_VIz&D?cAeKiGFeS(TGRR?^|&DJf(g zM(qjA%hXh?#E~VB+5=`?%&>{fZYO5a&nh9zpqL%W+8;^sq`Cl25UvmW9@h+dcbLV& z-X!WGLgz*$0y_a*&2Vne>2T+TgdlMUtNz5qpder0@Nit$85!Z>_(Ulw;9{XRhBP5$ zV(d<_(@6xO<0(zx{S>%-sCkIRyn| zRV*$d<4{~YFYn}Jb2BjT$`vlI{%O|+1c;D zd;C~i`}ApD-OkQ;@A~^qOwOKFRjsRo4$;Qu$`uur+FCrTr{~qHVqy%&(b4vHY^<0V zdU)^-fXA<>n31u)y|RKGsF+xM{K^Vh>vMA_B^@(wF)^o4!vl13^3zYPt>WTed~x^g z<|f=kIy(RBf2pg(XTG&%ZT&C*A}Cl^_VMG!hK~=N>A+;qd3tknn8pL>rB;C@|!os*XFE3CD2#ElEr$m+}Y@8codNAX>5$t zj2~x{3_D@dk>=YI% zC|tYdzvoGp;6`F@bz6=d;7MsQcmvTMH`#p;p5}`_d!N8 zGkg5_`SZ3mBcp5A^z@)zoteSP`o@ji+>MRd+308`C3STq%R$x4M1;!4#7s}m%_SsA zNTBmHKEAx1nkp=O`Ldha?Ck5;H8t|`XUzYFr$o* zV^-AC%FaeUHkB$PqpnV)O-)TtgFVZ^0ajI8TW@b|EnFrTzsR?U%1lsDL^F84N&D*To16l?u)z?4oeqVf;n?8T%mYgs}TT z#RY$Z9S`crU-J>lP5;?+-p8{tEL_R1|iR@N;6HgcTV1d|(p5M~YVjodBuP-kzsz>YVP*lsyD=4I-tTJz;+LD+EP9>c#E{+<`*cfV3cx6}D$;qRmvNCDu zKmO6wl(<3QHqg`S>iX`x`FSTN>>BFpKYmL^I5s#)1MRC3bdeYxH@LA;ZCeqXd<^ zp&S}M^jE%Xt@bl~H*4MKf2BR|qpnN>>;3MWk6ODXFM{ zGPk=69{1_f($W}#zzcJ9w6>0nEHAI9fCCjgAh=OuVr*^U7OSdiY01y0QY|gv@~W$= zsR5D0-kv0d78Ib9>g`S3)+9MHBqTi@32QO`H_b6So58?JE+7DNG}$9jC?IxzW`ts8 zOE^Mgy$6~C<}*0F$*M3kl=NY-9zcy6{aTD0Z~-Gt5;beG@A=&QLJB4xM=DN)I+UG_ z&Z@sZGAGgfggwE)c4;H449Z)xngA0+xzXeQ&ToJ7cVL)!PA4e z3^angyn}=Jd2oXS1=G^N3W|>h*D*GBdmDZ?MMW_&)R|wrC@#j#Zf`$0cyLfxhx#U1 zYN+!dvk?sGot^G(M@Ni;RZNET_;_HTtgOC%Ny*mM)Kqx5yu7AnR@UvtDi5VD_l+4e!w}ZE)s|&pY2qR)*=yR>FFD@n}si>%`A_ESpB4uT`6Twi< z$WTz=;c;>4?OjieH)Y#PF#V*_Rk_UT4OWn~*1V`Cv9a&lT)IXP=#mwG*VBqkuzFt%F z+&Lr@?(Sk8dik=ZCT7v0A$$Al*Hu*DSKruxN>foWKOdE`m>BdMp$XmFqtQf}De(aT zP}XH-$;fbWy15Mv;hRR~0IzU#^x(mrJD7nUKL)YTno0PvwCw13@nT>A9dS)foc99* z=pEg-k(~|Cw68BbZeTMYrCCM>=Mu=U>FFve;ESVYk(;ZkDk9?Ri?v36KAf7?)}5Wu z^r@<{u|Y+PZi${A=5O@hhlY?KVQdWMDKv1ryvVSB{d#uR!-I`YS~@2Oq*7mB4h~`A zl$5Ql<>inNHa2$lh=|?YSFehSpyZ@b78YK;x_3`Yi=W@iYkvO2hn5x%jdSM=4N)8I z?gkf3O|6cZ>o_!IYYUfS71QUsvO=X^zb-1uV7z*Dbd-`JEG!@p8@s#v{CQRue22ci zOG__a6cxeYZ(-5Xv%6bSp{k1Hzs}CRy~ajELrzZIM>c|si{EB2`JxC{b22lItg~0s5M4J`1yf~jWsY)3;Fv)Ymvz$7o*dO&I;+r5;_s|AJ_+? z4@>9{P;sDdijGQbENUnzDcD00brYSAWVqN^>}Cl|19M$L0p@LQ?}!KxCbF}!_jY&B z$Y^Z5cMt1jN5`lr)LCL|Exg&x!RmOhCGhkXDQvu-{7xU_dY+v=fC>-->{w+|Nh6n zVg2^!f6MyW|DS*HNHuTd_ zg+=9+<=H3QF*x==S6QKhW2u3W=7M?$bSPlp5gjoq7a;zaFmJK30|UFeU0p~w15aXU zX?r_AUsaWZ19T&h<#$%&0kP3`{uk0008z)JyVYkz-bB`AoKQ$QdgVQUL2G+tgVuJG`!t>fc@0`O7& z{1z5oyr`_y&_IoAW(G+>s;cMD-@g6m(cxiBi=N)ai@Lf^O@!Hj9aB@&yLYp*?(XdD zii#lSY;9p@EhCeYb9A(`6BEP7$Il-ZhtHFiCM1NaAZm?R|6jiB>^w1XbW~BHsCelT zdU@~OJ$?+jkCoMvCm%ix3|Lx%uJQ08VTiG@$;%fM?CwrXczbhmBe!8|3*H@WZuG=< zb`B0040(AWArO3@J|+EMDs^mZZx1YB_;s3_S6A=fzjKG5-^i${YHsfSeRFdmq1(6X z>vwliFNKb*qT=b(>FJOVR$ev9s1_GbNLX9j+)Pas7w6`tP(J&QK#_m;th`)BN9c^MoPS=mdMP)B|KyrxE3`RrLFlOG&3H|y#m zN$b%gu#K&(u3gj8YHt4UVPOG@HE;;NeY>^>9ipfxx`;?A=jKMGc5Q8aJvNqyhm9>T zaAgHaHp0S}E_rxBzunNFr3Gyea?ZNC>=D85y9QBqo9=hu$PP1K^#Un*#v|$vk#;gM&|>mXs(c zT)u2&H8k}4HPYpA&Gh!Zdp9^}W5dg9Y~0?CqX)MWQsPWvuCIeA-Q955oszK8Ug|!i=CW6>B-K1=T2)Y{Cl9QA>9j#PMmMr+BG!? z2dEnf3#+M-#B!v%z~8gExwI4;D=sc78WuJ=iS!;B8KjZ-_Rh^^W~!(N2)MgHd4k#n zG+WSbJ$u&CVQ()jZEO4RAyk~s&SGLnl-SxD81VK+-7!0RZmz$dN);7VR7^=hq6BDh zl9G{;s7z&N>*y#eM@B*oSy-s2CoAjk5BDryL0H(zs;Oylv91n@g-~%WE;cn;TMG;0 z6*e~q2mSm&EzZwhULG5ZhybZLD{E;9{WN)bSy_<%r>0U;l$1n7JUu%*$H(b(H8l|t zSJ%gnk?IG%x3lx3N4vWn9nQ`Q3ikG`t-HIuy&fLY(ndxV6{ubK`btS57h!4&Dso9l zMa9I#$w|;yWn?5JksG+YoR_Dkr=%1Tg04(yDd>b0%HZHGGsn)v1gYzFbqfoqQo&DE zRRw<-@{08IkW4r>78E2atEI(YfZ7)q2TE&t`qC2eP2}YzB!YsF?CiF-V`HB__4VDo3#aDb;5XlljlrL*rqC@7Zn;ZJH+1XGPQYh%n zLU)g`_$u=T!q=NOa3pYZ`};31zkFFOOqf*a!^d<&~9%?saD;IT@9yq$G?W$k9Y~ZDeF|F*6gDDNj#0 z0n^jv6VDCM1lEjE#X}sjBMd*WBFI zm7Z>9rmO4cS6A23k(p_0Yita1+@nVfhMnE*+u`Be-AhXi4N&q(Ng-=-c=+DE$VgAm zpdcvLBP0Eo31>Mum6bU;0Ri^*K0aAl4Gk3)p`jKQ*4A-x4Gj+-WMnuwff1UQ*VI&A z9vSKE?B#`S0z7S0D$>?a@qiDEMLa@xlkj#kGoz#Z{GecCFtW4b<3VjkmlO$EMCAqD zJ`xttCnfo|q)Us;IH+3*sfVRMKote9A3TQeQnBuUj)(YuP%Q%ICnW`sLg$z${RuOk ztXj}rhQ5a=Z=p^g{2`X^pY;l1VMJp~5_gee2Y*s5Gg%i}2Hb;^fe_{+BVAqH+!7P( z>X7+hZEavcq14q44wjahnxcM*4(!{vH8mh&IXR*3*wNwWC?Rp{R&nvd0;tOB>bkn< zsW&%AMVXn|+R|tZ4Fv_r*K~HicW-tUX(i&~1_o7CM@MUG$TNl0cYFK!^SnIxXYB2J zdNwzaXDTIyUIIo$DJfhV^YefE+mk0+TEG6aq~wDKKmGLjb!g~?3mhD2Y46{E`jnl` z%X{XGyZhSOx8Jt4sjL6~_lk*X6Z4gBpxr)dY0%aZEJNl3D@pwkmUOhbg{NU%z%ZrYN zb~Q1vxVWSwIoY3i!?UNRdU%+bxw#b<4h{A6AS*{!*2Sf^w!goi08Cs~4Euy+VHjC z`@jAgiHclY`ueS{SkKwpU%iUH=DT+*D_&j~F7WVxfQU?vYuBz{4-enm+}}@4<>$Y4 zEg)cP>*L4TS``%z4y;eV{<^hQSC@^=)^>axnUebYr%$V?A&q8Y!q)cuc}dCq{AbUg zk0xvE{r!y%P=2MQQ&KjV9)B85ULHBID=S-DP?z)YP^n-_fPi@A3Vc7Co28{UZd_+3 zHp6xQ=#i-@C+F?k9UVjgAs_&+&*34C5)|0TgC)BXFeN}fr83iZMMY_}r%yLGBO~Do zMB2^AkCl}Q3T$ln44;^O6J1^SO`!4a>M}Kj-W1s_aNzUu8W^C51~Lj4mzGvd4OYE= zexM;16(Ri_>j-EEwzs#oXfzoa%)vuL)6?h!LK%US!pKPMC4z%jS6{uVsZmp7XScT> z9fh;V+#D$s-QDlrVb5^!qMTe=+3VNa+h7wQjSw0OI$c77=+WQ3gBR(`FE3yA@_OmX;vzG8pI`VCI6J6AWQ{SkEG0Rh3mxH_RT z4G5r8BO@`!q@@K0Vm$x_IId?3#m|pqTgJxv`eKA(Fp!1{XACq8@Y7*z$jE>mH#GD! z#~wa!Fc87kha!qX2@Xz4fu;%>ON4ul1PbJ_L9v*@q@bsz#l%3h70;x4!>=9|hCUGS z2!p#%GGB1q-~@(V3LO2&NQ`G$S)rjWE=c9Pf4{aiG*nw#S=rmWqhoUuo)4@Zj0gz8`ZZ0BXYFb;numInfsHmY~ zS=sV(cQ^LUnwr_!NMnwU*3`UpD>rw3eq6T)33kL7` z^Xlr@b4^VliR9L;l9KgxFA`SjEoEn#KvkeZ_&}+-MziwP^+q9R)ptVTU$*n zB;?T}WY?LSW9GzMTgFU}Kng`y*V0mbJ+dtv9BOJNCg3ec&P7oXXzET*($ac*$d~Ny z#!3Y_fdkB2N($;ZDJcU3!^0UFrly95F)`iUgM-NYR8(|xtE=nn&CNA8NAftv{^DX= zTXl73XCzo66VcGn%?+>i;DNutzP`PENy*q)M@Mk5uC9qmYHCNv{ri4?>gxLX2??E@ zEiL#?w6$Yn`}zh4)6$HMZ{CcI?CwU&nw6EYF%l`}=8B4L-&Rm?b8Br~SU`50f`Yku zQPIFaO^uV2va*FmY3am7YpaisipuTVd3jL9hlXlu8XH3a3ZEEqX`-Wth9)QJbU5Wb zJzH7^2C}n}Q0VR5(lRoVpMU$dyu6zmGN>CH?d=s5;NRQa?Ca<3h4Fo^Wc@UvxD%0J;cC3RFsjCiAh*k zMFldBpgUx((Fx6gMng9iiX`OL!dZ(EDJdzBdBey+p+rVPX^dVcXjQ4H$SxpiW~_H} za!CGALPA*?R#G7R`}tw*0pEw07nPcn1T}kH95ZTz8XKFF1KPj8zn>pTR!&I3H~}Yp zVIfv7@cz>2#F6IfOFF#i=}5@Hil&(9119OX@U0=QEi8=qeWA33UK)Mb>}>2bf`Xv7 z2m2ffD~w|3TND2PJpAeD#EA>%FmcL|6&uOeA+8yylu`L4`hSc(c)g?~Xt$8{j2!9Y z(zhPtek`xEQOMgoN9-O-QTg}ZyMUh|d;sq!I zA|j}rtgN)R+u8~W>gyx>v$NCO{K^$2rOL|JuUA(C0ysJ0Av-$S-v=v5NGLV+{$;FR)|jXxQ8P@gr6$fB3_V8xJ3T`|ZMlgTsXj zN=i*lfBDPKj=TG*Q#?FHMSuI-r%$o5r%(U(w-n0m?wdE6nH(Iy`<;UW7*quX!op|I zy1Vb}{Oi9ye5j~+>XfDB+S*S)jg4t*fANL7`p5`;1ZHNx`V}WpDy1xFy56#VTa$kOF zVuF-|hYyvMzW72z19^4h<3>im{jHoFvg8*R?d(pUmXf-654rO0?q7W+B2rcL_rJe> z9T@oKm*>x?rTyhENK-j;=GVV=b=}|p?z^I*YuA4DD-)B&#lQZwrA0#GfBX+UJ*25j zOc)xTI(75r*w{}$t*)AxAuF)C`KO;wPN>x1|NiP#8tu)Sb6J!fX#ylH6A(71dVY!sxx+`4t)f~F=s+Q^GIb4F6K zsOaU(tt}9e1O#GY2uDFtQ9=TQq<8OXYvts=_(DPgncr(`xw&R$1_sDEgQ5aS*6{RX zWd#O0IZ-I-=}44DrXzHSNJhtw2fQ7U)P`ySb{@nP7ag6Ig|jQ;*mXhU0pkqhe_ z=t|(4Cwp_2x`MSogB~2KiNr*#h~nZfmSH?8DuTBH&qt%dQxF{uhdowT)zxWf6biho z1qDR);p9Z6(&MEaDwJM!qDq6`T^N+g~aSySMZAgK$qAk+p3-vlEFbl+Hu!Ihhy zj+8IxMaX&$l^0eDJ(TnCN|lvWRnchP-cC-z!FhSLwFL#?;b8rsXM%ijD9(w~FEbPV zWLH;9ODCtqMDUwBI-;TsnYXyOfq{jE%1R3hIXRYl)WbteOic~hyU5{{U{*>oZmg_i zXRE1+ih6rveOg^@Vj?30&o#y`3k&Sh9zTW_&e#|pa1;;p~osJ0$~DrK2=pHq~Pxn5s{KgOWWPu*oche;^O9xjeYj)=qM@a%9Ss_ z^zhi-{o#kYI#JOtzA!QxAOHUQzCI+^-?=k9{M~m00|o|v{G*muPtT7(E-hJE{qA={ zLZIbuY*49JuE6W_;suz4*RS*OrKY}kam1wM3kh*^xs%$z8i(90_y6erO%(Qu0}^oN}_VN zyu7-ankpkhXln%)pU{bPG?&8G8{aZHzp=~58kn3%;MHK&otB1tc`|21MGV(E)JE8m!+n5S z2HZh#X_K@zI=!l@xHvrA-X89Od(0HrloSUCbMxTfs;aiO++0^z6B7!hs;aNArpDV_ zSJ%oaC#SEkwKX&pOv4L1_rlpCnY_4)Y+MkaOaMRNpy5e3sPvUt--;nuCA{~Ey2wV)sdtma4wKNGB}u> z4N?p=lhe~JEe;N%qJ(VF(*t*cni^>S>+8rTmyke(_wW!*L>``qh^J5Ay@QYW>{--` zK`c~Ky1>lB92j`PbQ84>rG57ENp%rt|9n5LqjJfR#zDe zH8m(v$HzCA^d4kqIytqsPfliJ+_(Xj<;cj!MqZwr+|{f0_T%G788I>8;J9_K3sO)}Q9<@M*1+1@=t1=LZErU=-e#uB^7G?U zU>D`>ZD{D^l>Lu1V5IVbHdRrPn~Q`OclY%4hKAbOxHwx|OUuyE%F31&q*2|y>*<-3 z)6!C19T8z;2TvvDRUVOZr9rXOw)s2mGx{C|)j7v%y8^J%bu%J-#^P8HAijZgQ z>6x94q|TrqYioD+%*@6{RH~74oSxp&($J8Q;OOY!5F1{? zN=h5^mYi&9X<`x_TwUGL0{57OMR0IMMN11_&Bi7$u(-IsK0DjT2PvP$#Yo6ODo$u< zMMZOSb~dji)V8$Wu zCLqAo6?=H%`9uyLyl!+l#)sJ0f`XC~Fw>}1@aA%IFb4z#WM`L`CnlPjs;W9WKYVz6 zyu6G`g@{O0)W*h}H}&;;dV+%P?vs-rKlb;7jDe91-e4yuc6J2?q&$s{d3&QDL7d3` z{@mQc!bwRxJMcxqiHv^Z@o{dhj0_J?a4@`(m6cLbSd+mi-PEL}cKS3r9w#TAo%;I7 zXaNm&e%{d$G}!w3KmU1W2MT^+;hdZ|Zw?NkquJRxIpgCG508&iQrOu~pZ4%rS$Xv; zKcAQPH@`78otOZ-MN{+iX+y)Ip@03?r6mW4OP9zq?e4Prq0gS}?j|LPi{omVnSp04Xd+sVo7?7#Yz zg2Mg#pFTZ*o|B`cB_jjsT31(jc|d@j9aak^C2-55mx6jS+-_inki8XLoMfeg>_L+G zhtVG%PSD55J_-4M*sUZad}iojZ-l)Ys;=My{Jdg=vm+%1v>?n8(A>iBN%|;opRyQz zaC(wX3p~kLSrHLr6;@P~pC1!L+}gD+`&Oetrc74GnPB zSXg*^G8pIt1qWMMxwz11_4SpNQUAzPN>0W)FErH7&fT3(zkeT(K_4I^qrJVQ1@25s z%Y=j$<}D$?-2Cp{`1pqpks@Wp%sWEf#=roBVP>YKH+qVfw5qM~q2Ac;OCL{-($kikHwI5AOAPhUSN zX=rF*{7^+S-uY=-^ONf;o{&)zUIG&B*BPZf*_=($h0DOG|t7sIf6HP)EneC?%!0 z7dmYd6C``}^&uC^)D&~&6K0Mrjdt^S|;jE^raG8kG~Qc{?|c9|MI7`o%8qK+&nr;Pv_#osIk5M zm%p^MC@Y^kXJfOt_;3F+ARKfvX0(>(i%?AKTgS z^J{D4h)qm@9;&aOnK?BzG!!4Nr+4cXjW#rdUB0fahDK0OQ&U@8Y^XC);A1L&kFD{E;%8#O(h zpMUe__3KE1gVvy-Au7tn1v`ZtCIt;$$H+(;4fHTjJK>!LK@AFZc*?OJq|rh`s8ow^c$$=22; zCV_!fRqgFXMHGs;xtAAmCy|kDZ|~tjqg7Xzmcpa$>6x5d!A!!5j&@<*qN9t8%gT`W z3U@oaa;Of2=|Bhw;3q~!6%>@0r>Fb*xw&D+EH7s;LPI?~P~9#oD=b7G%iB9Tn!!L_ zDkuny6H+zD=fkLqJ{D+9a2cRBOzOw*7?2SVong4ziGGcB4{mfCEjv3SBRU!tDkOvB z7`VFLylG+*7x(z_)Ko(Qm8z|6Xc!SuU*FuEl44_X`!U?%YXE?q=SSlZ}jYb+M)% z9W5-hwA9x2_wVeSpTBnxq)KL3gT&%CRv5}E6H?OQjA}~_EZ{5nzA02IKLOK<6Gz$wzaJq3r zM~6;dSeTzjF1WgSa`N)>?ru?$mKNyjj~+qUa{IQJn2ik*fg2j205dYGu3lg7?e+2k zGbJ~7VPSX}yb%L~oE$uBRFsm^jT=z5jE~2}fS=;;-`_tmk(4ASCn$(D>9c2Wt{@+~ zukXW$(NQNS0Rcn9=4QBTe0=!$K%G80A$mA?;wL87*BK0~jXgcPx-hmXD?>fh-@mn0 zQv>IdhsWR`XxSDPs83;62jV3sr@DH1`Q9EXV9>y2WNdEE%^}@OL?kY5Z4IepSX21> zPfzdfBb^h;7+HP+TygvB!Qd7x#PEHQ&^o@;Y z&+_x($n^DvMkX&$S(%H=-X0m042F^tTwT})*VgLla&VZNKYo0CjC?>TDZKK*0Xm>U zLU-=8wVj+GV*pyR(o&GkLH30Ha&vQXl0so;7ZOTI+1c6JN=y`I-jb4@K7Ib2!H|^| z6hu$*^=t4eIXPWiMn-Ui(N(jx#VeCD04g@5%z-MRt=-W9EvBR6_3O}Le)zDm;^V{4 zE-PD9bbP$J>hI6SCLjPh;PkYQ4^oZe!;*^?)4-@=)8<%*6@Q`7PB=%}kJI=6Lo zFJ4SfW6s9;22E^4gdp>VJs`Ak;7d@cgM(NVfM)LFGdKvUs->l}GIotn4cgf$C^$JG zR~LR(C|C*$XJ$G(y}e~+G5(B>c69{?Dk!L_B_@uKPfcZH7#gDH(A_;biGGoR0ak{i zqa`J_wi+5vPNk*Y-DPDUAKTh87;SB3Wj;P2N;4Q89rg8LVeqoi=?@<^Hzy?6+dDd@ zrna;+Hzy}!j~NqFSJ&K}mgeAKYD%T%<-rwZWp(Qom0DHR(^FC5;vy@H-cDKB2z=eLqgDN$;d$W67@;Y0wW^>1F=uYVcuB%)%0|9NFyRZr@#&Z^BZ{|G#XK-pvDM> z80tjOlaTj?3Q%}BI9%D;;KTU)W4!>145>2Z=jY~vHG_Qy_L?-BzrUAPL+Zk5M(3r zn6k38G%D5AH8eCozov%4pi-@^Jw35!uc;wf(Gd|PB~?{vX+AzKE+HW~Iplq~y9WfM zrQuV+O^*&$dOEtt=v?PBZ{X;lCRueu9FaM$5p*9*#ZI*6LWIF z1i(roApvRev9T;_Bsx;~IcmfBd0?Qw|7SK&P*7wfA&#Ij9T|x_H}+|y=Z!TBy6Ir_ zK+6y9J@jWRPAxj#gdf0S1YnIrNN!Lgq54dSk1YBu7-~@VLH`C~4`KG8w+{XZI$2<> zksN!XPJ;Rl-ym5@qRJK)2DTjN1ZipbD`@bbmLoJQ=%JvPgU*1w1Jao%in!olkg!0M zgmNh`5KI%)E^*%DokBl=_d@gu&{DxYfQ}pZc+t_QpF&whJ|mP>iHVVsp!eeakWY`t z5%nIfO?*l`8mcU!4aBF!6-Jo#ELJ8e^yEkr8Vno_aAU%2K?q^2_W&T7zg!1lwK5)gpiDycWe#rgXO z1jNOmhl z4)WLNizFw9g`sYZ$J6QPB0~v+)jGPgxSn!zb8^t7_4Q@xLGd|p?!YqwS`hA&Q4aY6 zbY>m{dMV^6F&H!&UKyh&6m+CAj{9Kl;K<<0Aqkrx&XaC0^mn*=@l3>_g6|gJPdJkr z%_2odL=gQb`TlV~g{gppu9S7;KYyS2M#x#q(gu=TOK`VvCXh3Ys7>)a#Pv)xIz+|F z%Kv21wLY_6lag>BR~yCz7O{)PrN+0_JwhQ z^tV~p1YQkSWz7F&=dtd?{liMe{CwSDw8E~6e$^ zfaQuO=Tsaso%J(K`RDgBg2TtgdIUIO|NhJ*Y-RErmIwdy`OD(eu@vGgGCUb+F+O0V z#Vm&Wh{Qyw*@-$1j7qFZe3-X{gyQ1D!k8FzZ_#H?Pj__G)pc=cWF|Kw2?*;WN!~15{Ts@BFJiZ`*w8H!GWD!RkgevDlhnwWMvtQ?d_>4?1n@|>GXqx-QD#33xE4tf4_kNQoqK=e*AHA($w_yX+_2R_us#t znX$J=ovx(h<;%rIclUGuhrPFoj_b^}wtdd&K7mf0*l}W%ZCMsrvY45fnHfr~5;L<( zj3s6)Q3)+(X33H)vSX&ikaj~l>DE^>duTZ4I~U*hZ~xKzqDMt-6>Qbs@4MET^O@gr zaAamadv-D>h>E&@|LN20Y<_-dDR+0DJu4{S=f8Ln)E^Lrk%y+Q-`96^R8xcZjl6TH zOtrMmpI1}s>?EjltgIiYt2b|=zX&=wnG8-xLc-P-IKM26`B-@P+S>YhcsL6Sl(NV? zC@z+fK@VVc_1QBJd_gXnpa0>9jt&hCaE0K*fTs28RTY)``lF-%esgos$WZq$E_!+0 zya{qXh$j&dJUr+!93CR^{`PHF*1*8k)%|@qR4!ezw@3C?ULI7lHa25p1l#ZOWq1=` zy+Rr}awtIf8XL2=zJ6Uv32E_*izE^g6J~t7yU^%fzfOFfr6qs=>(^OW;C;hHLQD*E zlJ#|vk>%uW--ZwF$Vl*cl9T!QK|>xKTwbP7kc@>aF}MZMJB4og$&L2PX7>>(jU<`d}Pn4;dlUs|f9#KS`- zFD*TP4%OqeYlem$9R~-pdezYp$vGJrA|ltX<8N}nNMBP>xPINza%kxJbNqEVIgy8k#1UIt zq?9){K6x@TLn7U{Au5`cg$p zn}7faKm!9SD;XK0qSvlLYrnNsUaqPNH`>4eH2CJ`w{M%8c6B{@g82dTWp#Cr9^o3` z;1Czj$RNH;?Cb&piHU1#^Yej$tc)WhWMkvO1KjOy--ZSRT_;slXuOeF1B%h*%li80 zakaJS>R!DHjT}6ChK83f%gfg?G6m-6!EF{10mTTeUw(cGiOkIXeW>DbJw!)uZfH%m!<@r8;?PtT7(4h`w(oIWik1{X7tK`A6uTKe+k_BNCOf`WN@PoLhq2f{fg zXJR6ztC-VXy&4n*3So3K=!x#`8ynA`7Z>yMfAfu{<5L6{L(H!4s2U=q-?B!^g+L5gfd=_4qL~Xw1y6uFK1>Ut^|x z?wpnuw2I&y;`;9C0ef0O;j6DO-}&ob=jKdI(Bo=s{Oe!Qo4a_CkFTWU{riUxLqo4z zxqdx58uR7UR5mtH;jrF;zVzL9hK7B8FJ24}o0@+9`I$3zc9;)T3w2OT42XST5W$}V?Kx^-%n`uUg|Y|rZbZaK=h??S2KE~#o_(U8 z#D17?mi^D98PpP};!bJ|)Cng$cl?~FVhR2JiARmtixWQ!Y_x~ z2em2TPC}>YB-aA;QRtMQrgdW+k&y)jn7(*<+1Yt}r>9p{6%~bs+A)ssa4Hqu6<=Q$ z7k__Lk2q_tu3lboafO9cD)RCi9esR~lS@hp3!|e+Bo~*cC}`=TqxJNprEP6-T`esk z-;I;g)pcZq=+7fxvZ3L@gP9pJ85FhLT-+VNWgvi$T%3>)uHB&_FxTPMadGMC znV(00k&BUm+tYLRZc~%BwY0R0OGn4X294(E2+ew9Bk~g957X47P)0`R^q?RmB`vM^ z`0nnuwtxURIYmXx)+Q#>($v+(#i78So2#jDa?;fF_HJ#RnW?BiE{nBwef`=R;e7)) zeSEyD3zJ<{)%5h)+2P^9K+Iwi5|)Sm(1h*}vQ3*vhT%^Klh!i1$e_D~QLayYZ@cSEy5o zs+H*MV4sg`@ubHV@f#r??-%cY_=G2QFs>u?esEpF*+*oiAbE=DE`*1F>|dZe>g5G5 zY+jzXx27h%5K~iITlMvDE!x?E9|=7gFR!X9IFZPPMdBK0jnmV}RYmIH;UT!TpMLtq z7vbR_J{%ucS6{n!>XfPJg9pdQ^YaP{r%p*rgM7ZfZ({>BWJ$@ZS8Ho7F5i8}#g(1? z`0>`3ukYo{tgJ~%-+%w$L1H2Y2Qza}5J9RC5kW%Y#>SH;RI0G><;zY^NbV>t<>xh@hl>l|YemJ`v!_mJYj1BKAGf!2a-KRRD?2`Ze7vw=WCZP8N5_W`8ym%YH5i=QBZ(h4jQ?_LKPK|xWSIV zH15U?WDOy0N<-tj@8sl=h}+d=YI^OOf&$WHCnp^puU$hn_0y+2JK^CxJj6N*)~+Cf z7!npk)-rl~hZf>r*nM8t$ z1J^XNVuXZLRkN}d7RJWH!XzZb#e;%+dq+l6Q=w!D4({w67)VPqFhCw-d;7pZW~RA0 zxF_A+LqqV%sH=yDc6AL5V45o|?CMIRjgDqzA)(d9rL}c>y0B1J7x}5kgKcSnU&-FS zwH4e;J3A2(Gqc9VogL5x1O&kLTUsJmDnzd8@UWK`2M1ISD=SbTN=bo%xv;RaQ&gm? zDk$jfJwAST*w6r?0LYrqi8wm)^Q)`pq(S8jPZNQ@yN!oqB9#>Sg^lZw=p5k=fz^}f zmZ9^6tD0CdF|&u88$56L#A0JJGT zg%xUYq^h7kcV{G!pgyNkV`GtV5*-cQH8c>=DwLMi)TE}8NLE&Xfh8r4jagYFlA$4q z1f@f9v9GVGse?mua&2u%NpLXqjTB0Ab4^WnxP^tabwWaOb4yE7lC`yoNl*}Ug&7%6 zPR7RG-c)L9Yf+JxmywZ!LrMyCQjw8PP9#!%d`StWMqJv|*9KrXJWrBb1Fw6KVXsIBej$j!C4S5VN`Pf5Wpw6YRjCoL^#V>>z` zB2-mnWj#C^8it07iYzQ7Buq^5@&*T?Ig*pp(Vgq662W?_yrK7{%9z5ELiv9hW89zT>UI~e`w5=`VF-uE}i$_N< zEUc|jC^9laLYNTZv6vVuEBqs?tJ&ES64$R=TlX zNT{wUlewbMvR4YHIlSKL6aYzn_IgKp;IGep_%-goRU6@7=q5H!e<45MI?iMm|MC z!mV4^uAw)9Y-%1JNmhJIFRiYTTVW_wK=|C@l@m+TFXYtqu;7 zj9j{!8fa16-DPA94YRWc1{xYXJQNj8OmcDt2I}gdE<<+0?Cj_$lw%qiadE@LV`FJ) zdV1>Wk&%Ogv$MImT3UjFR#ugjUy%eSLLxP=v$zG%(QDmzZc|q^}*#1;KqgmJ_4ZP!_`V|tcWkVy%h#8i8_Dv>r$(Nbg@wAh zlT$`UO-)e|vSSDrdR7)D3DDAGr;M4>i83fG3{xAZsKB9sZ;bH8CME_1fai_uAvV*+1RYCIy%6^25CrBv$}d?qqP;D3?-$C3Z!-U`}6Y3%I4>T zrb47Y$Hs1Mf{G+21`XfX7*;kpM)seT6{-U;Hb8#q=-A#yHY#}hot;mf3=P@XaB%4A zf{`^nZEw%SBqdc{{rWXn#m6egzAr@g$s|NfU>>g#!U zKKsnvd~@^ocwj(I?u##Ubmr#Z%u-T1b?Wx*hK3IxmX-_+Pn}|7%FTWK`tDt4=Rf`F z+O@2#pMHA%Iw9f44HlN9B=9z}vT*%HM124Kt5=zstgJ|&+28-^r}A0PwVS{{WTm3fBh@&UT~LHSO4_Wy?b6>=g+gT zB_~6H6cohD3P=6!?)rLYC&zH~;1v zQPJAkw{N$$TwTwdV__lK1i``RxJE>5Fj!#m@w~k3?0$ap^XMvRXh=u|1x-wVcciK+ zE*=mtK8{_OswxkUy*)Co3JY~~goNzu8XCsO3kwYmB_&Cu*4DYXiV90h1qClJ^t9{i z&CEcH!XCS;%ijL>ZDr*XLN_0ugap>Lt}cInF)`$24h>CDXJr`}sHlX5^!G0=R#!Va z>*$cl&CL@NH8pN->gqN&+1Ye@ZEawnzP_1RR8&O;6pn_BBP67@wyUe4z|KxgOjOj` zIxlZvU}U7aIwZu}+SWEC1R01>O%R@~0>%*+=j{zTF@@n)Lxw!2u1K7Q7Q@#U4mc#+ zV)c)UD`RAQBqbq3B{a0Ku)I7a#ogV`E-6qvY>zZx62qQtZRR@CjmKsu@R2j4h+S zjE=6W1Hs1L9w`AeHBC*4iB?v|#<(B1wk9Ot43f$4A5*Co7BVt6Hb@_DZuat0Q86_| z7242%GiYOzk)afETBQ#-_OY4=Kks{L&M3*zy5V>OjGldPeet# zyN{25{L$C<^Up6_$jJD|KYso>CFR_?&p!_i{_~%YkE^OKUp{rp&~SGbH3}QssZ&x? zgsT$O4j0#*JIBZ0e{W#{H)un{@$nBoczONtj~6c%6&)Y{`fF_Lr=OlW<>T}6cg_x*D24L&N&|_4TGEBO~;jG5tYi2s=BG83I)^)R4$y#C3f8wvbR# z(#FQp5)$g!*+HJzV5Bf`b2Br0c`Yr$$E2V@bRQo*A~Ku7P=D|M41Vm}N=jB%y1QLm z`1q8Sp^8U#t)L*X0~QzO=Mxg7rG(|}f`ujIG zJ39RR_4Tc-pfH^&;;&81Yi3jTv=Hi$=6CE$)rC}|DHUPb~&`{*- z5HtsLBk+6Zv|$oY&>x^62nj*{RbJjleGb0;{{F76etv0brKS1#!NG92#l(QTl$vU7 zt*!0mR$6-JPHio|lhD_Xj0_FM$E&JJOOwgn-O$RKmgtfz2`ak1eMQCaa7BfKgMxyMO>yzi z5Oy?LS`H4y#e@$|Tie;WtZZsVT@9MFMDI z;xZ$rFgjXLkcS6-sHG+NWH>mWn%&(kEJS*^y*<>iEiI_`tgOJOYiU7NgRL!?do3+m zT9+cIBWGromQqvY8MzJq z{(XI;qo`%2r2PFmJ9~RUp-@nWh!|iT5fO573JQsd^YhRdD=H#KYGY%2J3U>3k-ZTd zyu5sHke3ICGKn-Z^WZ^gshHSz-H0eIDKHtut^f)3Y;0a$$aY9dl9Lk?3kU$yB_TmUK~gd}7+i?FJY8KmIlR-kxuPOt zV|jTWAL1`-W+pA|=vZ4jK3-XAV*4}7 zS3^U5yoH5<0hIA=ZJ=Bk8@svXT3%jNRXsgLMOeFBU8&Ue z_QFCENl%YNDk`GWOH19|p@huLY-_8kLam7Ul}0No^Yt}0c5;GR^5iZ|p|rKt*5cJ{ zZPU{`I~yB=gLQO_jN;=vJ3BgJV)XTObt5C`^nro&bU1Q6JnHL*hf7LqZ50%(tqI41 zx3`j#o?c>NZ*NaeQj&>@wzi*NO%0u%pAYtvn_F&fOG{Z9u1^;iF!)PKA|o9got#j4 zmzNW3UPwq@9$W$>629l<<*lvR*%lUHF{Y&||vvEU47s;i@XkUBGx;TtuP= zD=YFT_V$jBFhS?$4hUFVd-e=z5VvkQIZaGFeTr1At5eYdbml_;FpGg2I(6AXx3~ z)zn}Lg}E;@x7yn0&Y@O(|9*894pVXQ^77ZOcX$2#n3&kvQ&OHhdHOUpm7V?kc^8+N znZ3Q79DaTzKnx7rzu(ZHs(R^?hDLKU=D5fqH!`ZK+S(c!2@I5%H#DSDmzRf!gM&eW zjE=^&7#pjmrl1fIK%;ecCnmzveh%WW5Tb!H@4)yh*;!r3g zlC3TBDRXl{5QFa+E;gu3;XDR+%+D`58M}|@Xz$;6LEwF$0zrNf)^CCVjD$ESEdvAn z{D`bt)KMon+9zsOXxIo^5R|PaeIKYdb8|u8aCZme18Gax)!;F%CulLez0vC_D#GrF zV4=bBa`OKmcacbG!$Ii@ zem6@?YiqFXXtb0RYik1ofB)+0?(V`uCnr#C3JY;Py1J^X+u0Qr4G+T~2F^@s>hN%1 zUsRMDA7|fsHm;u9(dElfOQ0j+^0J%T)vHh!z)1i)FgG{!{99WQ5o`?Fad7b6yO`Wb zOS7{h#p}U?x;kB5&;d9F zaZitl2{azv-9P>~Kkw|!%q%5UR`%@K)>c3O3xmoVAHU6@)j^wb^=d%C-MhG(aC4tM zYh{J()xJK^Mm04FVl^`0&`*E#XlV&k1UNu88Cg)son&UlTzYX4I|0u zySNZO88b7i{{8(=o(v9xG6xsS!-x0=u(Lxgv9PeRl9VJa&c{b44-L)FqoapJCb%tY zYK)CVMV+0&xq`M|M8wJpj=%nXOzCB1GcpzyhKE6-m6c6Mz8IggEv-|h&YiFtYw2_f#nRHyFg(1eiAIZw z(bLn`j*RT?9vnnka7bJN!+k@EA~+bb#p0!&TeMaPadCWZlBW6UxO3W|#2<59(dganF1 zFe3#rA_8jcxVTWpL3C_RdO3vFnAq2T)K`Zwbko=Y6Z&E7hml*1dnlRg;Sn61o=&Br z5(GgJJ>jx4!jl&m2o?!)Z7eO_-6<3#{{{zR=aH0DRaIJw`Ja zDl2PiI{{5!q2rPHb$*oG^6iiJ$JiLFOLgC@Ld>K37-CgvYp;hYY+SwsoUAnq-`imEs z7qPM`C{$EDdbG9%qBNm^0`Z%ho0AiD7Iv$Gf>*CXsj|KfT?{+Bl@*;1W{RO9TxSF~ z*}*|j(A2cG73^L|MwTi_#)pS+&2Vwuz8xAmH@C8ql7fsjfB*4u=wGCziTU`!K|_Oq z0WUAQVfXHJb>R+VVp3VTuz>jwH@A`!>KKsAq@;9ova`X6f`bNAC}^n?5|ovdl;Y#3 zrsn1-6b%h|`S9@3(WRxlJS{Cum1wld$-+W?eO#~Q2zOT+y|4Bu@4IjR8-W|gew_yc6kQ5HZgIW zaU>;aXsD`!Q4Ds3nwpGEP|)Du{Csw{vN8{kjZI_Y#6&>>)(~s!y1L29k`k~$?d;$m zZf^%U+sdk`iRdf}3nK*&&Seq_EXSOj%}r#rb8vuJu(AS$5jQs%S4hYbgTP3kz;)*D zzqhXnwnm?ASG2*_4Mid zJUa9I`~?NipWnL|62iiA`*vE|qel-OBqXr2-@F+ad35yjDTTtq^7Yq_jw>tAo)s2y zaDZh1cUfDTxcDFbpr9}?@Q;5iEm>M3-K(|rr=Ql=oSm;QsGT4r+`Wq)BO6;**2|ao z?}vn-#~2ZDboAgsOw6TAXU>qxhllUqmy|%;V`sOq@z=k$waLhwJBRMV&p!_gsHlAT zrIJ!_?}raFGe$;VeFg8=%a?O=*4AgwLKg$-w7dJcb2o3Mr@wmj^l4%u-ZwNdPoAJ( z!ps~P2yJCl1CqU8zaAU2u(*C*UA?{i z%^TXx z8kChW{Xq_Ed%LbK(h7QekB(?GhFYDdS@ry%Bk`K7RMb@kh~ySrpE z^3ZZ}o<9dS2(GiZxPyc5zo$@c-)3TRbDN&t-pmI4A|M-yeTV7rS9*~ z&0$6+Cs$B#bhNPneJIpwySw}Qsj0lY*RDA`kB$;4cuY*_S8r@oR;sJtyh-$qdV3um zg@lZZ>g(ZQ_4mhhUsSZdJ~BPuFNN?~C=Jwrn& zDcahKir_s?PQqmj`doMS>@1b4uaC|u`ab`AQh6B7*$p`lJr=&FFEhunG! zrJ^D)&)@%q=TcP_5rJKAbTnu}Nl6Y478XH4rKOmbdU#k`dU<7LR#xWbf>7n{ot#Xp zZBR&{`-z=A96<1yfRKc>5q_N$mksVh$t_wt4mFFb~ZQn^P^ImnsReJ zJj~2IJU}=vD)RLOX$D*uD%IN?JxdCuwH4K}o}RIBY;0Q_ot~CvZEa}i?_XZt)Rdm? z;$m$b9$r(^(vq5LW23K6CYP7DwPj~JIOywpd6kuQcIM^T+TtEtRyI6bSLg1IDOzss z#KfIDQBfKi>go{@#2&@i7eU@t0aZ6!Jl-ri+pbULaH zO-*O#(o*!F+}-8n_4U)!hlhK6BO^g_2n=j(?eEXbw6M_9B9rl~Ad2hidU%wU(&@;P zHZZ_0h(@DQy}Ur@$gu#KH#ci*U*EjE+SIp<_I`qEFm-@G0RPLGb~#7~zS+r-UAh@HG)X|GlFKx-F#VL~T2D9zh&?h757;S(hnt(&BcpadIyyXzk7s4QeA&lmW8=}I+}zu@&z!Ne z93Q`bzo>|p7v$aX@ptb)^Z52#ZSCRVzx{1)&cfpIWmVPQ-hcdKYs{%Y3l9IP? z4-ftQ!3&9xN5)oo_|>adu7rl7Tc4J8^XA#JB+}N_`}d8Fva(D}wzlw9&}d3ZmoJ-| zj*Y#2+usikv4jLt1%`($EG}IV7cVJ6>Xoze_3J0|G2|a1F=2aqcb7tuk`fXM3!9(c z*vQC0_2B9{FtD~(P@t&D&Te7R+&ni24Fo5rnORfQ`Z|qfV0v8t)J_iS=>6w_|IfC-zBb&CP!@>eCEo4mKSwSVDQg?TuIuaI^m&dCu zFGoc1@xddB{tJ@GZr$?m7#u|3R#cRg)!7*tTa}d>8le6R4ia7kUS3dgo;phQF|mw{ot>qn;9yQp9v;xe?%jjx5xHlWZ06^q!{hBeH-}_3 zDXH`4_4L}>c6MrO)zz{M1NDS?bQGV=IwM~9IS7nij)*e-YO zn3*v%Ls#(N!RV-i0}Bf}Q%Fs5b7N%{5y{L%YD!QL2L}&NTpak(AcFJogoLcD;4`5A z>+8F)@Zdp7iG&15y`7yqJMe@tF==ZzHSO#)H|ywJy{e%>qk%~S9~kso$ZYiY=i!l+ zMGXuU1I`kBV91^4;bCPZlV@f&Ht-qX4w{;Rx)It?J3D*^}NCaq+=};$jJj zOP8Ry0N)AMx|UW`6LK3c@6*z1ZGHA^YRbjs)-Cwj9zR}M^7BKs3^>PYYshTm_5 zTU*7&a&pLx!NjGqQbpzZbt9wh?x#-&2d%8w*tE20v==Yt=iS`c+2!RcDxN;w*a!%~ zE2O0C?QL&I{+4VR6tupMWVKtjE?shUU0j5dR7?#06ik!a+qJZA-n6np?j(4$I7@A9 zj~)#Sn45#wR9d>T113Bho4h=H4x^*+8cImyX{o44S(%N^!U8IPI^Eoyi_63W3O@8yxwy2on;ERIxj7Pv znORH>>X_waA0K9BZtkR{ot@Ryz(8i^n>WEI-``J8zJ2@pbuxKn<>5m(W4X9|7^$64 z7)hNco=;pSy1GnEDk@b~J3GC-Ha4JJ7ZxJd2WfXABJdY(Zl-CR#&&Y+|uIYBroskO3X~GtRy5{ zU2&Jeb${cAzCN7}_pHADx8F)j*Vm)3XKDG_XIHPLroLnxX=$vi=g!&N&(6MlNvE5d za&tqqzr8&;=<6#jt*M!jG0!-l%D8$}L4iVF%)U>rl-CbH55dk(Ibmm1x zL}FWXbbfwO5t5;Zqz6n^0|ULik=aIYJp34>41z#SINk^*1}3FY(c-@d`bk*W$zw3l zLEXR%6}yKMO;2nrC`FNx1i$3O?M9@o^z@82JH=>?|3+TjCcd`S6QwY^4YEkU!PJBPu?*uS1Yoenu z3C4cM#RZJQva-xfUtehQ3kvG%)6>n&#Knz_%F6ED!}`d_$HWBB{r-MaleRWorDcqi z)Uh!)H;}oY*O{3K34xO|BxG)Gb2B$rSsC9e%=RlPw6ys7U0m+m+26<743}s{#oQdy z`}p|{3@R#?m)qOz>~7yC*0{O3&`?oPS=p47m6he?-ukY3O8;F1Wqj)PyWdD1z?a@9nj<<>NCmqtl-~gYpOS5IEeImb|@LSorxV zl)b&x)qsF&*O-~Z!tUPP-%m_rW&Q3uq%%E#UR$f6z{=|4GCTYHd1E8;W3i8V|Gu{u ztTkELmX;SU#>Pxc&Yl$(uBd=E-QNDpnOnCC3xE3Q`SX~VD_1UE3JZJm=-oRim51lT z1$XzIoxl90sYyiSv(Gd%hKAn19~{)s`0O(wp^A#fk1@^u^iy{B;^Lowe)~2$`jbzf z=j-eH%U{;l0|P}xm6gF+Y;6q>Csc)?&_aU(Mmrpi$XN3BhIS03W+)6ZGQgb-2yk*D zlR@%LPxte4asqo9sfa}1DI=q<4wPpb8wZEDxa#WK+N30BXKU-AAS$)KJ|~AvwzKo{ zN>8t>EGU5b2gyz7LPbQ7NUp9iF~!BiY|p_VBm}>ao9p8P9#%$1Q&UY%WTd5~nORs^ zbv5)a&d$Vava=ICM?*sslc=bMhSpXJ#lgYI$lJT10Lg&9zEEX=*p-<{CfnQl`{(D^ z*XQSx$rcuFZkd^&Qv3TG8$(%1a6I8b^7Sn(ZE4BLad)?{@bb#Zsj11$C6jGz&{?ah zDkumJc69XcNKP&*D=dUd6zs1;#t|Ry;{!!2h}GHIp`qygl1K>&)zuvxAb8u_g4$hL z+S3DCsj;zr%*@CrG7`S2{Cr18bXn24EGlw!H8q9Gf=cYHI4uo31WZ)iY-l6k1xIJwvkC zci#yM7ZmL6jgLDz5?uHFeM0vsAdvJM-!UysTpZ51d-onaDlC+cU}14~9vRu&tE^O0 zLuJv{wz^6XJ8f(_I^aTtcFNF@Mms#j6pf!>ORK#6?%k0Q=suN{va?rK#>U{J;N!!b zZg@B5TsG4=TKQ7Rno`^sY*23{CsV#gM+d%iBwlNK3-8_Ybz&bV^dNx zI9OecDI&5R(U(TXjfDl2FCg2hsNgZOodN^Z)y>U|ist7>MxvrrRMgbs;)WRMQOU^~ z8nUv;CmkM!PFqF>uK>lFot=t`lM|GbwY6k2JXz4zl$Vpqrl#nv_w>$hUv(F?L8QzZ`p<+Q!l#^3!?Fb_a z!OKfe&&CGnKuB-{g%9;K=)7by*x*PI0y*!5&5Qiiz`%qA(0q}=NO*LUl7fPu8bk&Y zNPBK>C+Td^4Z6F>#Zjrn#R&;sUaqd;;kmhRWP`OwQ0I~O>L(WP*72kl@%BW6>UmNem;c)W+|I`OSiYet!M^ z*QCAldLZ$Thac!-v?04TGZ~oP* zp&=uqZ@-n0C@;T%AKCNg&kG6`6+L^lx#{C`=@J`TTG|W7L7{MRGBbyS?C-yPSx~^o zcjbzg*Y@_?x0RJVJfD05TK zeti7Ro0l&6_^hu#e3+k)JDi&voW-@Z&_!+1hq> zt*@7tYiV(FTU&Q_LJ@*oY<>OeYLEatJY;1pEkW(=>2Y@E;Q@(cWo3LkFi?Pz>Jk<< zGBQ3M8!ICtDjE#wx*@o+8P*mdzY4WbWo`t9{TzY4p1;7 z=h4{M+B!LzMysz63qt~7WMpe=cQ=J%W~QOx?G2@JdAXAl($uoDdV5=1LPPcSO-ztH zK%*rk+1grIgoR-SoRZ?|3Vv&0AvDNdUW9WP?qaY*G1*K_H82nsHZ}$;X=^JfNmLY_ z5YS{=7|MA#*6Ql^_Xh`^oH#gSWO8ygHYO&>WG*g2!MM1^#ko0nHCS1Lg5ba}F9(Io z!C`9Z<;$)veSH=dW8?n*AAg*jgu_Be2wqL}8ra$S`4bY>*O!*id%Jx*B4T}ge;=tA zs9$Gh_V>%n)z!h^pP4y4EG?ChVPdkd0HdX~RYBqN&&9;5s*nLA zNnBh{FDGYgti3%TKvUD&8d|XCW-=L$hl+~XS?Gfl6m)fw>Vpg+EiE%MxPfYFTwUem zO-+l6rl;w2BqVBT=H@OhPfUb`Nl7Uxrll<}gP|iKAu1Xk4o^Ws0#asOU5AF&*9p(C zl~rHgy?Zq^@by_(^!J091{Mv7N=TV?Z~%$By87wUl@&6Xl@%mvqRtW!5EF}!pPd~W z^YbJ8GjJ=%#R&-T^T)>S?%uzjnJFa1!GU}3+qYF!!op|H7#WR>Jb&KWs-*PIH|pxB zi2C|;bk3fIlIP{ir6qTF78VH!qzkRC1_kl(NJ-`7?C!#+$jK=zoSu$d8_xEP8z5VO zA;ipl{yg@7&z=<*3kqJm2(|8C{z9iKDP6c=VlqAbx4(^!YHNS}wSq!-_s>7Ctyx=t z@dc`*AAZ=|^Y%V__VVS}*e6e(JxfTqbm_Ct938i|K763jP zS2Q*0^dEkhnL$<_T*gQR3JKxj;^sy#8j4PQcgf`GX;hjT8rvs+}t`k)zu(; zdwX+pi;Ghz_ZUY)!quy%PQiWg_U*wzVj}zyo}OJ@v$JJo*47FNP&W7W*3|g<85$zv zgGQ^Z4G%Xrx3Y?jt*>urNJxMhF*vxgl19tQa&a*>_VmopZ)z$i@bWS-adFAYYHlto zJE>8S-xC#O!bk;yzM!KcBg4)P*MD&_jh2@OcZIt<(g{jR0s^e8TwT-Bs;Zz0Koyje z)6hU91d&KYHi@5~v9W_gW@dYPU0q-xoDFet(8$1Ptf}eeS6|=Llb2_2k0kT5vaYV; zVt02#LpL`nwX3V5!pBEf*UBm-1s?yfFmrQj>)6=p>dMN92rDaV>!_%zDx^|5IU(1# ztgNmM8hLYbZ|~gP`g$rgIM~6#%`Gvpqy(udNH73#nefrNx`u>gWtEg*k47X0RaI3~ zAh*cOEHJRDs=J#?wXqQtl#@$LTwQ(m5M6|e7lnn>(pFbzX3(L9cYAtzf4`^*Y&=lS z-@KWaFfloEhM&Kz?Csm5qmYm*SD2X-5?;Q1|GuE$)~#>8A(6mDZ)o7>|HB_tRR;&( zyy@(OKVDRnPXGDmg$0m4Zr-e}K4y?ULPE}-y>ux#`ThGp|2Z=gt9(ev!-sF*;+f8! zb9DtHDnFl{{mU{b&7?hr3J5GYI^F_H{TQ%9v}bh zZ*g(w&m$G_-Mcq$5)v+6{O-Hx=y&ga{k5p*+O04N+KjjrKiKa85C4dP*#?l?BU_!5*bOQV&_dFIXi!R?Bs;(i^9T~7*|)gKub$Y zN}{8koS=!RWE`ldt*pYsD=QlsQd1or&CLA$OG{}qTsu%FrKeX{7Zd~r+Sz${q@`6> z!bRll49`(%DU}LFHuz3(Y#?<5`BKoqBqsX#!TXJj`ou&cQ5otLxbQH6gy!Nzb8?~@ zLH~`QTEoqekwGLo!y})TmX`-Nxx2fsZ&DK6YT)5`co2N?loVfISJ%Km5XnK&245O9 zVB|Wwy9WlM>yn!b|GAe}N=jK-ZmyS?rY3e#=m)p7xVnmqD=J1t_V>@slrysAprnGQ zr?%GLA9s(uyxv|KEh-B48|)aGn&RWF7-?f6Atj6>IM{%3goV}DH#f(`=;$ab`}(%F zPEJ--I6A7SA>(Ihsk4(zMrB%DJU4ge4$|l0S%W7dF;PcHO)WT>Mx)c?u3ft1=Z7rA%uE)R&p)@cTv_?c zU)tJ4MgQYJWMumKe)(m6-NE4`duwNhOul{{S)D)p@c40T?2Q{tOyK=Ic#xkDPNRzp zdKJq`|IYggWg^csf>(hG*ncsu9p}G$b86r^z(xv4w<=;k*=;# zVKp}w7khc>=vZ1(D0F&LQ)sBJuAyOE9G%|Xost6om%l&mtH`_2*LQOx?)Bc@W@aQ3 zg;G;PrG|vS=@!dKfFXQ2ett<&Tm*3)+OW7de}8nf(bdb(kBV}2b#j8^th_uUgXq|S7y+(|wY9Tz zN=j2xU0rmvxw)xnR8&h#PfvEXwY9oBiBwiLFi>5Mltfe06G|79&i?+5jZo^@*@Ry4SwV`5NC1OyP+$4h+B(rlAoL(ccdS5wdA~ ze2AoK6BF!$snpTY`g&()X=ww4^mI6!;B7TCD`8|Xc64BNZD?3hvc5h(9vUhutF2v7 zu(>%q8y+qqA|a8Gu)Msq6cZyT$jciZzOn*}A}1%NP0PzKUQ|_yiGA~pi3#>Iy}d*l z=;Y)tzf4Z5tN+J;@bR^_9v?q?guGWuUxUWUta#{r`A?U$Cw?TI$B!c;qC2k z2bwUl8A%@FDc_*RFAKrKH@ye{hhHz{8-@#>XEW zJ${^?&dGV@insUOyRTlAl<@JLK5b|?ICykaT8di4*m!dC?c4TtDXC9BQB)ip{Pox6 zWebaQ=VWDxo{yc~83qBQvGHI3di2P{Uu&}Sc{_3mX;Kz)#?YOuz zXa4KITwOsisjA}S{PIg@=ll1MkK5Y#`9Jx@+epp_4KBvfBLDdO-$^+|65IMauS3jLqp7kdU}qJ z@7+VLA1`lN*}Hf9`(*O@^B}Q2c(At@7RJPMvQ zr)z2|D&iYAH&;*qPq>FiQ`7i(dAXgPjEsRne0*(fVWE$Yv9Y~9+*w&!n3Q;UWMtIW zW6$pC2|5LQ&B$3m)<{(qy1uTA1A8E(z>rAD{{(jt`HHr-0RdEMM@MzFzrTWlfPk)U zO3L6Mvc`gfOiawpgM*R9mXi|_0v$7v)PVI6v`|n(iPY)zbhy!w>4quRNpfREL`g|) zZAONtr;QDnoR?Q$Ur^xdi=5QdRIG9S{-&n3wuy<2jc~4`2SU^?DJd>4Abui!l|pf1 zWJ_bG37xsSyO9ydl67@OMgIQQ)^MU#RpsaV``g&KyQ5=+72UxBvm=5|W^GL(rKL4A zR8)k75LHucZCzb#td*6iDcs$SjaY}w&B^4#!p6qJLeNdTyeO3N^89>|+lYPSi4qN} zX(HneDKN;s2?|0g1d zdk7a7WPsP!V!9R^>)>Ex6B2@5d|6pU1YE;;d02VzS0whO;7P$j5W~o~$;?D5E18^{ zic}d-Pit!*pMnDXE)w(X?X$Bnd5nlKG(?y1&Yk}Lgar67e0=H|G^?~UI1oKOG0n=% zG%-Q$WJ3dp64usAN^Wi~Epu~qb@ukMvJMW-&Fkx(olZ^y0va0m`AbWf_KS+j%O@s| zjrH~U`U(k&i$_IGOpK35Mv94{qd7AJqLRA0lvG&Q?Cj1Cm8zn`$A^2=lP7T8z*j<} zfp%U+1Y7f0skjROLKERKAfE5;#pa{yQ{0=;r#rfqN%BSj3X(DoBP@| z>?H5rEhtb>AiSU}E7{q|5<~Ck`|pWvn4Vro$CD>xW9YbQYontuGUDpW%?{{Hl| zmlr!bR9~B$P)xD1BHwLh23&Md%b|+c+$=BG)fE->@)}{Vxtp6!O$7yQZRzyAz0OV> z8(v;02-nuSyPcik_{+^*Sh#b?+gm^YIp=U5#>B|TNJ}HBa$zAOLtR}~HY8+Z1U@(o z4M9OiM;dK*wzybV7n+UA%BiX9YD-HYAw$Emvc<*jZXX{R8C~7%?CI&j!N^E;b@;Wh z4xk=D#(Gare?L6H@Xa?h(door*Ut}AtISLt9b!hdyiB9HxhX5#*;Q78`GJ+)(z38{ zVghbCB3+wKZ)-zxyRI%WP%*8B3nV<8P9GY|&bG7!_o1n&w>LZ6*4D@fT#!3=GBb^h z6%}1wYijP?$Y~%b!sO*;Wy8WoN0*n2iuCoRrMcEC`c5ZJ+cihfSNJv+=uyAUst_ZwTw(_>xU2P z>rPJR&kG2Ylz^h`>wEpWpkQ(FyLS&C1_z&G9Kpd49=v*$k#Xb37hl-e%+6vC0y2?> z#q8{xH|WG(xL|5JHTB`c@UXrD-VDk|Q*>Ebd!|MDe`rlxk|hLsh_e_dUgn%{n_qy*LA__(Fz zg$u&Mg@s2)v$G`9l`HVI;aL+BxVVt4u(1IJl!5{`x0e@^kD;<-W`^Peh;OAj9+V$;e>piT84uxMyl zSU~cmpdh{jGc&ENE-oS>8X6$$5PU#5Mv-BemZq<-sTmbDFfcKZnF;Etqho#j!~__Y z#MF3TpsLE5!CuA09<_OBsFISJT5Rm#;KW33uDQ7;BNGmFcVQu1RZdQYg{Zucri!i| z$kmvmKoij3UQ*)juB+?nT3b6m-_~Ypd*uof6P$OCACHXq`pU|J5#7;2qs7J9+B!NW zCRQ`JSfQctTH~3p@|l}EIZ-GrEj2YEAx1{#=1EC(dRJFsqQ1VevX@t5&En$FR0GJKNLK+}y(>JG-Hw zh~YjnGjn#P{KglJh%jgHK8e&i3I%>OU*Cd)+FGQkVeQGy1tZMQ&)OR5mg;Jdp`kqi zRjjlWUUny^sHhSK=QJtF&CT9EC@4R_vJ(0wM@K(D=v$ClN36lvyF@UOTb-SwqoGzw zO7iq{aR~{@&ZbgRQ;A*@ngH^cI(k{q z$rGA;IF4c&d1Y`=LGcB`GofTBW?h6AfYAT{zkLKJ|N8g;{`Zgn-_P*xfAQ}#@b5G5 z?=$f4Gw}cKGw^X$Ae>lOIsW$};-|kq6U0x5wdwaRGeVh6_+$yk8KG&0GZx7OgeMKJ zfLH#wPT{{W%Q^9@5oC4z2J!R%`P(P2OXMNoc|N}H-@nWM`!Vr)AAd@`i;w^P|9V86 zt&gvD^8P=*13bh3{weWUiQhZf1|NKm3~Y{bNXp_P<$_byVekf#DA!r@^~j-VjY$A*S>cA(k%_FHILzyH3z zUS9qr5e|fmYmA(*zP|VGXJ>6}n3xn5TUy?{Sy^#*zIahUAU_|zBo7ZJrrWnc7+zTk z2)J>BojocFbqi7zxVb^h+1o>>fS=#bZ*K1X{i-VX!_3Uu+mYU4Xb28!Rn_`>M~96K zC#R}vaq-=|V`Dx(NWIF)K%E#DCn+f<6(7H{vaykthJHKr@^|l|=YXWEo}QHzC}OT( zH#di`qOwv|70UMB-p7yo`>m~cc#u^EwK&m>sH=m=&BKF>3%<6swXrcWnTJPA44uHq z$&e5M#(^Hn3WLd`s0fx9n8cut!RawHw7p$Zqp#1+ZD!Wcu(HzFXl~BUjh-SDY@p(+ ztAoukJnZ8mD5#*2nYp$$Hy0HpB_%IUsPhvOr5S|$$jJG5Q1S7Xc6IITot?#Fc6NLF zt}aklRa7`QtgXAd4-U}n5D~Gl>Fhi_?CW!MM6bG`;otztUA(+XO1ZhKt0N;mK74#q zQYk453sAQ4F(~q2@Pjb~mTPeE%*@tSK>>Q{etwga=v%9)adKK)gEdl9qpM3WXxG-7 znoLch{HdmZ*%unp?`}+3xk#r>@g3oqv(A{lkCm>*A($sWt04*}ShWYuct4OQBUmdi^ zv9W*vbe-bjmX=mlkX#`w91=1!v$>g<2aT=2|K#M(4!B{QoH#S<>s3|S+8i9<=&r6d zG#DG-x}~dITf4)^#z5u@aY4^3^+UAzD<0Fp&?ICUIwF_LRnfG z8}s+)=LeZ}g>fV$A$9G_nF$L+>w0iNr`y^J2|+78W+tHt z=iw0-Pf1x{UtNul7ZVc}j*J9l3C{}M>+J0AZc!23z4%K*AFjg4Wic>lYTDSSt3x`R zjt>6P{r$+$Q&j~~c77h}PB>Tg_ZJs~gOSpgp1!jK_7^WB-wfZ@#l@HyUS2jf@M||V zC=?+fq*+c){U7$;Gc3w;U)v{HCM(rgVnl49B8XC@BUQS9AidY24?`V>-uuvd@4Ywa zU5ZGNUPJ*)u*YbMNmjx;SDrsdvj2PU_t?kre%T+^d|9*1z`!u`Joodvuj@QFHl0Aa-A_UPTua&C}z@MMbl-A3Xxy2ko1rr0wms zwV)vMC_#IF^e8S4eK#kksj2O4(Dn`-(AV$p-`*}P6cs&i027<S|aA_4SV) zm5?YbeE5(!ZwL#gr*CeeeFJk4j1h~AAt3?+LPF>pFD-?K^YLA{K&9Tfv%DM|3)iH% zd3QH*vf|<=PQYupy^RI~CQwKl!V+=ztcpr)?Xzd2qXY-Pq~!JMr6p(QufJwvi;Udf zUSG%W!@}b4|M20?PFUFC!~gh)t?kyaSOuW2}jeqgMEhb!BB48%s-@nPp|QwPj}`xj`b8 zl-$1E(c$eaA#w5I_3M$5pz$C(gLwlwYnb(egc1+{GXmC5IF}O!WEjTbcS9l{ZFjJP zQc^I>_wvH-1JRJjRKnF2N&F;+0|NYBH#fp!4yOaYYe)$AFCifyUc>qhE(~FPM?NMj z4DNXt?F$N$l8E_ALPB|YUY?JSp&_P0#l-|Q$jAuuoU*dyWD3R9)Y+LJE_r!bS)qeN zr1x!Yot=>-%gPE4c5*@=CyQ|l4Rv%Rk!ZB!N z%V{(%Eo0-!RBUg5 z|9)-`I6B_G{q47v zmFRK9X7K7&b2F^8c&|Tv*xPGr%El%mlbsDup}jpPr-Vd8!rUB=t$chUBFK!+%*4eh zD9Ffw06sB6rzbh$V=Eyqf)J` z?Cj7s%g>LEb#)~gZJ?z4`r6o_leRYtw6MTVS5Xo6G%YRN+>(>a$}%$C-3<+qT`wsq zDhds?wl+8S@yW~ttJ%T9+L}g7OUuiPjdgQ#aPad>P0h**568QZum#4(Iy!>Zl$KUj zmYVA7YGUHxfQ)8FhL;xz(AaY;E)EMLF%m8Kt_1~gac*vQc2sIY0`~B{ykIwohya<_ z&yR2~Mn$3b2|G$M;}#ME1C+miQc^|+NZi(?=luBq|$RaeJ;O+!OV3yAP~ zdO<<8wT+FDkw!)a2B2;edr=_aqSKU^sHG((g=AY_UrGu=!mX>Dp021cH5CytG(-li zsmb0REc&9Nsj0?BtSaFnSX%1swYTTul9WtHn3?J6v9>;b99acePpQ=7$5~i>d^R^< zzs}0y<3s1;!Gk~kXlM`@KXAanU}grcC28poKa`a0==k;5(NPr@_)w~pI6hacX*4-Wp|112VG>z$q5-K?ymN12$+&A+8$Uhc))b4eTlx7dttrsF<22C!;&*<%RrK zR#smhINqkF=u9wx^cCo&g0GG=X<(qGCHA({(z3F`!W*^XCV`D8Xkz>xw0|(B*!N7n_PD!b#KoSAR1LOqI^02VLnTTlS8X1|JhlC&%7Z_-6 zZebA`T2@wG9vW(FtgB0<78f@(q@>u|YHK?>rlr-?WM`xIXKPERS61fdqc37<84*!l zURD+nVe=7;IIM0U$`uvS>F}GP&0A0aHX*Xvp`n=!D-seMj*c+OrKd+nqm@Uc zMnxe*fO9@bI7IT$!^7E`N=1$pG=IlG{c>4ZIKsNPSXiKg3npDq5RR8rYHBLZyc7zF zM5QJt=jFx4Q7BecZf;12q^5d#!M+k1iF5m2_BB5rPC7D~LJ0{;OpK29_9pn_iHS(o z>*|Ju_4M4nos|VvFRqo5W3jhCdlvqlg@v^>Ivt-&3ya}la4B_k*x7H~XlQu!XmHTZ zj+a+Wt*B^qwWo(fg8ey#VO$*>b8~|skYGmp`f_n$9Y4;bze8V!~x zf;JHpgao^X2bj9>1GBL?IgO64uBN1jh_JDN*$UURk`k^bLC&wL!qpLeil>2JPqbGvu%_3O;c3l|O?fK3j`Z%N7h`*n2g z+}Yh798^;JsjE-MiIQJG(EwICY9nM-n$6 zfR**ag`}i6Z=O913Oag}nK>fj{rlbBjEo~kkdIng`s0t98YIva701SQcl-Kq)DRc% z?%v&9S;0yQUA~`xUSB8N$3z3q&F!0SutIgxRbFj(&$mrPCT>R?+Uaw&iLBIV`7!+Uz#+x`3$6;)J-MsHl)jT_gl z`TMuG4-O_L>**;fdU&+7OiU1-U1UZF2P-OY4w97(3F+u)X`xaj7&jlE!NJ+tv@~(? z&p+qkiHTWWzJI@@1m{>|<|O9|MLhHzWk?NO%(+9fO0h3h?uT%Pb@$H#a{& zB7)fOOie8)pwp4madu8fz}nZ}-^$9`8pprvY{I(e?~gSOyi2C0NFyPa?(6I1gl=p` z26ilwJdTbA&(zn~!2#Y$QXv39byMh+gt0c3lThE7QVy%cF^5KfVnM=Ka!9W-#k{R#{OIgBu8 z?(J#rF~x|4SXfweGz_-b69uOuHa0R6sVKCIVLsS%9QgYa{bG<0oSmbh3JXC>fWeeZ zuC1M&?e4}pM@9y1Nz7Ud4MjywO<@e}>~wX76FDw!e0+2?DoRO7PR`G-yL)&zDoREM z8CY0Ckwzjqwks>u)mmC-&fq%z(WBe9-Q2*%O-ln+!rvc_%*e>4C6JQ@1(8|r>Vg{# z#%CKF7<-bEWMq&I>Fr%x!z>Z~&%QpSLo_wdoijAVRrJ7smeyBaiHcTMzI(T@U~7B$ zFc(*9>a%AnD`fJ)gC|ZzM!sO&A|hB=4jgcEd-(AE`{H7LhUwhOYIgR`o3b(yk;4q@ z`Q+rgceS-5BK!8q$+fk;c+uP}EBnbO($dY%Z{GCvUAy-A=hD(09lN`Wi$+GDea6R! z?#b4ct?ehDu(4sC^Wue@+rEAK_lJl7_~W~Gfq@@?%*5p8hILMA>i+#qOa=zq+gOFb zl&h<|v4PbSA0K)s$YHLn!S>6>*4Vha`{)szyDThuc}RB>>zu^I-+%x9dmo=q7_Q); zpdWtt?YE>P7M9OHCzH3gfBGpm7wlyU<=(yTzDrAEWBceMW8>M`*RNr%Vq&^+V|x0x z-x?bD`I(rMlyFp=nbFYr_+xQ#q@d^K^z}da#e>OL*tUmaFnK>ch-MgI~8tvf0 zufO*5+unZoFd*Q_k^TE!UGLuqtK-t8!-s8bW@lf$LeBcsDH3UB=Iz^t23gs|hcz@h zJ72u$?9|kRqZbpI-d+m}w0=QK!5R}Bt*opKM&pr6<>kF{1=Dd*p@f741%rZ+508%* z6FYz2%Br>X&Ykph%trO}N=wJaii!*j1O&ibnVf8I_x4s*MJm6uv#}A`eM?LD9O~=A zl+e)ul^3fCGczLZSXdYkpvPz`kjXhY_4VNUqDzvJ(%M>E8xWwRBp?8X>dl)YBgkax z=;-PquK|aSAA`AXVS)8jVPSHzj}LMW;o*siu&cScdU@f>g}6_pf*VU%*9e{-(O3Xo zhhPD~p90?`osKDlhesr%1(A>dgB)=zBX}Fm&Vhlr%8H7@6&GBN$;q*?M8g;Co1h>M z#*H{mAc0CIqhSNW0L)pwzR}SM39wsYw}MV5{+*x=Brs^VutmaYkE06OzUcK6?Akpy zeNYg3!oI%Y;qY`31X_Y$7|FPSKnR{Q<{wzK5Z7$c(MWQ5{OO(qRWLr@+uP30#RV=T za6>Ssh>u6^#Ki@~=!}fIIwX38g>`f)DxN*t+>D9g=00)4!C_(H_umHwu3tZJKwP}K z`lp}n-}mrfVL5XqENpulGG9%RxaL9B0mWd4aG+r}Oi(u{k=9 zk8f=i77{76-rjrn%F98`c5)hEkm!nvu3kNLN>{h7ZELHcL0z4hSyr~NaCH@HA+WFt z3bwZ=CeY~-5lK$Ie}B*KiX+_ea!ibvn1FzvAJ#vKiJ$|*rgG;Foh~79{=AJ1CU-eG zva+nK`i$QE)>d1ai3zcn@%ZuJAbgvOiiL%no0F613G?valXCYi(d{IYdwW3vL<_;% zx~*+_xvtLKTtq})zo6ju?dE0*MPA;(ATtvd6f#*%41Rl@|6^kK88&cn@zm7iF;In2JoBaHUoUNaqhX+AO1|12LFwndSqnoEE-V4NB46Y3PH!vVTaiUVe zhXCQ5Xq#iFksvPNeM}Ih;NL;F9AqdzKk$>o88=Xx&@{(?!#fr9DbOW|P8#0x=%J;h z#mD1}O`(K^r892uFFHF11*NCwT?fh!I2Z6HB8lYgZpm;f#>5mBW@Ql> z-_X$P?99x709#v}rE_!h@16oa*B%37Ds|RHMO`Hb3HJE&@76N$JrYc&D7MG z7%wkpXK!yz5Mp9zG`-)%@+1P}HR9AO&q^D!2K~*(2 zwzs#X#ob+4SX9)!>+)@L`)2*7a&1mRi&W7%j@JcG=#+NjT;v)8X3V{fp-}fmxe}p z`Nl?nzpd^0^P-}7Zww7#SLyO)8m+q_Yani3M2r6KQZZ7na4`%_bObtDoT zR>=Gziz^|aq!bz1-Hp_&oSd?9czACwi1+gH($Z8adV!glhK7oY6iN-lN12mjX{oB} z>|9YXFi=Ormz74iHT`x@KHHAd3nLfiH;*COc4=S z!{JeQH)dp@1pwbTF);&y#o8M4uac7Zcqb<#BS%NfhI4X|Ikd5Xg{H7DEX>vx@4D>l z!orvsGMPm3_D)XD&85>lJ;7{=h)77lI|?%_yt}|R2gxlV0r@;!Kl=K{#1Q>AxJNPL zhBXn+8Jr;^BQf*BZwIm&+`#zh^780(3I!ai*w}&sG)k?lZEb^sz?umSwPQ3HFw@4T z$lIH63*(A7FwotdO5HpA!PpoZ3-2ASZZUHJTLJS!b8~lh5UNsAY;5G@u3dvAWNE3X z$=Mkh%;@OB!LczUnU$3AHyj)+D|2#EQ?s!FJ+`sY-5svExVZlQ&Q3o+Nl9U0C#Txl zuCACE*km0Y(NarK$MuT2c~cXvOl@qCL@g`B?l^L+Fo>+JEiAwmcWC>gt&pbVej4Ei9_4W@hT^ZEa;`t*mNlW@l?_O-=duu3s-Gn4N8D zv9iJrV0!x9yO{iPaY;zT$AdQN=f}f?jvE+<2?^i^`uR;xE-!=McjA3t*cbJ*uRH{oVFFvF9CoWD*>>Q&T)6=uDQB|d>dGVsLaa-H=HqK3F z&nhd|*1mi9;DL|NH{X2xv6fa_+rx*0gWldMDmprtz@W_mGCp#eIBMcO3qna;oU=3d zV#I!irzeSo21HH{CMf6@`XNBu?K{SGOqLB1@QJJ zk(`{u!m_h5o59b7X%c=W7Z-w-R$QE&?dNA{X>I*?pCLY{#Ey)Mi=Q9bnefk{0hFDM z?hDcD!|^67OIP>G6>IB?im|cMQWFz&lwj;bp8-3_0Rg?eVEqa*Za5FzzK#7|US28{ zj+>$)d3ju?Pf!2w1NxFjkE*M8cY}hgt^LU-mo60)eE}dE)DP z@Zf<1G}_arKmC-JhFusZr|s?Ee=jS$bm`Mi&CPKw($aG25{M7z!Hw^bq+MhgGUcS5g_N|lC-u!iUmque^+yVl28MnAN+_$p&>8IV@oE+R& zR(|yglN7jkg@w^T+uFiZmxBY7ir25v!enMfC+z)u6B8z;4?ajw-`)M~H!>Mt9T~a1 z`}^-yD(?IH;~A2YKKh7>$=x0Ii;Hn>Wo7mA&oKNRJjlc(CAGGOYb!Q3JlotHT4%WO z;pWD(-Me??3KP@utDFe`uO9L5_~6LUpxbihWn|hpMA!}WMhLFa(X(xtDYXd zYh@)D*M}eK=`AdPM<6MA;DDhaC`vbP%E-V>jBBgjUKyDWK9H2`?EKfi#>VjK!4dxc z{o;2vZkhf|NWy!K0YT-zzhEP@uNpEF+4n%F2QjFBR4lU_D09YVV)xRzI(11 zR#tQKo*wkLwX`l>!cP9;BG!3gV&>*GH8V3+Rhai18&_1!&LW$Fo_TUIctW8U*FL&J-wkJKi|_6Y=YF(mKIED@SO;DUv@T$B*p0ESXw40 zx3+e45R+RyJu*2dsk*wnJSxiCT2IfxAwIsM0y|Ik_TVNEXAWm)5(!fh>=99^#EdpE z5!X1bt}rXJ=e>rZN2Cfq~A98@LXsshA!+I|m12rWG7)X$f*fW#!GAX=(cUNDIZq;d(kd zTU8aT?35HVX|1jK_|WI*?;je9j#g2@eij&^MMajDii&o2FyfY$+S@BJy6L&O_zFiy z85vE@*w~vl(OkZIRaG@8sJnkWwsa;)D zQ|Md?3R+n;HqOq1L5`G5Ny*$?W21=)C#Ql!b~dQNj*c80A|i2di;FWe!NFJoga3u= zGO%krJ*TENHqz2WL{6MAHN`ous7O}!#0fpU&Q5Ta6&2CrfhFtC9grDCMGFg`KcAho zw`XSN;mOE&{d#kgNwtJ^%U7y(vs$;@4j@FB$oH>Xa8g*|=@;v6UEv15*o6BFy} zNlBM4v$ERRjgF#yEXCkOpkuqWRatrM8qzl1-CJ8A7K0?;(6F)5*a+$nym{azo0yzE zD<_wix4u3&XwP6-gU$Wm0Vr-D=+Ws*OS7}cB?<|Jhc7HFEk#A~^Ru&K6$S&Aq$C(r z&CQdOsi{alT3ffa;{Bth2HR+F@5V-TwXW{v%g8f5c+k)Q3bLVLQ`5bBb#)pV$B%=@ zi9QcO(ag==*|~ccb8~j~h=>P_8_b5FLanZ@uZM?oah*Ho>$|b>{CR5X`SYKBW@w14 zKxrxHII60{!@vHDcff}qii_j#wzg(w_RoKEauyZ+@yD}gc>kO{858sR^~;x$kta_c zKJ4ncy!`laLIMZJp+lCIBO~|j!O;uzFRrq{NJp;h)~!d6;1=fM!a4iRo5n^txq}C< zUF+(4`?kMdP3^PKL_}(9fByOIT_dA?``FmBvfjV{_FH%NPe1+YtH8kTzI*>ZHTBGy zg9lw)9z6KrhrB%a5NvEf^(iak;rZYLMMeD8TU+Jjzx+~Gwz2WW3#5t;91s)3Jal=P zOg?dfmp3)_>C*=f0s}cXE?$g^+TPyUiiiN=$JKRV0lraQ-lIn?EQW?QHqz4t1i)he z@f)nNbLT+Ydib!T12#e(o%;GmkA@gTe@;$m>D1K4MRdh@dBw$}qVC)o9`^F$VYtqM zf-qeO3%h(7nNZO1Gcx4lczMC`TV2h~RZ`;Pb9C(O#mqrL;p|x*owBltiNZo19hh5- zi-(8v^YK?VG6G?~yBqW{P0j4=+1dVn>=me};F+hU0t5N@@C*|Z^Ybw=m>}ER4-PIa zCL{<596M%a1{NU_P;6|brh|j{eKa*e66@`K`Leqk&T~b@ii$m|Jf@N>E9kJYu?Yz! zC9SVdPkVW>vtPUz8oIiA?_OM-kPs}Xi;G)Z#D1BJ%goHT-{$2B3BkDA(z3BpUap|P z!UE3w+8X%pY;5Z4H8qbOA%V)zk4)d{>hQ3;yO5BQ68P`16~G{Y^d>TWmoI~M2ty<+ z>F7vycO&DfsVO96YunsBHC0?}YO1K{<^~#9PL6?roSc(WO%1NO4GgYaF*h$N8XJRc z9{opT6PlXp>r+zDlLj{>f?;(+0y;b#wkB`_X*6&^aditGQBV+eM6kbUY3b@p?1=dI z*xM89W}HDm9|0MAufv5YWKt4ZUj%C?Jv}^}Lc!WPDJeS}SrjWP7Z6B~v4p?N#h^rhBYtTvy|8ri2?iu=Km~s7;xZlg@@8#BsB>G-vp7__j$KY@CU)|q( z6#hGIcnp!E-+Mgq@A!YXhsW>T{(hhMx4*u2@9}$&{qO(xzy2=7v;F=1{rBJD-~Z*` zufV@wfq%aO|9%Djx4Z&-XAt~6|J^bCKR)Jv%TNBl<>&bOtMk{-^!L~DKi+7AJ78W3a z=jGkMfBQBXL9(()ioit1$0sZt6}7aqumF41`SWgWw{FeML_{D*W@m>re{!;j$m!Eo zRs??>3^q&4fdR16b#*y7^z?4teDRpxm8t*i|^kb9mPanN-8xKeL-*Uix+u$(FvQFz_W33y19*vpuHs| zbpE`P)2&-Dt6jUs%j@7UH1zOcT^)Apa3#L8)6rpIaORAX60Bt1-FwbyT({WTo;f2V zl$f}_4)6S>OUU^xGj5TQf`WW}!NJSRpdRz{pFZv2fNmcA6i1I57<6@QY!nn+x$@07 zH*R!wJ%8TbuA#xqtf*LD|NQyrC~Wrf@?~Y;emgno=y>j&gao)|v$LL_?Cjj!p`my0 zPEGmvaB^~RczaJy-n|qORbd8&Jm2%@gh@(OwW{j= z{hK!p3|LvOUM(yHP21EI{%Y3@NiIxsMPc4IXQiOXJ(K^`H#QD#fxMzx}D|aNFd=o26~LT`iT=NDpghJ zAL;9}vch(Ucd@bY$&(Tixw(%X&CYswfO|xzZ)_|t<9Zk-Amk9EqIh}FopW`)eS2*U ziC`8M6O+!)rKPMadw=tSt7QknRMhA9(|?$Qv84 zU1MQUQK_!p*l2Cl(_>{-R4geWeuq=1Bqj6nA3d6$c5~z45E4pA*w|QFBKk*BQLBtw zWF#6}NOZ2P;eExzVrmM4DA7MMGV1Eu+{9m*}6786LK=folkv ziQBiyWVnW+qUPqt$2~mYq6-R|nSs5Ji|hP(DiyP@^mJ)y9v*ny(9%*=ymZOY5jOjp z8u;|CUkBj|SKMdLz)OeTp|0+U69hBp_HAe9a|}~lYU<|Z;vznAU{Qf^g=rKgC(LRq zDmJ%*oDXYs)2ysc zpB53p=lj+zd;8O1zhlCdtWJT0&nEJB=`_VQO%h;YdhLU06V~QAh~A!@GAUCj$e8g~h}`okz>@ z(j^WKclXiJ+1ZE)9-fmYZEZ(Kk!gV`&ctMJ@YSpCZbQTK=e4w8k{B5wYQYu_W5Zv4YpbWn%8HANa5j#NI6HH5!)t(^o|o78^W5CX z<*coRg`Gdo%xr6W>(=UOQW8Hu+RANhtE=c2672Ggjrw{cBOxJc>yD1i&6XBpV;&wo zz1rIK^_w>>EG}MDQOU^xE6mc8i%U)}HFbI#ok|W4g4m7*k$`}>INVn79g2zZ^W$^B zzMhgICdS5QY1z{QQaZeo__+9jLP-Hj(@88FsGLowa33u;;UoRsA_twY=NLH$< zn57dY8*ObVDJ!eY%$l0GI1>{M4OiEKg4){HSQ8U1EgzqThJk_XYzqq&6_DVNrgw5u zRRvKWY%MbR$`w^rWGu(WqoUyZQ&vt$=;>){3JbHeL{dLLKQ$FJKG1(kO7il+7z6`4 zFAq}|BvSqTv$BvOaB@PIB`z*6kI0=nI3Qb=nTd>whX>ki*eCV#qf(KsiHU)^oFM98 zPX$gF?5f1a!;Fp$SwuuC;}#wco;31qL~93a8)V$*bYxiF+=7D>5|Wa@r^Rk4(h2D8 zkVwwXVPQxWAlc^Vh_p;v8nSS%uHN260^iq{Oa|>2ZJm%1!dQ#6D!$&H<~IlPmt5?sQ(bH>e1icI9 zO=V?xF$V@LEKZ#QbM*1!r6pfq977``A3S*UC@~SqHW!!K*{4s7i^atc9a2+kXjosb ztW;7uazs`Z9RXwy*w|oFc>H*J+TH!)MG1-YbY#uL!ua`ld4q#jR<^cMQ<2oSu^AXx zUoR+-lRJG{N2jrIZLPLeOABX?q9XY2oSpgk(Ne#A7fe2Rc{R28__4A6em_6rx9exz z2-}pOACeyt5#r+9+%7IdLn|v;Sy!$cKW<9Ux%Bxp`oP2 z)APm+W8>)PhKAbO;9z}ySO?0=YHFgQOigulX|#%p*4C61d@`-AGcwxSD=KI-U0q{i z^lXqPH8%$*6FZ3Lci7qzEw-2#4-ZF2bVcxf_Vaq;Nr&71!IN{m}jP=EjQbY7mGo`eKgiVF)j87xX!2G1C~${ihnf!D67slnaW z&=4F9-k`sKZEZtCSeTBEhK7$%O-)M+osL)7&#$_=sVO?z$Vg8wH1y_8>^5s@$;y(+ zH8s7xNl9QpT3VKsp%;lYHAu8NlH>x6%vBEY-lJcNlA%^$IPt0ety2L&c+6u&8n*DX^`rqrOnMj zcBrohr5zp4J9paJoSntR@fp2!tFh6}?($_c@aN_R2i@KI`LA4wjh&xIdqP+kllz&O zrKPwyVPOsqC#T`zwKZZ52f`lM5vr=^&LQoym$g$FFIl3rf>n z4i4;PqRX$RckUckQm_=0NbKz5;_>k~g82H1h=_{^1`ZC6j6_6Sy(%f`?cLQiHii_U zoE$o{{rwpkCMGH>NEr6^W@Uj3Xm5|SLUOW>jtCeZ|2>U8?(=7TR8$lji`<*1XIffuF`Bm4)}EdzDM(Z~I+~b(f?HFA z3_9UYX=s3}Kwn>5+sg~il#~=FCu3u<;7dx<(mXuSnnP|WDG50R509v*l$4klD%Ht} zN{x#{Y6%@z!qJ$JKxMcs$z)u^CnWg$BZCwcMkLhT-FuYGh%~)R_A8&51rA48D zshg5wXQ!v<>YA6=*qD*w-~hu)YHC$gYO0fyfdPer*EBxf+FDoF%d4!cp&>Ms_~Z@@ zym&D@Y-`KGAt?!S^uhwT5Zv5;e)IF-SmOMkrBzuuJzZJ~0<@tab`Fb*bac46wYAI2 z78g4@oSfh+0*w?aS2;Od-90j?xHud{w;3i#B(30(8y>!S6OFn{mjVN)r)Ou0cdL)j z%F2ru#li z_V%o-e0*_n8yhPt@OI(Mxwv@m9`fG2yfoU(%)NUlDX=rx*$oY?uV-aRNZ@?e(Xp|C zPu8(x>gtV+PoDJkAz`SYQBeV!xPt?T)}^JZtDT*=l2uTEb-BA6lyN~pPn z4z|VSW_UOU$LZ5vUaPAwULYsI$%!lQ$B#=(6%{!+tgW%PUtSKLD5yF2@7L5^y9Tdw zRn^K0CJnfPs;PPKprgas7_*s%hMk@MeglJJ$E2m}>fXO!TeG+S^2_7Lk;mWI@bzV3 zId;t3du?rND?I%4>BEQ76Mp}`qC!~s@L^Na>FJ+;Ze#fBjvm$49vpo4uBS&$?W?b( zq^hf*KOY$}Ha>DhKp;JRV`F5*%8Hr!;zjhnrl!2Txw(-CSzd;DBQzCIk3B_&AOD=Yi>)Yi7NVBN2&>E>2k+}4(t=k5+W zOkrVbD=b4=TIS}-$w)QF#2_0WD;p5d-aa~7RYjv27#JCOc_B@R+>g7vk55DdxE5%2 zg@(q(!SzO=xVeRdU?mRY3yI|KpPO4z0h5ZMAzTmW$l^N@MyATjhzJV{Lqi&^u&}Bs zF3#FoU*Fw5FR!*XIhknKW@a`v=I6uw0q%E21=dx1dNww6I=TQ}(sGYu8Am z?ChI2i;A3`)z!_+2;+d8+l?Er4-hLfEiEIXhzR7v2o?p_US(y$!RR}OFbq?bmEr%N zVXB~@q9Qhy;GgXshx3`A_jZPmL>g=S^u3V9p4hkA#SS8}( zR8(YT{r!iB(cw^05fXB80u7m99O>wkm*2fxUk~;&Rwv`*H8tScYiSi0E-du+g14-u zmYzO6-Ph;oDJG_%K&Qj8>g9#ha9G&LNPj<(<@fWub!&7K8D*U1XJ$4x!3Dl_36nIO zj};ZqpSQNYb?eou)>d_O78X_2`ufL@;k`X}Oi~i0aLiA?`3BCJhYx3FoSi`nNJ@I~ z;>nY+FgCVRrvd_Yc7FOPKcAa>|9)fRiHYyNBUU78YOSr9$tf#;^9{^uAUf;m9XTQ< z1}i-*?p$2b(%ISf?@vr%265#|PR`B_*vrh!Cr|qOudTt4bL7Y;pIBSZ&Aoh?m&eWU zLTPJ{kAokgrNzvQ-p}{nkB?heoIVY1*_$_uixdhgD=#nMWy6H>;zfV|JB*v3-^Gh) z;7?AXX(lSl$?4*Pc_?hUJUk8#0|OfyB_+zr=g*s%babHWqp5lNw2Deu+3IRb3nqP6 zuND=-H)>;Z=8Tva@=l=iaBv6+BqTg$+~VW;_%OEu2PiA+^5r8(^!1TnDlSH5^y*bi zyPKM{v~WblHP_UXhX*&eq-1jP`ugG`9NwUZ&CJZs#>7ZV3ki96^!83pVikM&GKo}E zGcb^x4CXjI8Ka{)Il8*y;x;yQbu%+HHC9%VlIG@^tv5Cz39qi6osFEjlM@%0uyAxV zk}P1cAmN+E@NFWguEDS*Q78ok)zyiKussI`rl-T2gT{MkXnH!1GGwxgOK>nj<3J8_ zFL?o$4txc;)}b+OXt#im0}Fgy9IjB1yr5Fin4{B!g1o$tq=<~fbOChVsHoW3&`=~% ze0`&$V8iwEgQF5jjkvgg0C#ufR)}^H`ar~t!r$N98^mDby}|mxF%ql~d@b(78Q|#& zS5T zL?rYb9o ziFtYs4J|F@i8X=%mA4i5JAhK4FB$;ZfX$?^9$GXukdI6Ik` z=;_gDrKPZ$nwn~9d3hm~o04K=boDBUR8-W*Fh*NiN=kyD51*#9vy>FBNCpNvI{f{K zE`4wB;9yJ)EE1lcO-*o+p-JWDR>R<*Cnepufer!T;x;prkkHdhPlt2c#YIWU$S5_n zx3{4I4OwmNsHpb#=4LQyu3z`_tEp*jj*T@o#>ylwud)*44qaV0H#kod{$!KGY*A5> znu@7`t7}?XWo3Fg+OpQxv9Xnv#l-;uri`1ve?dWMX-tg0y{V~(2Z%xm2^0#5LMbVw zrLfe)a-W=xM7W2Cl$5ZrgTu|64N%{=2cZ-)>~U&xngQsSvf!7)@E-n zDvBiG+FF0Vo!z-}f`aJGjE}p!pE+~(EI4U%b0Hxd9D6>Q{CsioYSNlMz-n3%A$V`k>!N=)3@+1Q}bjvYIGJTP!) z=k42!jEfgx`dnRo_pYo=SorI&&CF(I;BFHY{qRF6spjTauR1#w6!z^C5U8kl`*wEL z-2BTg&!108dG%_0J1FStBqX52?B?d_iMbd0YM3Y^MURXf%)=nI zp+y9y6H@)?5#gMIpCc&=T{pC$(F(%Z3Eeth-{fTUYw+xzo>5UqG6n>=xKJp2?MwL7 zy}iT3>GYTwuw02gYG^1LnqFR*`=V2Ta}=7HbUK{LIE%nP7Z-=ufkN^01m_<4{JnWJ z8i05ue0{;BLU+l+!rMDH7xWuj+Z#8Wor{Wkdh!_-z)P3lyWV2ll9O?azka>35fl?` z?PJHVBelFdGUDWP`ZPf|o1e#i1olh^2iMoj%5-%lB+&3$T`ez1Iu`uXjSa%&qN-X@ zu&~h92CAQgL~=6b)NXDUF7WaO22M>aEWl4MA>r+f)@E|Dy1I;vk57O9ojb|N=z>~V z)z^=YXJ(=~XJ%GiefxG%k*=b#<5}s;C49x3n}hVV0=Oa4^=_cXpP{Getvi_hKIYlVjZxzAA##D@B+d5L&g(i4R|xrXGZe`?-=5Jk(!E@ z2wE5c0q9C&5=^{HGc(a8adJW~HZd_NDI^4>K$sCSGT_48V+)}(3Iu9$3W^z~g`;VsO~g?9p1ebv>4g}%O~rqH8d7!MUk`h>aGc&{-7bIRBp_G+{gd7|i8?mRNtSl_-=-ARSI+~Ou zFVDkcXV=}mwpLLAiY<0xkxhrqNLjg};_>6LF+00cr+9gzqUPr(COkaY*|DpSt~_Q3 z930rMMjl;Kl7j<%e=v@&U1MiQh5#mP9UU-C>*~M_G%-1TTu3N6d36lZKb@dX5o zj$$_mtO`HB+qaQAxPDzq%EP0x^Y-n`Ol@shSy$KA)`^MiY$`uywpL#sLrsmC65YR# z*PE3U>&nf|sVQXsVUAv2h9_N246SiEslbfo;X!I0mbGiw`1wstii_bX)X~8!Q&BNG zT3Tvjqo_zC6&LsQ6&8|6YHBVnb$`15(8Dz`DJZygtE!4bx^hKFCnIBUu&T<|7F~d} zwBg~pIy*ZdAr+OhG>~h(yo80ZE4#EbKOYexEX>2>?L9rcxe4FR*|Ua*&COuZ5CrPd zQXCCHJI17aYHD~mFi=iTK_Mihw|8J5E>2rpUESBWzP`OZF3!;K`gM1AIMN~_jEpoj zDU^&1tm^gjZrt$pE-kIE$7%{Q(E9q@x8X$sHP_NIBcr()7J2NjP^qDz*a5;B5Lt4t zHE@jr4ghiOg5wOq2|%(Q`<_lt$l~YbMnn+jmaMF@vV;U20bO0OtDKPmmo_?gb#c<_x{Pt*rq8*BG~efad0oj+hv2Z50(SuZD)fLBckMUUheOVIihjMn*|VZEfY{ z?(XX91_n`4jg8gS0RcFE`udiZV&~e<&fFY*xtyHva3?3?3MwrPZfi0*A_Dz2kPOIV zxZ-kiF(*Qg9$u@^P{K4-Q`6Bwtb47kVaBbjB$MUjG&I7)+Zi{wbg-tas36SB*aJ^Z zZEl7q53?mq)oN=aBhAhA^|9+l5Z*|n;NXISq9W{uo0;LbUsgt=$;*q2Qz(6X@Q~p% z1FGTrI%b3N@<@Y$i&0YpSD=PQZtmn{eLeWyii*j}yIC2WQdBgv!mYskf&tF{p-FJ(N_Vx!4o<1EOzP)|_ zeozoA>+$1M>b-lfUghP9iNV{ARakNHl`F{k3=Qqw$9#M?Hnz7z zLysRnaKOc7ef`CY#KaRPKKQ`MXk`TyGcmD!`;?VoriLQnI=E_utpn?CcI7 z=HyIDdi;2K+1vZLe%9=4SeTFyKR?>*3kxuGp^1#+CK%j&d=3uX z-E(u<*$N8m?7F(;<>>BcYxDBz>gMMU4;L018;gmVnPCRe)a2kGBZKR&@$rTRFfvU{ zii)PE+uO-x5fOR$$jH7v?4irbDk{RV*4Rj;%E-vc`TDlDc6LTaYH6vf2L-ja_w>ZZ z;u8mt)U8{XSjfnDc(k>Rj;5ty$I!;6wsw3xFYm?;K0bZ@vNECp%g3jxnw~y2)!R#@ zN=d1zVpk2Hb?iikhTgh0HWnEvCB@H=GtIz2Y^=OIKR=1o*odRIh6W!Wb_|!6nwxEH zuUs)U#&>OLft^}g8`qNr^Ib+JI(l@pua8C(6_t<(2-vfS^6`0k4i19hp`;`vHV4pC9ULEYU= zCX0&V*%lUthLG*SPRQse+`S?qqN3<<4h;DD!Pg6FInKvQN`iteE`5DVOGQQ6+G1kP z&fVR(e%IFK=GND*s=~f1cs1(kIXU=bSy+Hzj>!}F;ygT{`_Il!OoW8MwH*S|%3hK8UZ zu2wKJRZ$^krbG|!*f9-_*4A&oZD~qDprut-HaFMMU~bOOkE1sjb)KI1 z9WpcL=O-t@#Xw3bF0Q@3sVON5sS8-df`fg0aIVGGUqApzMK}|KCWP4|JYhug3DZU- zqL6_Au?jOhGMQk65|nT>wvo6%N(kR4Bm`Fkpc47}hlWN(5cCVePzGZw$VCy1UKpMU zezK@2(2l57P_^RXqN4)?sZ<&*BqTN#JTH)vi0fdi50D-~N(2+tJ+1{ve%{{5Xc5o5 zcO@GZhWt`sU}z}81oZdcv%??-zIT6*zC9|ki(Dxqm_uTql-yhfXe{9zUTksG5_;t{P!dO{R;g175Min@b6dP-><;`>nrg0GZS(D?`wp=-zT2w zugCxW9R45g|J7#0*ZkLKWa2CE-}dh9+1Q9a8^MeW4GjFgf|rqs>k1m!3FbZx2>zPL7Dkp+g!PAd*y6C@3&9tEnM()YGG@ zd+eC3Y*p3Emoqa?PH39t=kM&y&%3#yX_lP)`0*Np#eMSRsZ%uC!UAZdXU`r#?&^vj zLR1tx`{BbR5*WT%3$wA2NO$hMecRfKq?W$^z`&a~m=u6OiewX@1CT@ghHecsPu3v9aRf@HtISFE1w~fI#Z(JTQR1jiuiwzHvQl5Krw3nKW##7P@GzOo#|H-e>gw2-rzh-Vbo#==^mI@VKR-7&uCXR3 zF?ZwQB9r_3XJ_ejVPRHQbnwt*($wT)*vGoM9zRBso0C(QK~Y80&y3-j6Bp0UM*GCU zft6v}PDof=o0`I_1X9Vy#@bp)2wZu7e&Ez6CkqL2a(a2q%{_dWo-QPWehZpe1qGs_ zhYnr8-r9;J9Sq!>n(ggRp0u>6seSW}q9Q2Hlatogr%y{umzBMIIX8#dilAUd#?B7@ z4y>$a&-(j=PZJ!>!NJDn?hfk}nkYw)+S_A#Q&NJ)pN-A<`19wO*ql9UZazASJlpl_ zM~=wIR8+t@hrffA6dH_ZY@IkED2UDx`YjwB{QNO7YisCG@$wQ(n)~_poVKBw2O2M6`_nVDr|YHGg! z9?tSZhd4PC6OsRSbv=6Y)TyALjg5Qv{QQm{J#c_bUS0j}yP_gd(Gw>~q{+!=&&tbT z7k6;Dd-um5!MfhRUqPX{`PsAXZtP4cDAd;O?DX|%YwzFB&!3}PL?f64==G z?bFk{bLXd@nwzg&`Q#IM`L3?_?{D8WF=1hmm94IR{d#7`!GVnp@8X9K!E}Og3?!?u zF;`c#{|GxhnSAWn>C*uL8yhgv3kjk9H#N1ni5v$Q{PXk33JC}tIbv=;Ir-gpH8m0v zpMS2b40Bg|yMn@JpGix%wf*+n^t7(-zI_}VdsL!BhfbV`jeX0wMMpC;fBw0P%fpAi z{#sag{yY84GSh#b?-~aMuVd0>lu`%p*U?LD0I6OQ!2wS3{U~us4EE4bH;(UCB@i8Sug5l)y z@>*Pc`Ld)$L}dSdu&A)-jc1dVE-!!h5R9gyM@2;o3Z6a%qlty(!iCt_hYz>5Vq*ma zE?x5Vot<49ZebxY@yZq0AYe<3j1(3=ch1%pdGw?tM~`}X3=CLUWMsMUu6Mz3NzudPE?8P5{OiKd|(aa2HwT=#9;eGqGv|v^vY-%zx@M^8BpaFCG zbXggg@RpWLO#Ao8#r^Wjn>Ri_|NQ4iA9;DbeEHjNSy|`K9XR0Zyt(=QeNhoN_g7z8 zS}ra9`YW;rUwm=n#>mK4pp`LtlO6n6k3WOb-te z6Fa+@n4+TebWcwc6A~#Z3cH+0@LOAlgcKGQ6-7kY*;!eE(TmhR(WZ%x#@;K9=IV-8 zPGVw21eHoK*6H-Wc68Iz(P*=`M>+wvh`>N1q089NWwhY1vx9x(m>A5Pe0{-R!n_ER zA&{6brJ~V-gJWaqba00}JOTs3ZAVLuXagnvNi9Ot2Wg9l2v=8odk{zS@)8nUUCqrw zRL#%N$nf#8v2k*Wjs``EMl(0Jw~vpntwoO3z`)QjFtDPcswynZ$OyaTwG5|Jc=(MQ z>gs-e&CP9XAW^ZilgY&U5UvyWi+=m<^=ljvj~?~%+TQ-jt^UnS-F2dJRDRsH@Bsw zot^Y_ZtlZ}aWww^`=%xpm6Ip+^t!vBJ!@~*)I{^UuI}4!2L^B+Kx6&>{qb=pCpI=d zJ~Y+|o*x&NuP@GuAP1n=KRyntFB(CHh8-P`AEO8V%{N!Bl$O4J4O_)W46f<5YlDNo z{IalsonA39?A+bFiIg_-#c64|xlvI_Xo3-$mPXK@!Tv{L2YZRgP15Paz9Q~p#}O?~ zWN5&u3=YP75jGGyJt_)r5KKg4V{xBIbtW;~BM}kq?hXzf98WVTAW@o;0qYc*Y-vfMppA<3oV~r1 z6TWXs3bLl~QKhElCOmM_|A)P|j*jd+(|vs= zGl@^)#Ic)EYEieCnVF%*%nT)FE=i>-F{`8!Gcz-`n7O59ZoA!Wdpt=dGx4n_dup}g z$vtP?b?#bst-G@Suy>cLcGa%h@O|(5JkO6B5(2jyjRs!~GRpk@laq^!p~gY?l=y}S zA|q7a<>lBGiG)!<(!)^Qh10XLGCm$XQc$f{SG&9Y{bgmL#vWiUaAZnI`1l@$^VyShGn!1&_x&jkeN^sm1@ zI`Z}X?6Zp(6B3?2eextW_KPom{cCVP{`9BXTJR6t+zt-@{O8_Y6_tw@?d>-=|MHiK z2`#P9K2uj89Q^LP>1hK4qWb>f0|-W+e#*~ZUHxzWc6R3H_uJpzxRH_Z%{On~QYc(p zoSbCxvuCegXJl}5U%3(za(w*eO<^G~FSOiqbI@^MbYgEmKM(i1y!^RyW@b}U7@Z&` z(9UjV=KXtcKdxT~Mf%-4j2-`R4dIAmH3N4h~!?kbnI9-~am8zP@naXJz4T33dr_ z{W*8e*7n{#T*=^jF*hF^eEAY76aoU~<{cepXT7}^78pHNRh^uSjk&mRae<_@wFM{6 zty@x3NZZ)iNk|YCm5@kD+1=gW2j>A}BwUxEpWxtda2OhbdmVZeaFDmRo0}~y`T5b) zI5-#>fY%2s-o3rKIpX-C(Nln!;*j;7nW?DA%j@ji)wQ%#QUcB;e384m zot^gf{QNpPWo5_`OiWZ&)zkzPdX34I(9)8V3kgB`KZ9Xp1Vuwzj%DG10}v)iouhv9YNsEzQZv(lRuZ!Dwm8&xac%C@3$lx*B=EaPSougAC{A zXKzm;LEQ~c2ewLhIxtG`^rTY3b%T%I!vi!31|uZ}Q~>1cg8t;|i-h$2{Nm!YH0V2! z@r!)mpddnbn3o6jIS3HAi_>W65~1IO)E?r>i}$g#3=AwU9~^9M4iATtnMA6q>+XhP zLq!FeprxhG&Y&P&UC^(lr+a&&qv22~D43ZU8Uk-iUq3BvVq$bOAwgADK_N7>yL)gj zJ{}4AAt9rqOG{ZCf@;yu6&8AtBJvAnk^i*Vh+HR5)6NguK0HXP+?X!a6#v>uHuE5t93P?Kr z?p--KFR!jHWCwV7=;h9jy07(s8`HqgGqrpKhFDa?JcR>IJ=|e_FU7bc-T%4MUijrb3QBe~U zv$M&`va&dS1_xJHi;MO2L`7X)dwOH8 zcUf6g6?nwZdX|;d)KDlGmB+@Gl~q)vq8XXn^hc))UU{QX^BqoP0$ zpi(_NFj^`vZ)$>j%goHrucV~Cy|~!N$Hc_VEkD1fr@lTi(%hVI^}|^P7jIfx(_dX? zc6PS5@$n4}EiI|3P=5slRaSO&6&8AVfkjzd+|j{eQRd{dwpLX|M>{%td1Yik0R=9l zvvW#H9h0LAzA|)@_+GwU!oV zXXI8uW7g7wU)|6!FK=OCXeca9K|x)eLRnl~T7ptvO)Wkix|G5~eSI03fPmrQot>H* zQ&YmNyRy>QXlpAfYHC_lwYCPOh>(zu4xPTYhx3&fJ*})j9iq-$Qd5_fR#&N1B_%O2 z5@~R7VId!;FmB+H!Dc zYSz?%aD&V*Ma8nR)6>~mXJ_c3a&lh2eDEMT`ts#Vm!hJcJo)gUtV~Gg@?~G&S;tM`L2IG%^ z1VQb>g&Q}h)DIutzo$?xUi|b^FR!DcufHxXM*6g!9n_~FPk#ETmDT$CpZ?U}ucY*w z-xwG`v$eVkhn%{4fBzrcFW z7Z)ENnS62rhZnM!(}qxNlxC{ zT3JDQ4=-g69L|SqY za{-;*#|N{L!orjkBEcV9qEJFYK!-wtKdJyYKjCVC5+o@pK0YpvP>E2fnVCPGi@|>Y zHv+-F#{7w3Wy6UCrU>{wG#VUkaZDOyN(#<%mZlUlIgqoE_(vv#ei9j(l9J6_Sb4^% zF+f)We;$Pbx*I`Jfhq>h3nB%GRfQmudx*SboP3N+S<)6FAv^mxKjiLlan!upO`=IyY{yg&3zyJP^f6UFjdi58- zaB(?fl1SRxKuAzg-!b_nRaIPEzxaiv7X}8)%b)^iY5n3Cva*ANKmK@d;OK~~iTe6K{_){M66wn?+1Y8dmoFbZii^8; z?b0Pwhxhgp6FE4pTp^KmcA!KR6}@=T*?Dd4haW~q@7`r&LyzPAd-#$#IpyRE3J9vE zhzPXLD=Vj`n1B54cW}8Qy`{AkcPA&OsHogr%$e~U6VA;1e8Sm-1REkxg2<3aO~w2l zcPJ2Xh}gS(!&Ym6o~|91Qbj4Y z#f#(_UthR)ii>f^`7o2mS@(Ca=NSylI=sAWZE?;LYG(@zH#c}0%F9vN^75imYik*d zm>5S#7nj7u%F60$3dPqK{X4iku~qo=%9u-XGF%EQmYb)itu1mb8XHSXBOOA}pdwa{v$bsbJ3kzFWIXkPa zHZ&9wadGMEJ38v=ad!vxwyth%t-IUD2a5NCg6U}_gTW_AqYVvVBq=GWsY#eC@2U&+3YO(e3Fv4Z~OR+k8f`m6{)H5 z^18YX4ILdp4=OC|;D98|;bC`o2?@fbK0Zz&Nl9sGWoIugB9lW#Mo}?7ew4YO!XhUZ z8Va{gb~aSKEEb8YtG2eEACec^+Wh=9G;D35{|6CHLql6THnyt^1a!Q@+qA4KQ&NJ0h`L>Q zd0rm!BjCcrJ|rdKzJz-L2#nAe;vRrbI*|oOutK3&ME@KqZNlgvjNILQ_zTOWLe{|9SyIxC8()0kMWT{Y-~+UT^(}FY;B{XtE*dEv$H)sEG+!};q*n4g{^HsKv5B{xjsHdMi@i& z_qViw>I7e1dwXYRS{i=wxVYx#j*hf6Yim=}n3#@^;o;m|YimtSAD`OV@$srEZ*N0G z50BE)o*wv93=BLy8H}MJ2E)xwUESUupS`u!&+nt}IX+%TM^BGT9vz*Spi+&E;Q{RK z9vj1q1eA-$#_{p8GFMl9eJ`)FvhMD}LRVKqLvQc$^8S7X!`Ii?*u$f^7~T<#Ks-Ic zF~u2VVd3shBo{?QAjyqPuCK4DiHozhM}?}fv84qwM%=wXs7p@=ojWS3xR~JIIXYsd zR$Wcp?_6Dx_eRhlL5YGJ4^?q*Z!0TTR|=)8Yka)6HaOVW*wQj7simd0l}t7@)zZSJ z?Cvfqa&^_yL#|w3UwOHwr27gU(e1uI3SC!z5SbSHa0vwu3nXp zU@*S^8a?!L=h)cN(=oz`i1^*_KKm>@{MD;J{GqV$>Q&IG4-dgKlacx5FO7`QF9Eyk zfBsJumGNx-u3z`{-QIrtwz^tc`qCx%UY!6kmR+s@mK8 zhd=D?d3jyCCM{iC``vfAB3!$6`*wEr*I&PVo1V_j{>2xbo@;AQo|KeGNL;ysq+Fs( zg!6J}=+Pr2FyFajX4cVxY+>XG7#pK^i@Z_f8fIjyt)b?D+%wEmQJYs#0NoT$2lx#n zB;4JRTncWQy1KJ-Rn_clOG`iieD%4xy}d|vfrBY2skIe8C4GGplZ1rU*0#2!By`i@ zTwPdbZ$}~kvN|ze0RJl@f=b1;Ga&(^dk{p3oD5`IU@S%)Px(x$7ufFvUl|-{jOBB3 zKv70lkr@5s8N~DR^0KpWHTLvONGL0-t3y8!>=x8B84S#90s@c`T3efzhPnu9uc(Il z`yuy1T0*3&a1qrSePA}-Fw#hppp1=kloHgE5^IGnd&!@*~V z-c)90Xejzgn5_|1n2-=;oPhNP-!7}02CY8wNeE8?{OP!RVq^eMaBwg(Q3<}czdu1G zE-Z|X_h52w;^I)*!mog9O+`gpTV9@rhlz=gPgxmKp?rOHbWq=&oFurnnwp7;6BG0E zIXQ-g3JSiy?d_oa*xRFKSY6%OiTn!%g|M*sd0YdKMy;Ykr|<7iP2orf5qM!?W+pC9 zQBhTuLRncs9YsS!RW&trbMx@9pa6B0prFM?WR2_SaB*2#Ao%G%xC*hor}l$4SplXsZ3vg~Z6nqUNoqz@Gp9v(->fq}if zsw!=5PEL!z(xb-4TwMhPjhLL_hYtxYnU+?0`N;|Lo&^LXB*^5owUw2G1TisT;qY+e zf+i)2i6NN>DZrq$^76X7BX7Q~4VfV>E~BGQo(v4w+Vb)uJ^u7`bkvpUZdFw+DcRjc zX4;)QaPw_5GX%rJkcCdr$IzV-6^)KwU%!99xES^Ez(8=%Dl0){H8bn%+}neXg`M5V z2<#~&|H4;-TKVFlpC3QJx;k918yk_4!ou?Ma5$s?BPa+33lb(M6frR#o`8Va+3oGj zObH2gc6ayLS!DlcYI1PkdWF6(D%sZ7NRb{Jv$yBw*3qf2N16k&50QL#cnF#r^hror zAvjcge8_NTaj4K)e(<2MP*xVTosErW&ln6DnTr?E!#X*su2xdIc+uFnukX>L_IBhh z>FD(IeEs#}A{0;>8qLjb-)?L`|05^IU_fIP9)9ZaPFMF zJu;X|OC==EpGR->;lr95^czh~#>d~k$2-7@+}HQbH}~#2J0p3$rRCkbjSbv4L`4e= zpFZ8$2@bw-gNG+M`S5UeH#+*(Ep~RKYMh;AW+H_?Ad!uaA0C#J$jV;7j(vOmy06dFl$+biYIqpxQU?d< zEIa<1i;mw%L7}wtvUcn~<=xv%jB~CMwFy8y-$L*;Q4Ag^65r zuwl8GJUx74EiDEH92|G=wzVPE53Y87{l>=o%zS#}MIZtH!GqP+urMJZ(2EZbx3&@! z#hLl^si_AC$H&OnzjX^VHR!M;C9hwHjt|@ec_umA(h>wUXc5`i3=RAHp{?}vfqq{b0jKaZh$;5218L1+OWaF<6}^lFJ02pYi&h!*wpmqO-;?Zx(5$tXVJ5O%kT6Q zS2+0Y3JZ^ppld}ZBtCv=38ZDzGy?<2#*o5=3<6hIByZ8_rlvA7NZwjsN3Jt!nhgyb z8-0BN0V*nHW~h~SbOZ&ds~a0rD1Ch`Euo=0I(P4;rw9)4o+90Xo&g|=}sfjF$@^Yl!p(=-&9#Vth zG0Vs(F2)_r%?~AtTE)G4* zloYtwpvQ*VJ2@E%EgzNIEJZi|%2I<9>p%Z5p%`cVmvD`-{>Az~YYVJ@|L^~g?O^%& zGk^Z>fA8Nvf6agYH(vGMAN}|U@!qWeeti7@WbN;LQsObFd z;2+<>|Np*$|J|MH|Gho_U%%7e-iv>FU;eoMaqZ*i;p6A~c#k92h%FND`nT&pb&LJq zt^d8(;r)NQ7k|6ekAKDEgv%YHo}aFh$t*uTMnlBb@M&42BbGNFe}#7~J|4+-gg>9f zg~fBA17{@%pdLh}A|n7C`?NGv-(zE;+(ps=m5T9LY%GzG$NC1LAdiiOeiyky5fLFF zv9ZVs%gl_8CG_X0CQ&GGxP^sLsdRct37L$(Mrdeuc4Z|rxZuVh;}se|BO`nJ{Cwy* zpf_Y^H#V-X-`gW<(z?0~#{K&%E69yfRn5ygJlxnwNRW^qI66eN9x00;rO{}7e3vhK zcq}g7zn`1S&;R@1+t`4a)z+q|dF>kJSYLfLItm{cWPEMkhuBqA7^y0`ix*%nLuCmac}EARCYqW= z4*>2H1%=vL@RuDOFJDHc>YF#H_20fNE}onFp2k*xp`2ft1wMt2Q>HOrlj=8+4ow4q&m3jX63B2v}J3^t^mIJL};AvRG*;2zh~l z{QNR9G}^|-{5(;ANKW2hE=ftEq5=XRnb;yC!NE&QXH4>dhQ^&c?(WF%s;@^9ysmD2 z{mxEv^WD3cxz*L}GZ`3mcDHZi@kfu~Edpl+^F;8BBqf!USbjV)F(ILd2=MVx?cnA0 z_8u8oTg%N=QsUupb{-r&JZxw{PL8wlz`%nCeSMCOQ08=XJ$eK_)9u^p>U28l4*>zF zmu6;eY#=2@NJv_mLV@NjJ|4;Tv9TK)M@Iz(Dk@@PpgVxxqNyn&f-I1e6Oi9--ZU|3 zZresu$bhK2_Z=I4WgBqX5yIXt{~FDgn}8fjeMn8AM}E*=}Z zxVW}Pp~%Px2m}O-jjgO?X3ERox#Qt6HgL<6h)|h8X8tqAcrP2R9aeFyQpYy5Bno7 zE-jsuw6wImOeV|7h>C`UOiaMZBqt{z;O{>*wZC6puB$5~b=Rz%4*dw2Vwl%)NWNy9EVc zDWFpeo*dH8kO8!|#$f2`f`r)Gy17|bhtwNW)3&y=v+;3nZwUz#lj>^x>Y<^svikZ^ zBQGw3=7{7uBo0A8iG3I!$3Do)^7H%oO-*XQJfu5wAq+S=ftkB^ubvH{oDrl!KeBqS6RC=~GGu@Cqi7MZ-rf&z7Q>;v*< zN=xCq#1Xo=iTo*UZcEFywvCOdDt&z(9t(@k&a<GEWKBT6~$%%>KcUW3VPscvM0ef&zT8jE2d`V|#O-*<2ii*0skB*+6qSh!N zU}e?bet6i^168=LZfPl!YQ4S1#I&^X@>W+TCctGw`q2712#!ih($X<8OG~VM2n$JAPjCxe6K<&BNY z%Akvjh!7J4e{F4TeLW=wtOEFL*Voh2rKEUyNu>Gt!$Y`udHMQrl_c&A4p9# zHF|mi0;tX&9@f^sUm;plyN! z12sGH!NA9pmgeNdme$wv^Ocq1VnKhtyxhnLIa5td+uNvosHxf7G8oItLqkzfy1Et? zd3m$5V`GVlI?N?8ad;RCE-fu3rLeGpf!SH;RmH`Tl(e-~TWf9(j|KQ1?d|UFN=gKq zr>o1;6RBHeWk?b8@{*9y)rI!3ug}LvR8(C(FK>Grd^~=BDJi&bj*meG6%QYJSxo;<0k(bW|c^zgv>-QI3vBO-zX|9kg3I=sD+sEV|w;o*=FAt82l6O+=? znVG>s&3CiJGk!BDRwryV4z=x8VhXteNfDAzJG(dk5@D7b4S zCB%AAP(}u5a*>g4Zhn3=8dNy(@vg3JZi$KI&E~jI6BSVDQ^$wDNK&+>i;JoSc)BnTcKt-22eSu@ak*xfB^m_?gnv zV`9*8LGEu(PE-`eGEi5bi-q@%iYhHdM~p;rc7|i2wibOaCnxld(Dg-5xUFq<_2wp$ zucV~J!~z0(duL~JbD@-RacOLvonD?d$7TSCKfWq~zex(sFz}IqByIbqpLJ z$H$qO($aVCczKPCY;Km6fFt1I(%ZYUQ(q5qtEp*Y{|K_NVRU|?#BO4ZVmkZ^ISs~a87&&MZpa)M5>uFlI#SJ&OWrUpzZkT=}i z==6z+_V(~_Lqj_|ut-{4gM&3S4GqcUo}Sj$hzM6|+r0R{0EiM)o znwhDqBei#FiNUb3QC9Z$?(9Sb)4@Sf5{X*#^X=_kUeeO~`nkD_i^IdXClEcPwKeE( zrKLkdk)u{u2hFX$eOue!USA)sz&bj}Fz@e&CK#!xEiLWs@$rU+`uZ_3U0s8NG@6yw z-MdLi1j7jo8fE45^qHC2S@=zm@mO43Q9+>~PnS%F;tahrFE8}&Dk~`zKR<%uL8rqd z=Ie`0uDm?tN+X#U+REf)9P>!YBB%m5x(f?wGnoVWF?Dt!;2HosN`wkV3$JYHkMQ$=aIG zT@)~RNyf&6Hoc)CGSbY<#s;Jl_*rl^B_)-YmzKuF;EV&^rJ^D%%)-LakxK3CY;KN= zGc&WWNKWqV?(3sa;ARU6X=xc9Eh%w!*4KyjptrZI%-`S23d#=nWz3mWHe?kZ9JIH) zxX8*{S|amqVgfWnJ-xKFv9Zxn3dPdW);23^WTdAD>6%(vadAC8IQEp3l$B#*#>eO8 zs8lsIB2Y=C=Lfm;-o4>r ze}8z+q48c=NJx;Alb0ux*VlJ;a&pww;i}l!c=!->WnNwpm#veLi+ zxqZ0%gUSJBYDLAvhj6ioi>s*>6u{AfM07<(@Y>ebV`KUFxVZxZ=jJvyL0abG^7h6z zP+AI?7F-{XA9r^nI~NSt$B&1GoScM&%*~O}IWhuWg}i)G5ok*!5?CK(^2*BWY)A+% zub^OD-1_?NE)q6GL?R+qS23d3*O!t4ry3)Auu<*pi40nEbM!20YeDOvN$(3Mn}WLH8jl4L9!bfii}iO*V9W)9U1BC!!NC=86Q6|0N0SJ zs+=5&)Y&;XnU`l_p`j5J)Y&;dUsVOY3KDr&R$5!Vyi`=6QJI+N?v9EwFfcPqPHt%- z`t**DnVDT(wY4OYhK7+5l?sMsZmzjGxFYTCb943e{{E(>zP{zU~f;M)YRnW zdU)vT+uKvA4GkqFetw3A_V(G?LqmOiG#V7z$dc{qN>4}PG_*&(y;)gyb_NE1eiaox zJ;lXtZaO-4b{QFPgoT8dnc3T?q%<_t)FdXly29<<)C8@Zo14FXNy*q4dM?V!7+FJ2 z36C3;op7@vH9jyfGBPWxtPDI+7ngv5%*>h^IvqSwPfxHv;7Y;0naIOUOY`w@aRH~P zq5^j?4-c$YRpsS{hPt~42j}F}*5cJ}Zn%08Dpc@CI|rppmu>rP&tT;Bk~6+6e7tGgeW5ShNU*AP;f@^ybmj5!nVGq{si~o%0Rdpl z(dh(T5=tlr13q~-x4=NSSqch3eem?m%4%vVFAoWU@(T1lWLw|4gY@kE{de!swHFpP zH*ab>JRBVbY4Pq|24iz`c-Y5BSQxd1tu5$K&z-w^H8S$_^zB=W+Ava^o&E90{e4&0 z3l~I0==68*o;`!J2tJ3sJ@`|Rv+3hAIeBzcS*fkf!GRgn=4NxVg#|Y^^ah|Phlfi` zD=Xt{T3&`*MO8H>2I^RFC1hnC9V;s*CTeSaeGLp;Tyk?88?&=rTue;RnS)lv))uMM zm6eN&Jw2hJT3VKtMMaB?aASdfMJ5x>4$w}#y^(30n+r~di%VrCc!U-fSO*2Rwbj>G zOAB{-&?bX})zv|%+uWR+OG;8xQ&x_M=;;|A#t21EFEq5S4ysiP<^t+odwWcbo}Q5r zg)%iYJ)M@Ot*xpW8#_I{zFu5xU?45+swla-{kgfCnr=GEYRE;9M}cz-`8hKq}vJ2CO}6p8^Jo-0>^ zf=*7p{kFbdRu+9^C^pv zqzGKO5)}o#0n(k3zq7UV>QzI7jt(E6hsX5vlPA!9+_{5s+x9k*&TDLpWZRw|IOp{A zF#8xDj*QgMfTL<=W@IEjURT%HI3r_zer_%;O-)ToibNV2+23z#L)8XezCgKVL?G#37v9X{#85y8C zM@J)_1L}VG0HKl&4`(Seot?wNKxoU)Cy_v9!xgunATSU-VDKd~Gc7IU24GcIr@$jgs78UL8PEVuKCo4;%ZEh|uMnnh-ii#oy95g5e1u?P6$fc!&gQ6l0 z4GD?x@TDcBfUB#6-HHrof|GvtZhQOgZcUB4I&?tb#*Hy4^EYp*t2Z{jd(zf7JD<}{Vxpc|dd2#W@i~4#sH4Y9p zw|n=#{<^D6PY-_Jg@y0F8yV5l`}}hSg~rC`&!?uqr-I}7(W9v;N5|{e<>d*soUbn% zn}h_iXbuiSL)qB{1@rRWym|JFOy=OYc{3*F^z`}j%uF7h3l}^+S67k9#m)WO-#R+3 zufKoa)Fdta+utHX=MR6FoHQ{3dvt2*4}Vx(w6HjLPC=oq?Yr-Gc04>TUKA9}&j$;^ z&+pnbc=Moq0ln_#O{6w_{dGx+m>3&dKmhcx*dI<#clX7`w{NjOmoIB+f$oXQC5zQC zFyQ4SDypSb$Yj?~Pe(^9DQRoN=`k~tnyRmFXh@~b&d$uFq=1MU9Sz17ejhF_B2!>w z#NGY+^~;y7t&uh`Hby2}TAG;!1!1&NT%4Q?Z#wSqaGnvWe~cnhQ&CNTYYYk!{1yEO zqzj|(keZ5$0pNDDne0*ssK_l?>#ki`x zoJ{ui_VkR8r_9^PHSQLUMB8d(+nH#VA>2n&09&dj`e)zf2a422yiC}U%`wl{9TGkbC}JL}^E zf(WYg^YciK$7pSL7u9YF31#IRCVc}sGBM_ol(fB#%t0hRxw)Y(QcytT3_>kcRdx5S zhzPDlU|V{7YiW6TwYDxT)z`!O?&3nwy}-N#$#-R?ug}+4LPAp$^^)=NhzMzES=s1l zWS=AF0_2mCk<(LTX>fBxDcaaLGt<_FTnH$s(wV$uaGl^_Azal(Mfv$eUIS8UN=nkw z0s_EtN3IQx29GssG>_3OiIkDSV4zmu;^ONIJ#%?E;eNx>QdS0&yWd8MVurGbx* zMuS_DmEV+~kGmg^{p4g;E+wjYaNS^3j`2WzJo&eXpCDq?QGqa(gvN9&d#>Uz@CZ@Kwtt}_V)6>$@*EcV(v9YWSGa1ZLN|;Mp zT38sqN6aILH1gnJS69^4s;g&a#>Q~x)z-$m5i9_If4H|$UqF>jPEJK7Ep26GeVs~` zlS9?LzkhA5xL8y3_HB$%x3}x-jf^-r^!4lOPfjK$!HF<6t*-|mI51F1NLjh4=gN==xoTq1h z|G@!zE&TjOMimvP>AARwiXvfQZmzY}*%{1;va;P>bV6`UBgj89;9y-{C#UlAnHi`7 zb#)ybD=Jo2#>QfqDIR)yiHRW4#>E*Jz*ErM+tU*lr>(879u+k>I5$^RWMQGI>hIsl zq;=KSIy!=u=j33% z91u`d)z?R-`}&%hd3ojMx3w`CVPST5-rg*4nT?HuLvnI`J@`(}&i3}Gf;Bd#r#m~F zn+FD#m9@7Q7J7S|ni7?-+S-T+OG^g_sPh{eQ&L=AU0gsxuc`tC%HBR9p}xMQg+g(3 zw6F*ZgIYZ=&)xm*U0>gllCCZW13YmrFT$Y{9Bg6X>I%(&LqklAg@u(B(id7=X*73t zJ3CO-2rnq|6G}>&nu>~og6!?Ry^(lUS{fhk<%KaTvZhi}LPLXt9y~ zNynTPBU2>eqtlN0Z*(-F<3}!5L$zky?q{K) zxU+_ZH8;=BwzZK+sEh>%H#hh8QmLk<>gv9}b#)^nMMc)u3JUi2494_yLxYX-!KIVGjF72Hf$S4&G|^i)((sovfg6TpFj@7P*2am zz_hgD;@sSr7++ubR7*-&v|lnA`<h%J(o#di*SDbo zjzHv^*xD8q4GvaSxw|VXo0?{4kB{Tkrlv&2q`MmgDQD-*%z*(^$gQkQOroM%TKf93 zvmG3ajY%Z9ak8?Uo$uZa3`E*XULGpv9v*c1zySP&Ha6bg1qB@);J8~^!I9hEUR@m# zVP)mukdS~Z7Wj?4yfQOuYYPiWBx`FwKRUg?A6`IHQ+IbdeR8s^D?D6B$J7)A@TsZP zR2?0hHGO?kQ`y;I$ocp*HVzMGW}e|w>v{YB;=%}bj^gw%hz)Q5YuBqACnV3L- zMMozi177Ed2;{G%rq0ezPJ&G%D;pb&IV9Mut=2lZHEL>gf?R9avb4N)jHy4~oU*FreB_%U5z~0WuQBsnWM2-98q@+Yc1NYi{ z_Z~dJ+>e99+8TB3=4NAKPEL4n_VHU{got?jF?;2=THZbikL zH|Wb=y~@r`CO?1v_H9HGU2#L37&|FOG!a)P|6n>XFu=H{M0ZEDiex^hKZo6y@hIPmgnYt!kV z%>)H0Dq2|7)`GZcW%c>zw{E4RJb(V-Lv^*hJTy(cy~oFGZB|yo!j_is0JOC^I*N%Q zyXMkhu`$H<6G?(gsGOHMX1K_3UnUo@J9g`QqWNOSWrlecDPr={iTSy3@GR9Wfe z1z%ZKR(E$(6KK?~uIcFw4d7qe*f=wJjjgRsO-V_%ww9LB(csykCU0);>swIJ+)Sqv zxv5ktk(CP9IcihLPzwt3@IXbWq9Q*Zy;pyK%Q*!;y+i zbse4R>c@{kQ4|!Emj`DU3E`+}!%w!o4Lt!TCp2jf9+Z_ql?UzF#J^o88?uHtg(}$Dv0S9E@b@tgO@1qodSR z_(d;Ay~c3OCnu5nd;dNhUc9_umGA8(C-d|3@I*$!v6h`JD0um@o7>XTt5-PNFc+Pk ze*V0zO-Ba_tCN!-K1@!Untu7EtSnsY3kygc5)wi#{JneL-pGxn(H=j3@E|3HkB^5Z zF7EgkZXy{OKE9}^!^79F84RfPz%YkPO;z>$d3cnbKkx2FlCz3ROUv`; z*VMqTL?Ur<$;($)fAh`hX+#7Y8?vI`ym|F1Jsp0f(9pxfr%y98xw+4u_w_wIeE*)o zxO3-U{>9vUW8*J>868CmvaT+A6U)m+M!))%j7)$3k3XKA*xP^d$H)vJ`0ix+?Mn}C3a55NDuyqu5k^UpmzPEP*gKf1c) z{Oe!q=wPdhi+XyWd?F;&*!cbT>+2R4|MX8B9Hph-eRq25~ z(%ZK`{7_QD%X{fkXy~(NfBth_9Y6mse`#T{x=QeGb#SA3X{Wzj*Qb^`xXHPo6$aPQH5e^Usls{^Ut^ zHW%0Le&_7Gx%uHkON+ex`SVs*3kyH|04f1HyP4Ve__yEA%pi{&`JZ2Zy|RKlZed{@ z->a*Be#nkWO+7w_H;;pZjSY$Zj~_!NB`6pgy1xGOX?3-xCLf=V&%(m<=Ww{QvB8mf zZ~$EybW1fgdwc!;=p1Nj!u2{g7ZxTX16>*PeF+IFDq31J+VV0gv5Java^Q{6&JwwN zK0aMtQ&TjWnwp4+yL(U1`g(0GW{0k>NT=%T^zqTubaTTPySv-ZPesMbs;unZy}rJn zAW#G;l%b)H4iMCIbjW1%Dx;&BQDy3JaTvMh=2}?j>qkeUTT)zXXNPNYXD8^J$RKfX zVK4><>2xq)U0lk`M@A|u!9=#Rqtj<*y1T=}iP6vKXm2mN^mcYxSwlnJ-Oy0rO5fSp z)s>WFWTd4P7zie5Vxox&_?eB30|U9a_V&oaYHpsKtg7g#{<$&DKf#*fUUy88TiBr?s+ z{P^R4{wJA?Y^S?-$H(8iLCsoTUSB^YrL&W`t08r-wzjGYonL2X(2&c@kjd@OOy@*h zCNmR^@2Duy&52Cp$jHRRv@~R%puU269%gyqs-bs*IUg}wglY%@s}EtKG_ZG6;~Lle$Yh@)fjah-TiLOG6ge3qZ8$XV+|9f{lVXD{Gw~ z!QpXe&@t~N-i@d<{d^r;!gEl?WbHX?9q;z@PeHsh!I%5EPJB}0omrpy<2$oH1?&4H zJ`wS~u-5S$;(Ny9sEM(@J7S-S?~%36`nCymJ)yh*xQ^En`-xwNQ0L=0M1P8SE!J7T z$lqIM{c6Ogz^jQ*gjeGi$K$MZyaTaS!V5sGldEOx|U&`IHQPP4bLZD|I@ws_|6|c(|_O3pT6S%>H2?v2iC8EE&Y6*^*yrw z^7r12*t4JS&rkR5r|Um|2Vy_54Xm*~9r3Tf|8B%{uuVJ*|0R~6K2AJBJm>GNfBehG zwg1}8-)xih*gyXN|HU^z91p~C`SbV3@5cH&@i*4_gkzRPYsGyQIto_LH8mAFE%bk= zR2q#;j*bQqHa$H%o9JhShbJe)_n(>yE(vRV6CV$nLsAm{f@I*35GXdXvyn4O&>*N( zWPd^n4m}vW{~(hPqle^V^rLXk$5^mNL@cT zI6XxziiZazrB|=Y%f-ddowKmOY#+3)D_4w-dwU-}>h3l&x^Y8K5B!nYSr-=$4pr6a zY9y=#1tImOu<-F?=%u)sOKj}H!Gi}G8T|ZLuDH9S)?ZjCDti9Bjm`A*ixhdz&5JEyAK%vho$mD)u zw)W@|gMk!CAD{X8$B!EtK=W~Po0-APO+(|t1=I>2Jm~H=HU{6Kx%t6^!9fd)>(`+U zdGTUt$=8>M2kw*8)3voorZY=PDJN%tA5Jq=CK3}@S2s67fa2rx^BW&uUCqdV6UEI9 zKCp@k=pK+x^7LtMuZ;~fZ$m@Ro(&Dz*+FNERLhA8S68TAkPQU46ECl#BKp>IbAf?S z@}{RB9UUH~r%Ou0*S7Un4wamopkR17((g-3P){I|#)zCCOUwTL_2daic04>{VsNGG?V%>Z#T6dDy1KIis@=_-B+~ji z)GlIT*RDZ-^5#u*v!*6Dw}%I+7f^@XylG)EI{M}f(pK<|)z!hV<>YkhmZBn^e*gab zytg+yyQpY(_QQwA$4N;70{r}miHC=%v>=x&I{M(?*|UNIVd0Axot(h&Aaa5%EQW^e z->l!9JpAg_&=B%ma9q85wYZ3HNmCQv$+fk>KyGdciJY7V5BB$CV|jSExuc>s zHgB<*@*Y2iqJfW3Kp;8!`1rwt%uI1{e*U;PRABP*goLhKadTT( zc=(V`my)`0f!K$tDkY^WSFjH+Uk(mJCxJTE>(>hlNFUbJtgnCZVr?xTfQw5~5-Oej z{pe_J?%TKH;b$ zAp!itygU&RFbfwJ&d$IuymSc;SR~jfDP6v7YRdW@*x3yXy1U=JSy*s$-CMV?53JuoLj(ITI%;pv&aSMC1e}=}5E7Ay`RLK!UTiEslO36sc64-nOr=Um zLBG4Zdv=zWhqKSqb8>QTudq-~j*ZREuD^eKyR=kEiH*(1W@O~))9!BM8(CO@|31N_ zXY%rzm~?l)emytm`8#)@W+Se%0s`n_Y;P|t#Kg$Uqi(yw^sq)n z$;v`eb#Sn<5*f+ICnS`ZxV^o*i!<@Y4OU*0pdd1qiQ1ux%K7sqCR0;C{IIZKV}m?V zaPzjdJUy>o6&I(|;Ux|WyMCRW9Y^T>`)O&syf<$K1VBetTr4BQ#^&riK7MxA(qd^T zE9>tMFL70sp`nPwN_4S5^s0_pJR#RhS#K&iC-PLt?*wm!2&&G!IuD5UDTxMfa zQK_$g{d!{qYBotpI{oe2v$L2OHa2#4WThS-r>0^b{QcpbOkq+hFI+%M*X!5y^~g1I zaG0Ne|9*7T(h`2jfdS$-xpN11DR?Ix9l5zRG%72R_-1bp(nVR>qeqL2Zf+MY+`gTa z_44J@rzt61Tx@J`(Y=3PQ6VOF{=B0jYKo(y1_ob#2{y*b$;^zOpMZdzTx#mv+~j0Z zl8%nPeroF2*!VbwVrYoe-tqB`jmk=6V>UJsk+86#q1Dy4HYf=k9TOALJAfY5+dCox z+EnBkqJx^841GRWYv^v~BvM#dYAU)1$T=sRaWopnQG`|= zH6M(yP(xsiS1{)xY6S2Q5zaYi=0Q=ws05y(pN_j(RRoqc0HYR+VBpe2kC_;2#l(b$ zMn|WoLvfRsNbt3Ca-gM02RJ4M`We)p@VSVw4aR}QCreGG(cochWXbdU0|lCqH}k_HAAs=(tEMfBLkrP+0iN6<62U zS)>FC2%JBUBM~W}3JOps&dz@LFg6CFsfkHn-&bFuzAq@Kr&n7GJ_Aw=6%=T+d-q00 z&?!+?&dP%CIUsF9C6bc8&rAq+;aJwfZ-Mo43TtL9d$@}+oIyd(}{gZ_SQsfU0?d(4LjE@gg-?K9s zO+f)(?CI&{<@|hOV`XI$si$Xoxw;znCSG1IFZgNt`p9G`Wg;Trjm*lz*cIswIXTJ6 zp`jrm=!GH0+}oS*F4otBTj=26?hZva)R10Gr6ZwO%gRFXWOOvtD`Ya1E5z}Kb0IvO zLMbT7$wA^5wwjxplY?V1EDRn?;$^y(r zprT;$0@Bjpz>A4VN%^U|4OLWPv`!q|AQKSt3j78C!pwrGR}yC@M)lBX1P6zP;`4!| z1a$_o&5DX}2B3zUk%19D_6Xk@g@X4a-V4-}@bI)WFy)|J@b!(0%VF}K0|LONg7C~>+!rrm{PXlFk_#_ic5+%;`sSP7UPD7J zE_-_~o<1ENwY9}{oxwOho|tfV=j4=?&C7$^J1`J)1Ng7^_9zr-X>swm zxQ&hDV`${0q{71&7NFvU#v87S|qG4h~b(?(WBr;j7@|Gc;^& ze*73|k^%xcI-uY#E+PY4MkYW1WyQ&dUfK${vF`Tl!ki(j}PBLi~c?yi^DmtS&lP$*|-ySvZ@+`Ngd zAhJDpc-Yt?B94z=zoygS^oWSSxm!~sDS6?7g9Atk0|T0xzyH0aCN%3HJ6^h^s*1Ev zg6t?QO{YJ8ytx?}DI$U~1ycKCV=<;c$^)F=0s@kfDJd%}@Soqljqx_LjTpmnaA0Ko z^l3$fk`gB;D8-=3nwoNQ-o4x1{p{J~B+^yY)adl1BV=I+3PO=^@7~;8TpYZ}p`nwL zYik7sy1GzqbafHA-#R)T9yK*HGmVXYe!9BO&bUff(8njaj;F`0fBZEo^{Ypi3~x)t47q$QS-t%Pi1l{ zQH_8b26yY6oP-2_e?Pytxcq!5Ou=(P@^DQ}VPSZwGytWoDK~GO3BWLIA?5?hsmgHo6du!{6 z2+*NGn)C304i;)CXmw*^pz_YljEKNq6L(r%EwQK2{y`ld6@{!G3I){=+_$l(*l&zA zpw7h=7OrJ0D|dGqt-E`8m`?Zb(9|?FO-^oWYj00ZCU}#zwF3ii<=WVUhoibsUG3{@ zV&do5(6F%3)&{kYv2jk$%nTC26%|2un4FxNN={a0CVhm4j*h~WqM;!y?CuV4Y-_87 z1N>_(E$I6b%0Te`T3hYyg@g<+S%FMOi7VrE-5Kn zThKA6s=|E>0(wpkG`bkAuCK%6e(RQt%kc2#W_C8J2-eIb{I_rW`;Cpku$-9q_FGV3 zIXH}r1_%H3U)R^2oiAQQ{`i|WpcY-Za_0_+;rsg$5jSqIv&Y6_%$$;fIb>+)?k>_h z6%@e3*xr8eqOuY?bsry$VjCJ16u$gYSGT<#IVpyQ7%#N79Uiu|!9`|lOgMC1T!e+; z2gcPKx3IY-Yb8K5X?%mzon!Rofci(f~_uMh=r#s%WKJ2}^ENZO^>sin9|IhiG zK0a{o$HgI)jZ7XJLyjHDtTr~N<+QeHYqPTAj9$IEaRaIhb#+u%=I4nNa^%&4tRpTi zAwi=}PtVRqM@vZX@p*fXj)Fv^r6nL>W!2b-bW=x16%|Lv>gutv+FDmvFh@&D1_!IF zot;57O-}CWs;l$yQB%{`kB{%|?dpn*)Yeu}@$>8GK*!wI7pbWR1~giAbz2*_-0-)O zNPd2)sf1$J$q89)IXQ67`un@N!Ix27oR$Xa1jrXewk!w~NlDq+aF)QYk(E_g2%dzs zwXbh>c5Q8LuAd)tz$qzpbQpogFNf#GnOqbEqaU2~lGVZEYPL z3I$q+(o*CO+S}*nU%lGUK%rP#LJ>YX+R_4IguZ@q^59@k4?&5bP`bMM`XVE77YPX& z9-f&&&YGa0i_6f^))t&7+}xNY?e30^xw!D~$jV}^jq&8%IT4ZY@QI1BF$x7WP2?63 zzg|&MB!aE2m6T{{iHnoT*RQXygFz%LOwfYb+RV%Z1q}`1hizy;KAe$JMaAS~RTbuB zmoL}WuF>;Xot=e*G&M_0@82IE2a`far=(UCs2?K#F)9kH<)kDL5mZ@MR$jjbS(=&I%If-c%r=yjj~<1Z>Hhu3MlG$ACo#7n zDBx$#=<2q$LGk3`A|#}*kGdj~VmUdrwA$LD+{pFJw4gj`1#}GmzF>c zN0kGt>+Nl59r*b{Lq@egNJvmHG4a{6*RRvl&z$+@8@O0szb-Eq75(;GOUsFgr%%hu z#KgY&#>{MX_LpCJdzF><@7K~A9Q^QMcvx2#daR+LUw^%MQ(ynfFC`^gTYvmChqM&%JmN9)6g)MS6N`Ykt0igNllYNkT$%b9uR+pPrtnDX0Uj zt!Zf<9uD-J0;qMXt7GU61YLSMJp2q$2v=A8`r_9g6x7&w?OI`>gM+%dwRL7DMlR$E z*xEt?Q(YYtWME)!9v9cr(%2XoX=!O|3q53QErnuaWMPq%)ZN|Q9vZ5prKySQsJAyU zQD0w8&Cjo;1@r(D6GcTAmztW9k#agIL0K7T9)p9RJYT+SZJn0Z*;!W?5<;XH)YVm1 zhK6G95*k`uj3i4>PcJVxbJEk1wgty)NlA8gNQkQ|MoNr_zP=6){{C56RaIG8zP?Bz zNl&k?hW`QAc}hw{19D7|Sri*vQ-f3&dwUxjA`vq!4ar8%&Pcq+(L6lh`9+$2T3Tde zK!C3=R(cri;C%=PKxKu{yt}!9y#k+4R1}eV3zuDFq@$y~J&jgD-@?Pq%`GkC<2yUy zc@Uy+G}@y_dwZj!YHI)SA0i^n&3k)q-cTq94jeiZ9{&0@QbtamM9Rq0(yzZZHuCc` zF)1nz5C8JZz<|6w6O*Lm)vJ4ZOH0PafBT!T@Rcijd#_)+yMOxWi4#RddwYNU5gGXz zJsI84@BRC|y`rKcN0^ul4DR0j*T3rPxwx2^R8%G={`g~fSV`%#&lD7{UHk2~g#}AX z%;VeJ-@l)mv#~jH4TfBT&d4qUZ`g}69bSx`x4>05ZXfB-LVP|*DR{rkzus1sUQp^8^rEGKvBl%?h9=&M)A z!bV+}zeSF0P>ge?cNy zT7qhUJGGY=QW3MW^Yh_V^YFmj8mUyl!Kg~a#DH&s+8Wjf=x|0y6ManZFF>n=%K*Ay zAD_g;(o!U)f~64?Q%TP+f=krNDLlNmxT-2C2|mw&fSerUUwL~Y{RH(cq&K>`!3&a? zmyiHAJCQ>d9qmouV(D7t;^L?%7Z+#e$jDN<^FBJ-!2w-DtbwAVF-HjxFDt9APDntd zC5)bqf=(iy9}$7|f{Tl>F`3-ZFf-HE=HS4~DfRqGlYc4MI^WMIlp0>9~-c3mf+|h1s$B*;yq@+TNnLToJ5$bo^! z=vz?G`ugL?X=!|X?CezP?(UC2mX{-s(A|A=b8oM=S59ugw08@7%#%QA8v?ou~weh#<3hdwXStMmu+ohX;uk>+7khA|j_wkw}As zpch`e$i`-B+SRqThCBln7Ik%~Nmo{agW1@={T6(NnVI%>D%I8&PNA~0wbRaJd`P+EdugQU^UP8v;LUspFMsH&==AtuJs(%3jSxT2!2 zE;iPNPOu3NucN0;MMs;N;reZB>*)c_2I*OCZN0q-2|7CR@=&7B&6SrM8VU=Wm{eBI z&SEtO4`fNn{Csb(ySu2Uq9UrI*RLb_TUi6x6& z%LApu!=tTjd^|rNJTYWL&(7A=SXyG14rOa+r?z?`HPK31&@(moR$VJ60Wh} zV5Al#CzD7920lJ@b)%!GW~i%MSrrtFjdgVR`6(;w>u1qPv;zY{K~hrk@=;MY=){BY zaCB$`=O4xw*LL=f}#*#T66t-~oIgXU?2HO{K1`Zf_?faB-bFMWH-;^y81k z#oXLqe(B(V-}lw47cMX{UA)-e|MSnIqw4CPeRknOTiXvmOidXWeEzwRP<{Qs{0t=Id+VdH8By|?D+U&$G-g1({pnZif>-tufDRjURZeZrmT#cn~6zF z>*mehe;*!JQTg;!Y3c6nKmNFXA8S)->H7K~e_UO4bp^Wwcfx=DD=F#AFLCc28v5Y} ztn!3~_4P|j$Hyxxt*tN8Gr60a;SmHS%g6})*zRs;XM$8YH%G8#l$654`ujnTGB7}@ zT1`z~UtS)Gq@&~NihB?|qJ+z{y&XvuaCt^YSJAiFSYqreC@3sMmZ!6GXejbA6B5W| z_~XG@!y3rPCoBvO4{(RcWQ@7+R3k|j{56c6*!TC($f&By&qpqll@;bJO-oB_v3%hcquMcboMMWQ<=H{zclanu9l9O|HZ)zGH&CfSB zmXkx@0IMTMMY~kltjWkJVvpxa`aq! zR1e@WGBA*n^YrZO1TzfI7eBwNR~HsaN-kd(5;8TdsDO9b*jQB5*ciVn=;D#pT~Tr8 z&b4dsKw-pylRGF#K|xnHFK==2#*MHrS=oyhqoT&>TV$lXyp$B!p8fsd;W9Gf;=aB_ ztx8=TOq_uMa6(K>v6BH6sRn5u4(Y(FS zof8v_ikhFleLFQ(Sy@IVBxG!CVF4NAGBTjjE-n@npbn06otmnvL*MH1<*cl0*D5RR z?d9am%;5HhTMp~p^mMr8&;c_ugNL!Fhep%VQdg%?Iy>Pt1i1|Tg68J8zzCGt<)(-M*?SV$P_qPa>tJHaAyQQ7AZGP*4#)bu22%#>T|N z*B2c^@N%rJQBSR*XQM?$d3oV?Po&hLM-&?iasY+m=7xC`YQUbJs;Y8wHa6MWSP!M7 zxVqZd;NQS5B7D}URN!t96N7wdD6w#j#>QsRH)PrS`-5Xn&_}6M+!??wV~|P->NzT` znVBgm$W%kO3Di$;$&hM~K50k@s3qV%qRWd;C|)0RV_!N0G${!lF$x6?P(t-Zut&jh zC@D!!CX=21Z$eu}1}Y`qbb3-z5j0@V&feajo1#~W`cr0RMFlFucurax#;Bkm8yhDl z*SoSo(6jg9m3Cnk_}uBLYBQdCrHE1Vmus^;b( zVD$BQc$_;YDjFV+Y6i%^P}mF)KX?GXnW(6Z4YJEXGmw$7vjdH%wbj~MNa*tA($e|) zjtHHOG=uWHa2c-3=f0B zt*Tm5vbHup?(Tl}EWDsAE7Q|tGCMmG~AnR)mS zy%W@&$HsPd>*^F0j~><0X=?+SSxbwV8I_kOPX-5}&5)O`tbFohVZqN&NC*V;wY7-} zP=_vFEGpXB!5#nfX&mkD-P^Yb&rv`ClwRm_K`k*e13#L&IuDPtGkVmhGxPG2NY}3u zXxQGNbWo3de4^h$`0Ztlg4f`Wd23k%PmH#VxPv$7f( zbarlTc6J&Wad2p8R9COAqSDUEsi?@{)(Z$gbGf`cJslJzEG#XZntJ~}bUuWua$#X* zB_)O6(hLmTzK!pJo!!n3?ER`LB$1h#VvRO92u&Y!Sg&49O*uK8IfIVolP8Od$YK)^ zh>b=4J|qNcwb0PHIb??M^K)_{i*0>9KVMN%SlHixdU|7{xL8SvpPx)#SioErN>0@2 zkppO8z`~-ciVgw#!B9pa>uhGm+nbB)qi0$|LPiGE>BU8S_97x-VRLg!OG!!R&-3uO zx#8}bn27GCt1DQ11qCW9;Mk3f5Xw{`A&?!mwz|43Ed>QlOj=tvHt@TG?;FlJT)Diw z($a~E)6<}ti;Bw1#>K%o2fmbwN?hCoolX%OtEQ%|o|rg8-{Rv@;rI2ucJ0QEq$FkK z3m1rcd|H~KBI?Bb{VOXV9}5XtS~fS|xr6nwtgMYqef|7=ON+g|q$Fk+)6;EjAf9V$ zgE3oELn28?XlsKO4%Zzh^qHA6Gozz4nx>|qAsp(XqoJV+3hL@9DX7`R#K_aL!$AQW zACHNVl@$~7^FzlpJspfs2Z!F?yLW49OiV6ZfXDCl?YcTkORRp7?OIo7ZZ07KlJm?A zNV2@V$caN%qN?gQ-zY1i9(Dh|r6ua8>FIy`@!M~ysYj0N+vn%^-FNV%9X-m#q@%O5 zv$wafASA@Zbl^Z|Cmd^JG827sa{A?$y}hn3R#qk^KEA%by}gGIy}X#21?etU=mt_! zl$7M<;e?x*fM4*^B?_gf38ZAKyu!nKdq+l+li{rg1GcNHrw4ObS62!JN(ZpNL99Ym z1I(?E5HLtT_7cGK0l5NQ+ngNmfCx%gYAUi!{r#c*z&?!@@FxQ!g}xy@BruRhOHNKn zAyiZ0;m}opDoXS_38fMg;^^iQOi^@4P(LJk6<`^mKN=iN@RvagBb3^NqKcv52JZ+u z5W?j}D6t41746Tg6o#T4Iw*z{480ia6MA$)L-J8OPB^|84mmuISNdQ1&Y=5>rf-A` zi#RgjH6!$3#NRR=$C2^;n7?@N{(1z$Geev=vCsHx=-}btB3x?>@7Q0TLnzNaK8H9v z#u+gl$5}8w8HSsUcn5?BkFk#<|L^zzdPe{K*%3!3`u6|6PdJ8%#~Eityffkm#93e; z&mo*dINHa@|9AWUm6L|?N(?6jK3!s;czwdZ$Jqb)FT}IpMqoUjI3vdE{J(pga5E6+ zfPL)Ybw2L@`+wn4ygsqvxWs=Ed;k8wAD@A}|NNSN9_By&5Aj!wXER)udkdfBnxV@PEQ5@bOysKjGc}|KUA;90mWov;EKC9b=@#zYzPxnETgz z=g0X7F)MFFLGT3VMcgJ*pIeseR{Hs{aB#m&(- z(2zi`85_e4*wj=^%-;U$)klx|`|a$&No;J~-JO_lbAxIW$!|!b=H?a_j*Y#2duApu z5X{Yph~?$AwZueWVICfM5*|K8Hu<@8!NK?LJ$X`CC@#*-Y;Qj`_4H{~m9+GcBSuC8 z1K=%TO|Pei9PFN+OP7uvQ&VeifAwl&0%-`Us%L8M~jMbbCb#7Qq|Oe#OUEMG=%!1nHjQN&_lX%1&Iz&jVvq>OePhTjEw1N zB8fsqCNc5WE%fieh6)d#oxOWED@$2fLIT>A_4VRnMMX|dkW<&!Yih6(h4SqFebBC; z|7vdD*%=(PqC0{V6e=qpKc1f_li@TgEZo_-eLFA^PBT;lR#w94`Rb=n2LvE59uzEA zR!`5#NvukRgwWHNo__J7xmjDAm)F5zZ0y;yjt+f&Zf;9@cKowvSFa-d9G?t4RJOJp z9P;vog{!NhqfSoj?C9S;cyRkRmCDB_B!t{NtTrSi(Rp56B=X|n-5DDLy-PuXg~iaY zvvYN|u1-sfogKQ=hY!2Ejf|k`ZESq{6y7|-%l7c$>@1YLgkB!p7A`Js?vN1Fjc7DJ zzB6aMy(cDa-%dzCg#*4R_zShP&Yg2~#Cz!MG&JPpMaTU4bLgO1Syfc3tGBkGg5}_l zlS6La_3Nmi;60#2NTniK1NywRHE0iog+oK{+}YU3%7Xt0WEiZhq24hz?&<=~R!t4n z0eB=kJN5NXozl=~ZhrP`auR+#Rn>}$ot>E(G8tO#yu7U~RB5=lczL0&zI6+VT{sDD z-n@M~E>1uI?_qNC(W9Cg4Gmsi)P$ZsZEx4t=cQ-=4GqDMXKc*IrlwX~yR|hmWM}vB zdvJ6-efs?Q>}pC`O*lanqk@Z+JY=(zlydWHZ8XEQW@JQmfbn#+wF|v&u9FVo1le4*b z>sCMj7Z<7>3?CS(G*G;!r=LI1#pUfiGXp=Kq$KekmY2)RRaDs6@E*3d+S+t=Sy|!7 zd+`F-@wszqYL%7v9#HR*m(S1N+FD!;4(8_O<3qg%u2McePEH@6iHXIQamB;JFQ)7aSK$E~e;dQk8W4MBqrSB0i#Lj!6kR#vA^D=LL?~fj5tb2R_dS4>B^~!nCpJ?}x8VQj&$m%4%q6d%Lz) zRrS;nwvRysL>GY*&R)D#Hk0s=8Hckay21_quz%f%HM zdiO3qu?rXY_>hqaRk)%eFRzCO>W)ykv$7f*UbzCgmY&|}(>gkhjSn7lcjG!yQmU%j z-Nls%x>i=!=H}8;SeT%om{@%LDt$u+1{ar~-`pJBz30!fuvl95_1(KySSTmQ!eV05 z*N0JETl?fm9i1yzUca84baLY4gbNdT17BYr9%<>kJh&DKUnEjGR_O_9=gwhVf@&{4 zT}bHoaXULG(i$4n)J~tavKkqA@uIsM`dj3AzI!(|W@dK$xU6g?o!NyWAj2~|`@w_R zSu**|89~8>gq4*$cf!N@`H{;8uX9QY{HesfA~*N^`O~M}-RI^WKQ1qqlst3@+KBb_ z+FBJAX68$mQ1itn!_Ka&+ujb}v!mm=bK2Ta>Q7CMO4Vf#=x8cANA(V(NVaVMMbI9>FK4V zv@}uC0|&q@o}7fP5}MVFjN01lZ1f7jv~6g}%_Wo3K}tw~A}1=!+uO^FN(G;tsG^{D zg31dW&`wW!4W!dR;S~Z)6rC1=J_-dpyk6+Bf@6v#2x6Z?0n;=o36H}=77ze3J2d%%w6bF z8QvfwyN&P?5js`87aWJ64541Z;0K|~f@4B=iz*Jt0|ae=K_tL@i0~1hc0f4i2p1iL zi-7J^NJvmnObk99WZI&`2gN{cuCnsUllYy$Zvp1pxpP!1d|g>t^74FqZf=A>Q(qrm z&8t@z7t70yjHIQJYBD!hTzrY1uw-FTS=ry8k#Xsgl$5)BN5}YhS{l+ey}ZCyhq@J= z*6C^7HQyDBzx_>0>dKWr{#aRoD)ZZKV`5&=w}gb#rw<(>Tto1d zpFL}DKQy$tiTN4omR((-Y1-IGN?KZiB41ZWBFV~{nH3fe3{+H*ND2zp)}^IGL*?bx z)-p2I)=>PnwmLXSN`n2rzCJwc;&SdBT(Y~nOG|-)Ec8?WcX!NYa&j(QIC&E6&bxP! z5y{16V$$9YKd_sdl$5bCI=%w~{{9LI`ufPY>FWy$g2yB&X=J3Yk3vyUP*eo*Y;Z6# zQceyegucGJcWY~rZl$ABRrT=UojYM+XU^=~ckv=rx?p9Sn`>#gx@KlpSLfuw+X-)4 zMg~&+z~aJhn?{58jc}t8>3{L@nVD&6;6QqLL06EK6(1i+C+-FX#l<0k1hZWV1)WFK z*-eSInw`ag6O z;Rq@$?CdNq_VPlW9MWPUBaMu7bg0y!AuwK@o$;GNuITma!SwVtP0jT5+1VR6XtawL zL9(2mU!*6Xw(Xg@to`s+w7Z>i}ckU!7BWKOcZG8N}17tRHaM;=o4?lU*)n#JB%WG`h z-j4J(b8{{(ZEZNmkU)&Nepc44TmAh|mxGD9vNAgx94sIJO6C3g8yh(}ii)WAkBlrV zz#k^IxTmM6sE$r~`ReNUxUa9cxQa@4 zHj)%56mjtj7og)B8iLbIN-8L*hrWS2DlJX8N;5J*0E~=825e4_uC9VYaPY_o)YS$C zl9JGZj*aE#8yHAQIXX5rf#v;YS2PJUDn}W^)t1Lq0xx`~LpT&6XC- z3Xs5z%o%HIIP)_zr>45PoScv-otTL34YD!R)M8_A+?bk5OVia=SC5Do9-f}g%{4Gk zQ1J9@ZXOxQ&o?($RD|EHzrV21#s>ZJva;df+FGn-?d*_$+1w1utFduL#*G_eV@XNs z>To;7#kID!v}9#bsi5WHUIc|RoEe#!K|#bdR$QE)9}(f|>fsR+Q(RnB6cy#_iZr;~ z+>#P7Y#r&jNQ9~&A_B<`puZ#G1Fr~zJlt(Wei~e{etyuj!uy4M6@Pykjp(;PQ2~-R zJZ)sUP6}?1f&#cxF@7*cI*eg>{lr9K^^Tm^gaqOa2yF$?KR`u*(IbKD3{F|}b0Q*$ znF7>z$do`&1}?nt@We!d`2%k*a#Z2v^`~!)#3qL87DtYW!TlCTBhu!Gz67HNfjckm zy^N|vcz93{;ch1=;SLT41~xXav2AV0RDxdF$tgLxmA)k<+1n%8v!tY@B{$c@!`K+w zb@-XdWFsRGh^wm$3cS5R4UdVzUx3zaZ5_6lKUb{9ll#&8BwTDMb%Z(dF zMaT*^H7zfnnrd!_4hjifa98>HA-5qV*T!_N;!&AodS75e(;&%3(z^eio*^9U^$v2SWBDr#q*SNHPe z+#Ir#czMC(1~uoz325_ISK+kh<>lrM4_{jYy9RykkdW2Y*RLxoq@`I|ot+mJ-o0yW z1>5)X<^KNHulxEmHNXCv;rF+&*uS5bH#__J^No$Lu(M~mxuc>sHt7EyfsUoTd~U9*%hOX%&d8{^cwqslGZ!yv(-U!V=ZuJu zl9G@J37MJM-ma-pQQ5zrhlfJBetls9)M;yL+|NPb$J`h?DR|(VouQc`lcAu;5@K zy@~MWqY_BW?=d5aih{c=E{<@!VMM^pAN^ZmeniZzQBfp#U{GqIUrVKLP+?&`5*o_L zhl-EKJS#Mm;rb7tZx~sKBf!ItSsnfhtAvmcW2;j6DSHHU})`Om2-Q`4hIm6fkt`RS*{MLRpf zt@8f;{rm3j-+p`MOls=0XPcW83N!PmQ{mxTTRS^3F>Gu{kNWv-ZoYqCRwgWb_%MmI zxcI{lot>(x%*=! zt*ChQYGnmv15wfJ?59sRHzOl?cyRqKFR!nori+TqjT^JGVPRZctgODiOG{6l6cmVw zf$lLg^XgS|v#Kf^n~e?F8-s&TwGi5@Av&c83aRpP%HGxflYPYH?W?KUTc6MxR(AE$fHgM1r6PK5lmg3{( zv*y?GONBLM+>`_|SQH?p$S)dd8stuagN z=s>QxrDa70vj310VQC3nes8b8znmNt{zF3}Bgh~^x@UX)j$K%T-haXN0%6y*-(n^k>RCGC`2)k&^@7KD5$A-c@!sQX`3! zwbD{JXx!bY)a-0HbqQxgax(m6@XdfY14mCx44Izf7#2nlm%Y8=SM6 zu8OxejRukqs6cS-W1bTqPoZF*hVc&bC?w!UM4(Cn-ZN%B@YXUCTaX-{n+rz*K^=ge z26-yp-dF`vj<$GjgSy^$hudkY#iV8CKR#%aEar`*$kaKg$bc4>% z$7gbKeI48dRPTp}p@l=*l&NWF=fj8KD{*qdRl2it;|7Rf;^N?N+`Q@Nc=+(yvx$kj zyK8H{zF*O|;NVxUe*e9+^vs#R|J}*y*)ve##l)DHG&RATxpz-lnThH2>Gt-$y=TvC zZJC(%@6XKK+k5{$DCm<<{`R-9u%Cb4+sn%O_FE<bS0B-a`n>*_{Dp~KeDfQ+`wmpePZ`)+X&zAjPG z{QS3XU%W_6A`fh@b!Kug%S;rrx}1Xt;1;-##rZP$GJIE?)ffQyH0_ zo^51`d{+vwy?6a@Gj*9yJ`}gmoqrdvgr=-fBZ2zdhsF?lbBdf&)(kc z+m|jeF|n`|6@B=yyli9h>8C6#+1VdH?CkjX96ZR*o|*aK!;2RY5vNY^@Z{(J>tFBR z(`W|{96T5si{E;3^1gkzg5JE@+bb_WZ~#Yp_;7D;c=+sDTq#pidwW}3Iyy{DXU-7L zRYyn0RrBG4pC1#`-~S#F@%{Hd{16v+@Zjg4dwK2beE)q$#>tbPedg$hS88h$75)5k zJ-yjkxL7qbzWGK=Yh>j2->0Yb^ayX!@4xTvdU}5H2@{ir1(=FguAtgyYm4*~ct%Z4 zk=#P)&P`2ewB}~$&aJFWOsLfAYUHrl+M1hFC>0g8wQ+GI64Kqu%Brg1nzpqK3o9vs zL%`9|$q7DUB&OTgfTv$a-;fMrZjMAY5M#{E@%pW;$Tu@F(bf(KXl@=H%+I&BR#kO! zs;B_P1gsNlYa|{uHgukp+*~IoB*W$95ndV# z3l9$>%h%uE!GZAafv4c;7!-s%5xk%F^o&o!L+0pcX6El-R@T;*osHM=@POMDP7Q5s zJGj zynLC+?vRvZVv>-6?(^pY7X6p}cza{(W2= zTwV6|k00;t)z`DLGcjpsV2oc_P*Y=Kl9C!8hJw`A_VdpPLV zp3&)Rp`j;E96t`S(Zh$Jl^s6p>bkJ-=n;By=*kkAvx6P+-gw+uN0u zzxhU0wX5sx+tE=&!vhDvRDyRIDeD3Pd3i5htgRsv1+2H%uZfEF@#Fsf8yoN5m6Y)F z@7t%RH#qqCaecj<+}B^j^|`Zyp4^EO3JSHg&!407Ei4Qd^v1^GB4{U29o)OOww940 zBLf;5D(%I^pzPY&H8o94fjgOd71Oy;i4lA0F5LB?^=^N1}0lg3V zsHY$Y8}%V1AAOvEAUzJNoUkzDV}tKUq}8F9larI292|^XI3!}Hrh-~RCKJ9KDiw1o z8Z9R$FAq7Ks9F=;B@m;*D5BAjxCAm_Nl8M2C*2+7PiF{JR%T{`NrG#uuCBBcu5U-j zsHkFkP98E8Jv`#$OG{A&^Pq27(**@NIKb%wo&v6LB_-t5&(8Mt1_UT8>*-}>&CX6v zCMD_UXlh19L4}=?a_N$)YFOCN(9N4*B?}8XJ9lrf1KFhmrXsEnQdl`t|%g ziFD+MuyAql+qWAV!NI3b3kamAKYhBr9TCC7!O0mL`}pzGrwIvcYzGgzxGXL{dX$yL z!}HBIU|zm{T~i|=@zqzlx+5bXaTpu3v+L>g_P%*DK5l0R9!OKut$u=BpDf@Qc+a}VyLMpKE3vKR8{ba?e2oiEFywBD;Ovd5pr^hilA^_zaAB(q@Y&+q1jZWG)=NGq+ay>q9(AK$Kl0d)BN{ZUbuF6rom zgw)qJH^;;f8DZt+xI6BIgv=XszBaBH2_&*$dJaYB{8vtP6B1P_u=X$dL(XcNDd=b zHEwR0{lE+A?v8zUyt1+|>p>cIVIf8Y^pUc%Dk}2wLPMSCTWDxbPC)^#Xh%n60%N8Y z_-F2PMh5b9$z=GzYHGkSKoXm#W@6&__`*VUwWFi5G8iPStxZh{3C_+W62^_3oTw-$ z>xtPQ)-FCi2?-@7g@qV(P*pCWZ24Kq67v~DCl0HwnLEHxlCTyF4=0m-eNn|uO-029Ybg-u z;Rpcj6iV-~FpO@fL4iKx<3pu>?4O}tPV|98LWnvv)@N|)#l)ng5mhUETNt}gZ${6~ z*Voa})fFDP!NHc6hzMh21B0NT+FF8>W^C-{mXp)mTv+Jo3I8lSk|ia+zIuA_OSZJs z*HftmbTUR%R7VG#^)@!9rV$aXt=PA+GB5}YZEx@IPfOF&laXx^CRa$k5Ob7k6<1=QBT_U}?3q+_{5Ztb&4*Q(N1uTlMu87RU$g>|9&x=m0HI zRkgGfd4ixNg2J%5dGjVl2SLHi%qLH_x1r(TbAEhF>ox4ODrT@M~qR!U2M^No&9fB$#i4G!w*9Y3zEJuvXEe=RInSsghd zC)d&O#~+x5eDxJOdq&3Fw~roCsmG2TKTf4?Y;12wN3*e=I1w1QzW(f4S{et({{4=Q z_wFGzOjVVG!@&U*Y0Mo?owBi+m_W_v;zf+J7`@xt)YJ|gQdhrn<>gE89}XOlm2GZ* z|Ni!E7nf6~q@++`x=ZI-oIWifQB?Hm)q@9tfyi%+h*+m@G#Up73yY5rI%!c+XU-fw z>go!PW^u8sEL2i+b5EXBS1T*?@OXIKy7l&LYpbg2$&*kJK7Zcbt*d+Tq_%czE0k-O zFCRUspio!$bp@uFcm65T!y0Ac@2nk6^rO`7emzN_Wq5AUnzE01v zjfoK#=i|d#^!9D&&3Jf_y0^PqTdSqT$LHucI7qB}pkljvb!Vrm3*=fbHCI;f9fHW7 zn~Scwv-8=r7cQix-o4AhHIfw{S?EF~pAJ_m>X{;jRfPHSs1F=T$Ot@ZUeI*N!~x>Qzn_wK-e zo135@?i-7XAnQV-2w&U60+kBhGYC%G+aTY8T7K)+hYwv{8X6}~7#Ur^{>v{D6NZN0 zd?P7YU;pmi;v$K(fB)IDnVDcy2LvEDE+*#X%h#_H5>A}>;tO~8hYx@My|GbT{NOVIvg95kYHkxk(r<0+go2p%9w}<()Aua($hmuUt8PW-iHq)5);!GUlbNH_L-Pa zQ`y_2(U|C)j}P|i>#=WSg#C#L(1;`?c6Z?yJ9mzW>By0RfxW#455P4#d>F2<7ccbn zQJtx;-`o4`H+Od&fyg%t563G-M&gz7^FO0Aj;yU;z1rJrYy>SyO%3PT)g>dwyET4*RcJ1c8Iz^z+%@6u>z z&YU>m;emXI++5Up!DK~JBjzBMmQV;^y=r44AfTsLQ?s%%I7lW-Noi@Nqznxq$xc(# z$S5J9y}hc++gn4!&@eU@E_e`z?Cg@0+uFe5#5yxRzO}WpGb_u*1uQOKU!)nMGlKcH zlaq~2KtN$(TU$v9nSA-Oxp_iDM@L&*bTs&$R4N?SNT@R~2o7#&=;{Jb1x)MOT4ej0 zn!>qXS2sLdQetbXq=Xgo;9ymiv$LwIjSa}DO--Jjii&!ADJcU3U0q>ex^%yNLc-On zy}eOUIyxYIH8g;a;p_|+Q%+7*RcflYH)c3_c|>i<%`GxAKR-7YwN5XuM0zH9US41z zIL&c!6&1zBp`rHn9v*3F_4TEtK|vN4j*h9REiK5yHa51lPEH0x$=_c~%fKKZ0rb|0 z2sJe&B?_g#|K`oiOk@FL4t@J}VQeU0nqQ&dxeI4i0&FJw0`GK|%WZCMIEF6&010(a|K5 zwRLcCVPSPOobe70-rflbg@t)};2iq;QmN=Nfej6n4r4w6$38gFSaGDJL`P#Dg}P~W zc4{hSEWyE;y`-lTwc4mC%<@- zGBX1L7%R5CJoM^aT^U3vJPWUdOzXfv7Z=Rkii*H2^YZfa#Oe*y5zr+GmK2ET-rl~x zpvi-P?dwY>6TI!XID*DbXxJhm&%o>M`tywR z9frdZI=KJwK>c?vi~sre|M>*|=M(s!PvHOg2@rZF;?aM1V*Ri7KmPrH{g=d}P)aeL z`Pcpb`q6)XHl9Ho{XdWEAJ6LJGZ@b%UisrQ7)Sd5^8a`>;{5;peKGd&%K!cG7|+Ii zilN-bKGbClmpq{~WBA*M*CdWj{1v?XjFgB_dXpgzHIcEp2!A!GlP8P8K(JM8w+K?rv7r`Sb9sEiFBIR8Ro728lE__V8gvg`C{c zqn9stbgZt{)~czou;}Ssx$@{yPmiG?oJ=h(aL_nAbN=bvKwTZl4RD-nZLO>z_luVo z*$~syG@7U=Kfk~K*x2kWbPT+_o}MEkOG^a>U}Xgd-@Li9Qc$3#CMM?Z55GlSow_;) zhq-xg@8)I`J!u=O>9#h^?F|hNAC{CXFUQfyWDX9{^Bz82S%EH0SQshnJ3EPqyu2(d zUS5lfySur$f`SJR!Vj{!Sz3DG!oh>+4ZMEc+pDE@_^_HTu8Fp*tmBO zN;(ONgoKX{8eiX;nblQ%>nBf|n|F5;tRoH%xM?;vnwrq@g!AXg6L{_T`E_*a>vwl2 zCqWrfP$($a++0`)3PSD|jW$0&J4>aWJIBo(7>HjV9A!|lz>QT^rLKPNoHN~nx4TPF zhLDi+>Q!H#o*px^iV7TMM6wu^^Xuzi$Fs44EQI_K_y~oC6BF?%L0cjs0(Tjx^&%pu zWX#NLY~ z_M@Yy#^~v>u)r0E6cA(qd~{>sJ>=zWZzFeymlvuDr0yZtPfQF>ML2?x=7aZu{H*im zj~_=D=-$1eB3aoJ^n|yb9ypnFbWWW@HWtCT=ivc)AM0DVM4@PW{1}8D4i3U|1b5k) zGn|~Di$a@@)IA>`RP>?K=Hl}3K-ybfo$8;dd+1bmbf~K%vk*Bj_$?7>Z`*X*w;A2x zBrRQD4&RiQ7gF~!GIn-WS7|i3v67NDHy=ERi-Rf?*Z0;|dO9e<*4FSD<>ZKo9XVoZ zIxw)aQ(v#5g8J3q;CJ7Rj96G8!=S6{&6~+dc-9mYs;ghTxOK~eo>YWB!TLJtR~#HX zJT%(U5?mErTxZTuDEIF_ew>pdE{+V9J9l2as;-uoXJsXkrs%0N?d__nhYxFNB59_p zOIP>!aVWudcLxScOpYCsl|_$lVZp`a#0epx++64+!|BwJI-@^L^_o#rtfdj~G!Y3vxd+3mn(fIg}KaPwT8nUo}zxm^jlam$}$B!#2f{uCT z4pc4t{Mp%$9^Jm};c@I3H+ORK?(Y5j{{BagGBbyV@9w^Mk)Dnb1W7ZfY>JB`L2Y^Y z?c0h9Vd1a7Lbc<~o0b+?S?J|Z*}Qg5OKbmr1%=Me_wT2tEi5ob)z*S{?&b#CXHn6M z7t70JGBYy|PipFuCtF+L;ipa=Kknyu_wMFqV&d7ehYlef?a7nU((~s*ah{s`@Zs7u zq&Of&5OiJ(3oyPwah{%bbUbxRTpV7=`FTG-q~*Zpu(%i!a`r5~hvntn-MqXD7dSZF z-DhU-$*8HFJ!@-=>!GWQxE?^M>g|R0M@6Nm2wYA^%@@8L3gz56A)&~~+qYL%z}lCQ zp;Dpk$NK-`#lXPs?!m#hI89AuW$+0*IsyWurNLVqrPEsd{0Qyb1NxSjC@Koa?DqD{ zm$|tD0>_Vgd2Md~{(EbyxcKLvYio~>|ME+JzkGQ%9>BbNrR zg82@Ph`ATBM|jh4KEx`YaM{PkLitLld6JSqEDQ;Ok1jDWA|fyl8Z;#Jp^HOG6*^mt-ju5=`c&X&dw4iHhJ@tj*VJTZ`}$g0IXT79dEI4Y z_!lQ9WaQ=MCM5X#ySoPmBPS;>4}5%WZBNhY>gnn0*VEFBjg{!6Q255WxQwqoa53vD$>^%7Phv&as~b# zD=Vu(6+CbF|1Iz%QveE93H zH8ny)Uw>_FjoL1L$N%_;n%cksu3d(|4%K4=gU>$W=dZ5*@yF$5d;2fGID0lf|HmJn zKBZDmoZ#R{P5th>SFd7XPM$n+BqZd?lOKL4Eac@qaDYUbn?vTVu<-u z_$5Fyjh-AFl3rduKJoGBx`&36=-lb}`1*Q6Wn*d@8d_W1+?N9 zWu>L%;Zaf1*9SceuE?ycmX@L-gk!2Loh_V&rib#)~rsB@A? z(a{wZ<>fIk?(P^Fva{ingIfh@PtZO1`5~P+lI5e~W+uFJT%B7P%Pf!m(b&88CDe3uhsM$`RK782A zYh?xcZ7!}azqGQNpa1T=>S`gOPe0YrKsM;mkgo2rW9Yhl_%J>WcLVe^KmIsBZ)0=# zu()`6`OBBLZ@aplI020TJj(9w=3NApxIQaxxbe3rleD&dyIiRaW8-Ze%nz_WE^O+r^7td?6QhtgrX> znwujzxuxaRtJzr>7bHy9)qVJ|xfvA1%F4r&l=S%V?rv=C*|V4N zOMms1x_W2l)>eJJg2KUr+SU)MKT>PZ+`x} zcRM@5!N-oBJQ)%3@Zsaf@$nedTwQP7dh{qaS4aqHbRbMLG^nd{a*{~n<1bzySL*Cp zIA~tH7#J`%W@eU`FE1z7nI}&Q3uk6Ne!R7XyEYS(ygV2VQ&W+VmoA~xP4KWiJwfHp z&PGif_3^T@qN31H8yj0&8m+3Tt}Z^_-rmwOB&4DOUO6{6Q&V^MjEtI^f`Z^+2M18_ z8H8LyH=dmhzOJWdOiV!mbW$E3US80!f|yMrAtRxrq_#FA!^g+Y4ixVadY(yeu%n}= zXJTS`c}YoRq%(a((rZOUe7v)>m6e}gULLaBkmTgzf@kID2M62QIy)yM5QJT8Yh=S# zRT0WXOG^p`<6>Hxqoaw5k55rib8}7(k}^Cza&w!S^7G+=b$7>@l%Mb6VQA>=oRibu zURFjX>+9Rvrlz*FRaFHBg36qp-qBHA?dPYfYhjU^+S`l7Bn^#Amk8owT%4hyt}cbr z($d{c=m!D=D=MIxb#iiWh>k8Q!dR=UjNbmn1{6NWkDoqGq1?T@v5}T0CA+Oe`no(IYrO;4uja0dXZV zl8v6Q;Nmhrzq^~CFDiQC1V)PY?{PE`CT`yR>8FtqjG-X*ym>PHAYdpfBgt%P3Zz;|NoHnK)4sm7wTcP_1rSof zKxu8&&^U5LO$|EZsVQ{y(an7O7V~>nR$<|Sf_LwpKBdu4o;-FeFmQA8)vN4mZtkzY z#`!&YQc@x;{N#xnt zAb~0<3=RGBpToln3Qz|(Hvatc!h*5!r=RlhR8{@{`;#ZWz6TDlv8AVf`0(9#2?^}% zEG%JRJ3GkD<>iGp|JE(6CL|=-*HCRSJRTf%2QN6%HStdx{Ia>U#mv+^rfG&N71 z!VDeX!{y6|4k7R6`|s!G?Cg#m6BR8eAkuDGS&>PF)V<(fc6K(lz`#3qwzmrkl$1n7 ze0`B32fd|)1orRWFDuj3ZTq7Z~AUW%SFSb;r>I=-ylSUkIgASQz0=L$3|~Gw8?A zkwf+o!xaZ79p?I22@u^pa4Z4>Xtb1+v^3}-KhicrLVSGC+eCT*X7}*kCM3ki!(j|% z7S4f4y~GNHSVv%uL69nla|sLc^+lc}lC0z7k(Uq}in%;Pe+6AAW;xJPB_|`T2~Odd z806u&yI;CwYMPpgobT#t3ybsTjg3o6!1N9dR#w*0NlL<+Fd_ouSW3z?eM?ACP{6l4 zG<54$em*=$c6R6|S5@iia&sFRHa9Fq^Qqp2xBKYX>wk;eR4RW&zvVPRkZ$07tJ$d#53 z3!9l)S;@~=R)&%V^{m=j{3;C$s;ZDwXlW@TVrW=XbN4RT1gOJhW!=3yKOYyTtgNII z9gXu&PuI~=RP^`n>KYr%%_TGe<>f6cv9Wr3N=mM-Wo5*^iVB(B(lRnqP+)1Pu1+Sm zx6jPf);c<>tGm0mw$9HtHj+pn7nhaI%yf3Txk*Z1x|Ej(GP}FGprEvLLc+qrEIn0G zQc_F|RaOvqw6&$Ae0_(9QA^a&5D)-|?B2b)I;7g^>Xw#HPQn{3E^cB{RW&!))CBjj zzCPIELqkY<(b1`_+}xa;B-5!?($YkRF34M)oFO3#3wQ6v#PIN7bhvS2eVwS7nVSy{ zJ$~HL0ooR7x}dm`NFpL&aibQ!fF0Rd&@tSqp&$>a+cG&HiZrl&!g z7ZX!c%gMQYdu%KqKtck%?z{BlTY>@(whj_*1qJ!|P#HvZQ%LB{88V%3y}MgfBq4G1 zsJZ#|>#ts+@`PF)R(^0Cv$Mm4`10lAA_z$$B1uX2?#;~w1)+}O>wEL&;$lJqxZIA8 z@Y=y+LgdvW<2N9HogLJ;#>TsMuV2T#*T~4j18$$ZJYofbEDt0CWM_lN;qs@`zOazU zdBpXToejMs;nN2>1j_5Mu;k)hP%aJcVcVhZV7bTl1b;uRH@N8bpA z0+K|aSoQLP+8W7M;QEAw#K%Jw4oVO7?Ah5k2dKXZh6pqaM4CI2GaVhhy;D-k%8-x? zzdh8em6ef^c6L@)6iP`6l8TU*85mep1Z^(nL$)JIpH!(3qMX-b1L*U)fa z0JJu+yrKO@Mu4&L;lol=RaIDvp>``KmX)=$0~Vi+)%$zsY_?oKcnbVf8mM+C)C=}iDJ;Bpt-oI+nl|l^e@siediCPP`1lVw<-f?tbLSAHhudvf7~&3fbr^F& z;qM>+K+Wuo85S1B#kX!D`+d?RLqm8{Ub}YmXky|<&gC#BrnI!O5`GnMeMi&^`M4Pw zIXP)*v9V!cP?=#G6Jgwvkf2hbLJbNxh)f~h7SSz4*HA4E2X*-BAmbNx0|f;vb4DzV ziOI@B?K-O9;HRHrh7Fo-VlniekjH6a0`1HjH`>~~yzs4y&#k*VAi%`L#zw3C z>@#?Vo0u$FqEJ9#UnVm%GdG8C-09QF$%_`5n8d~o4&J&|TDpqUn)LPU>4Ap7qvO1J zE-noXmoGOoAm?=T>YY2!ooj9N^D{BAwJj<-cdnzu-+%7h6)Or0zxd+PC8g4gTauFp z2QiDediCPPk&%6Ui1lIC#>c0rX=n(__;6W8v`(k<@><5Z29}ri_t(~L-3rZvf`Y!j z#>R+)tx|dTFus5Bn<|mu_dhomK0<+kyE$(_t=7|X#TI1ujM9TVf^3IfvAuoi(qH_-(h?PDckXQ6YH0ZW`l z{O8DsT0MLA#EBBg?b}bEmX{kD!N=_6$?w1K>9MkU<;yWKbLK2un3IEQ=eW4v|Nf(o5IuVEAU%ER)L;F|%WG)ptFJ06 z7c4L|w6*Q*M8%`6E%M9T+8#YRb;`$Q_UtulYHHvqD3Q#a%k(^lhT!+RXpvHh=M$L> zZXYo*Cr=IzW@MO~&z}#c>x&mlN(2H^(|~}sw!uN2&cy|~EG;c(&+gjg;IMF^n_G4D z*|XKvaA4W6L90D|x}^nPHXAm;e*zjH78a{k$>n{0$p7ECaoxIvgsv`B^E*0PTf+hS z*fFiv&29N|c-ghJX|+B+t5&(W<>t1xS5}I}*4FUP?(A%DPf2m*7NxSS?a(2OMkutm zkBx0_9~`W&_wq6_f?_~t=NDfbJt~t~S;6}YwUu>sQmM1Eqa%ErP^XIuE!1t5ma<$_ z)U(0GH8T^H9H^v$x*D<@6BE^HJQu+;H!?CO2YwMzQ7pz&Qi2Rj#B~sVL4?SMTV%3b zyDBTw)3E(#2QW>x*w}yo zeBVQp4P#SiuR!t1$H&Ub$tfoX^YyqgpaKJ>;GUk?SPKg?vxo?cAoB8{4=5BC6(RDz zc{6-X%F6osYHEUltgPJKwc6vy4;(-yvAa8JNe&*AN^NX5Y=D+lU!PLBX3f&2VPVb9 zhYx3G3j}6n0ReD$%g=`jC1Obf1J%_&K9-h#eo%&JZWf8m%p4ud%7%sx9g;{aENpGl z(z?1D8^golzot?hInvr19=>eZx^);8c6Z0dE?;hKos~5(@ad;=`SRtKmI}qdbA2~+ z^92hek|Re32GY|pYYernix=U}JasC|4`^(3bTl@0c5Z3;@=JK?z?ZGH_4e(qE)NeQ zqqS?Hbb6dqW}ZHM@#6gaYuAn+7m1)kk&|C&A$ z$UrbQ#;pF$o28{IRzSIf9ofS+k6c zva%jO9vRVSX3m^2K`j3A%YXm7qQcnN(9p$YXy{-6s;z~7fvxR{6R1K!*1oA}Yb*2> zJU!ojd-3A>`tQHLeqAJb^UXPPwAv?6?%$V6-+zDdF%$e@)y}fu2wX^%+1I)0&@dwp{YuCaHsig(#u265jbm`zhFRvLh zKKUdo>$A_!os-F+0SC9c^XD}hR2;>}ALZQcpy{?|O=M(C%aJ2FIncri4z8=~?pCW& z(qct*FrH5)(Z=-P~eh%gbwOGBcr724`2)oCOBj+xz+D=QlN# zmqVY-(^IW(Ypbq~h_JPFa7ax(c(AimE*A)FY+_b)+|60NKJ*m#>~VIUKY#P)y1L=vnwr&|XZ4~*(D3-~yOFJLjKYA1&{{~lA5)<25-MzRW&;L^l3;4YG4Wqur;fJQ7F*P$-&V!Z@znXbhM)4 z-FFQP?Ce0JwROe}1A}GDE?*uUy?WKk%D`a30?c{cz3c33U@&uLT^)2!P+9Yv-=wEM zefs#ZOg4V}xN#E6-~ayYw^>>5y=Pz`6kfjk^l4q)pa1;syPlqdgCipy9k#YprZ_v} zd#0yn)21m?tgUNnZ{6zZf;X0}ZFxDIt2S?j%NQKG@GUrP+R~+{pt*1XdXyFx`S~|* zUb++?KX0CiNowki8-M+4R@NW>fcc`XuB%r|OD!#-1libly*+YDs;aJX^}u0a z=H?C#B_$UwKugTVX5+@JtfNP}yKyzGUoVw5aEnZ~ky}vXQd=vNZQ6tZ*OQwOmuY>3h{ite}8&9W>AxoQ2&~e;^X7u5*7x9E<6o*dqWj5KOYX_ z9v%q^a0FMYLqfJ~Q!1;g_wCEh4hX<}Z+(4Rn_BJdy>Vkmh)&nuUQ~qNR1c55Jofa1 zs`0e6j*ga=sj0Ly zIM~4f_hM^nWu-)dC}?VGBe!H^h(!MW3eFY0tSm86Bnk@3%tRC;HdZL~^-WI3Q$;|4 zy}gf*M$^=^YnMp0e7T*SN_FBye}7uqx^-4oiHZIF=g*gvY}#aQ9vpn|;P7y5t+Vr= z{$y`oR)*1@zyHF8*49w4?CcB+Gc~oeL@ns?<8t}Rm8j-Bas&#N0>SFlQfXh`*|T|h z>(`r^g@ztDaOMnZK^HEBt^xd3?d(4J#NQtoZBLy+OVO! zdt~Iy880s=9M{)Bd^j-R?>}YA;>9H;ckjXndD0|fW3~G3-QnTj;J4nIJXtP(@ZjOY z^z`Y|Cr;eH{ocKik;201(+v#+10OzwuF8iW!e=a;|4?8mp?j*gCu%$1Ss_lAuP;F;!jvBe)Va)K(p54%ZD#3_| zapT^6Q=u3cdG;(fck<+McEwXnnssWcQ!B}Q!2#(DFcovW+QpKosV^@a0*l2bf|5|}_> zZtmr!(Li}cD0FvMDmyxQdvkN0oR%%~_SWf+A8%}ojI^^o7=i|VsUwSN5`&R!NHD>0Re@D&CNO;6!E;gva%W*`D*Bdgt|JYXu7+ze36!x z++1H@JG*V$N=iC9Dl5ao?d^p^)Z^9ENTr)MJ3FJQzo7x{+|1z^BWa9z;jms&k((P4 z;p-a`lAW#76&1zB1q2|*$)2Uz69ZgfneMStiFz>hOO1$>N);K2QFvBXWhH)tw`}qA zOGqdwDJxSdp}iU(uho{9BX8TsCn^e~ZTPW6wG2whYISrpG!&VF87j+|!yU^eLo^4O zBFHmi+8U_t4+)8lg~|XMWyHo76yW&=zRbvTX=tdZh>gW}AV$R1)re8Jx?(=OzCI^M zBtnEaFR!LXtBs2C_6`bydpu&Tetw7~=yZtvBF6`cW%#Cq)-&eJ^YRcE2nk6^DK19c zT3{el(C`hKmgu@le2SY#&hrTs3?pWH5zyaLr+Gl)z5}N-D)7W zBqkOWm6W8WqEZnVdYD6qirTgfPpxnp#(e>HxOb$|kPwN4pQAu_AfA6U8bocO!hjJp zdltrdGBTJ)Wf~DszhN<1jE*tR#w;o3E~B421*CKHuuxfN(NjEc%Q$UK%(1Gt1(DRK zD125}D?T3aTKG6a!ymbfOjSmqfbua^gy8^yS>AZA5(AN67xf&dQ)GIfhFLO9C^utlBay4K}lHk{@a#QB8^LO7=Nsa@rsG z9MNy6k}}Uq==&kp6i(5o27x*no3Twz#Y}X3JStNWEn{uW7pc`y8f8}jl;-(7O?DL^ zeu3E;jRx@xrVtMWL5z=(gT@q0!^4rM#wKGEKP?UMPN?_G<*Wi0ZFUAwKw&X| z*1Pz47XL?2Sua>mnP=(P5flo1`^aQaF=6xl37jhcXeqe`b15(Sjv^zNz5b#%Cn|vx zxx4}1Q*ti#TGtlzA!`bQgUkFAC6_2uxDxgE#P4TpPk80^&H-z_dSN|%_4_~X9sM5tye?PZ_w4n~UVpDY-G1>Z`r*~j`o&*s_>XUo?UnD*tM`QM*!n+bofnV5ANj>z@UQ>y z2>6dbn*N^H)~rWt9jwL9f#1`QA3^^-`8L+%&rQG09~rOl?U#Eoc4U5A{dLCL{1Ny) zjqR0f$6kNA&0f*}tKV<^Hh%>Dby-`#7cam2@+tD-1 zz3!{8F8w~~w|{zn>|M6L{x`wvY{lQ@uea|1)mr-Jrr&;@{l9wsSHJT*`=xb=01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp zM1Tko0U|&IhyW2F0z`la5CI}U1c(3;AOb{y2oM1xKm>>Y5g-CYfCvx)B0vO)01+Sp XM1Tko0U|&IhyW2F0z}|Hk-&ce4Utjh diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index ebfeec172..3d88c7cc1 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,12.8,, +Version,+,12.8,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2076,10 +2076,10 @@ Function,+,nfc_device_save_shadow,_Bool,"NfcDevice*, const char*" Function,+,nfc_device_set_loading_callback,void,"NfcDevice*, NfcLoadingCallback, void*" Function,+,nfc_device_set_name,void,"NfcDevice*, const char*" Function,+,nfc_file_select,_Bool,NfcDevice* -Function,?,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" +Function,-,nfc_util_bytes2num,uint64_t,"const uint8_t*, uint8_t" Function,-,nfc_util_even_parity32,uint8_t,uint32_t Function,-,nfc_util_num2bytes,void,"uint64_t, uint8_t, uint8_t*" -Function,?,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" +Function,-,nfc_util_odd_parity,void,"const uint8_t*, uint8_t*, uint8_t" Function,-,nfc_util_odd_parity8,uint8_t,uint8_t Function,-,nfca_append_crc16,void,"uint8_t*, uint16_t" Function,-,nfca_emulation_handler,_Bool,"uint8_t*, uint16_t, uint8_t*, uint16_t*" @@ -4368,8 +4368,8 @@ Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint Function,-,ucStreamBufferGetStreamBufferType,uint8_t,StreamBufferHandle_t Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" -Function,?,ulTaskGetIdleRunTimeCounter,uint32_t, -Function,?,ulTaskGetIdleRunTimePercent,uint32_t, +Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, +Function,-,ulTaskGetIdleRunTimePercent,uint32_t, Function,-,ungetc,int,"int, FILE*" Function,-,unsetenv,int,const char* Function,-,usbd_poll,void,usbd_device* @@ -4380,7 +4380,7 @@ Function,-,uxStreamBufferGetStreamBufferNumber,UBaseType_t,StreamBufferHandle_t Function,-,uxTaskGetNumberOfTasks,UBaseType_t, Function,-,uxTaskGetStackHighWaterMark,UBaseType_t,TaskHandle_t Function,-,uxTaskGetStackHighWaterMark2,uint16_t,TaskHandle_t -Function,?,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" +Function,+,uxTaskGetSystemState,UBaseType_t,"TaskStatus_t*, const UBaseType_t, uint32_t*" Function,-,uxTaskGetTaskNumber,UBaseType_t,TaskHandle_t Function,+,uxTaskPriorityGet,UBaseType_t,const TaskHandle_t Function,-,uxTaskPriorityGetFromISR,UBaseType_t,const TaskHandle_t From 25ce710b6e734f6325a35dc3c630ea0f23408c55 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Wed, 8 Feb 2023 04:32:23 +0100 Subject: [PATCH 110/231] Added multiple apps Added Geiger Counter, Nightstand, Scrambler, Pomodoro --- .../plugins/brainfuck/application.fam | 15 + applications/plugins/brainfuck/bfico.png | Bin 0 -> 1822 bytes applications/plugins/brainfuck/brainfuck.c | 149 +++++++ applications/plugins/brainfuck/brainfuck.h | 3 + applications/plugins/brainfuck/brainfuck_i.h | 89 ++++ .../brainfuck/icons/ButtonRightSmall_3x5.png | Bin 0 -> 1738 bytes .../icons/KeyBackspaceSelected_24x11.png | Bin 0 -> 1977 bytes .../brainfuck/icons/KeyBackspace_24x11.png | Bin 0 -> 1979 bytes .../icons/KeyInputSelected_30x11.png | Bin 0 -> 1992 bytes .../brainfuck/icons/KeyInput_30x11.png | Bin 0 -> 1994 bytes .../brainfuck/icons/KeyRunSelected_24x11.png | Bin 0 -> 1984 bytes .../plugins/brainfuck/icons/KeyRun_24x11.png | Bin 0 -> 1984 bytes .../brainfuck/icons/KeySaveSelected_24x11.png | Bin 0 -> 1853 bytes .../plugins/brainfuck/icons/KeySave_24x11.png | Bin 0 -> 1863 bytes .../plugins/brainfuck/icons/bfico.png | Bin 0 -> 1822 bytes .../brainfuck/scenes/brainfuck_scene.c | 30 ++ .../brainfuck/scenes/brainfuck_scene.h | 29 ++ .../brainfuck/scenes/brainfuck_scene_config.h | 6 + .../brainfuck/scenes/brainfuck_scene_dev.c | 16 + .../brainfuck/scenes/brainfuck_scene_exec.c | 16 + .../scenes/brainfuck_scene_file_create.c | 50 +++ .../scenes/brainfuck_scene_file_select.c | 34 ++ .../scenes/brainfuck_scene_set_input.c | 35 ++ .../brainfuck/scenes/brainfuck_scene_start.c | 55 +++ .../plugins/brainfuck/views/bf_dev_env.c | 419 ++++++++++++++++++ .../plugins/brainfuck/views/bf_dev_env.h | 15 + applications/plugins/brainfuck/worker.c | 276 ++++++++++++ applications/plugins/brainfuck/worker.h | 9 + .../plugins/geigercounter/application.fam | 13 + .../plugins/geigercounter/flipper_geiger.c | 227 ++++++++++ applications/plugins/geigercounter/geiger.png | Bin 0 -> 8048 bytes applications/plugins/nightstand/ClockIcon.png | Bin 0 -> 7920 bytes .../plugins/nightstand/application.fam | 13 + applications/plugins/nightstand/clock_app.c | 338 ++++++++++++++ applications/plugins/nightstand/clock_app.h | 39 ++ applications/plugins/pomodoro/application.fam | 12 + .../plugins/pomodoro/flipp_pomodoro_10.png | Bin 0 -> 157 bytes .../plugins/pomodoro/flipp_pomodoro_app.c | 101 +++++ .../plugins/pomodoro/flipp_pomodoro_app.h | 32 ++ .../plugins/pomodoro/flipp_pomodoro_app_i.h | 31 ++ applications/plugins/pomodoro/helpers/debug.h | 5 + .../plugins/pomodoro/helpers/notifications.c | 49 ++ .../plugins/pomodoro/helpers/notifications.h | 14 + applications/plugins/pomodoro/helpers/time.c | 20 + applications/plugins/pomodoro/helpers/time.h | 24 + .../flipp_pomodoro_focus_64/frame_00.png | Bin 0 -> 1242 bytes .../flipp_pomodoro_focus_64/frame_01.png | Bin 0 -> 1215 bytes .../images/flipp_pomodoro_focus_64/frame_rate | 1 + .../flipp_pomodoro_rest_64/frame_00.png | Bin 0 -> 1083 bytes .../flipp_pomodoro_rest_64/frame_01.png | Bin 0 -> 1080 bytes .../images/flipp_pomodoro_rest_64/frame_rate | 1 + .../plugins/pomodoro/modules/flipp_pomodoro.c | 94 ++++ .../plugins/pomodoro/modules/flipp_pomodoro.h | 54 +++ applications/plugins/pomodoro/scenes/.keep | 0 .../config/flipp_pomodoro_scene_config.h | 1 + .../pomodoro/scenes/flipp_pomodoro_scene.c | 30 ++ .../pomodoro/scenes/flipp_pomodoro_scene.h | 27 ++ .../scenes/flipp_pomodoro_scene_timer.c | 71 +++ applications/plugins/pomodoro/views/.keep | 0 .../views/flipp_pomodoro_timer_view.c | 195 ++++++++ .../views/flipp_pomodoro_timer_view.h | 21 + applications/plugins/scrambler/LICENSE | 21 + applications/plugins/scrambler/README.md | 16 + .../plugins/scrambler/application.fam | 20 + applications/plugins/scrambler/assets/1.png | Bin 0 -> 1964 bytes applications/plugins/scrambler/cube.png | Bin 0 -> 96 bytes .../plugins/scrambler/rubiks_cube_scrambler.c | 115 +++++ applications/plugins/scrambler/scrambler.c | 102 +++++ applications/plugins/scrambler/scrambler.h | 3 + 69 files changed, 2936 insertions(+) create mode 100644 applications/plugins/brainfuck/application.fam create mode 100644 applications/plugins/brainfuck/bfico.png create mode 100644 applications/plugins/brainfuck/brainfuck.c create mode 100644 applications/plugins/brainfuck/brainfuck.h create mode 100644 applications/plugins/brainfuck/brainfuck_i.h create mode 100644 applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png create mode 100644 applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyBackspace_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyInput_30x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyRunSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeyRun_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png create mode 100644 applications/plugins/brainfuck/icons/KeySave_24x11.png create mode 100644 applications/plugins/brainfuck/icons/bfico.png create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene.h create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_config.h create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c create mode 100644 applications/plugins/brainfuck/scenes/brainfuck_scene_start.c create mode 100644 applications/plugins/brainfuck/views/bf_dev_env.c create mode 100644 applications/plugins/brainfuck/views/bf_dev_env.h create mode 100644 applications/plugins/brainfuck/worker.c create mode 100644 applications/plugins/brainfuck/worker.h create mode 100644 applications/plugins/geigercounter/application.fam create mode 100644 applications/plugins/geigercounter/flipper_geiger.c create mode 100644 applications/plugins/geigercounter/geiger.png create mode 100644 applications/plugins/nightstand/ClockIcon.png create mode 100644 applications/plugins/nightstand/application.fam create mode 100644 applications/plugins/nightstand/clock_app.c create mode 100644 applications/plugins/nightstand/clock_app.h create mode 100644 applications/plugins/pomodoro/application.fam create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_10.png create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app.c create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app.h create mode 100644 applications/plugins/pomodoro/flipp_pomodoro_app_i.h create mode 100644 applications/plugins/pomodoro/helpers/debug.h create mode 100644 applications/plugins/pomodoro/helpers/notifications.c create mode 100644 applications/plugins/pomodoro/helpers/notifications.h create mode 100644 applications/plugins/pomodoro/helpers/time.c create mode 100644 applications/plugins/pomodoro/helpers/time.h create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_01.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_rate create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_00.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_01.png create mode 100644 applications/plugins/pomodoro/images/flipp_pomodoro_rest_64/frame_rate create mode 100644 applications/plugins/pomodoro/modules/flipp_pomodoro.c create mode 100644 applications/plugins/pomodoro/modules/flipp_pomodoro.h create mode 100644 applications/plugins/pomodoro/scenes/.keep create mode 100644 applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h create mode 100644 applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c create mode 100644 applications/plugins/pomodoro/views/.keep create mode 100644 applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c create mode 100644 applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h create mode 100644 applications/plugins/scrambler/LICENSE create mode 100644 applications/plugins/scrambler/README.md create mode 100644 applications/plugins/scrambler/application.fam create mode 100644 applications/plugins/scrambler/assets/1.png create mode 100644 applications/plugins/scrambler/cube.png create mode 100644 applications/plugins/scrambler/rubiks_cube_scrambler.c create mode 100644 applications/plugins/scrambler/scrambler.c create mode 100644 applications/plugins/scrambler/scrambler.h diff --git a/applications/plugins/brainfuck/application.fam b/applications/plugins/brainfuck/application.fam new file mode 100644 index 000000000..6e2b6d1f9 --- /dev/null +++ b/applications/plugins/brainfuck/application.fam @@ -0,0 +1,15 @@ +App( + appid="Brainfuck", + name="Brainfuck", + apptype=FlipperAppType.EXTERNAL, + entry_point="brainfuck_app", + requires=[ + "storage", + "gui", + ], + stack_size=8 * 1024, + fap_icon="bfico.png", + fap_category="Misc", + fap_icon_assets="icons", + fap_icon_assets_symbol="brainfuck", +) diff --git a/applications/plugins/brainfuck/bfico.png b/applications/plugins/brainfuck/bfico.png new file mode 100644 index 0000000000000000000000000000000000000000..b25368fb53e03b73878a9aa17a044c2d8dffea4a GIT binary patch literal 1822 zcma)6eM}o=7(YOmKyZGbBNNT#*c_9!cReT{7dirMfhxy3z6O4=Yws<+(e}=}D=ip7*(DuemTUCTeXIf*>)bd_xg@E9&+&;9yN=+jdK75 zNgO}WHlAeI8at9@wST^cjE;Wo&AI%pY})Zgdviz1zO505+S5*dbUq^E9Q0s*?vo}AfY~MGw zu|Mzolpned^&i|}@9r|UW{rFobKix^-<~<~`{a9D*2O#=KX^8w_sE*&4b4qS;g_z_ z%>ItO>znt#TrbxizB$-4;%pp9ch2lSSGO*{^-4$cf!Wn9^V_xE*PDC9E7G@MaPIMA z4|>R~WYsWTmK4E!G2Km^j|*v-P2Ti%9v(J(z9sA0(ByRSCR5*&W!-zmFpiA<@d0Pp79Ia%{^SR_JA^Z1H*V=KHF3l8&g!tuPanB6%y+!KYUx`BAxQK?$`gWgbjBe_ zcqCU+E|=2M$QM1=orh$50Da& zB0#ljt(w4KIErS9tc@x%g0It(iCNdsPPiQFXx3II!iD;`{ux2YWdl}(wAWi=})ahK5Ey>0jm~fzeL6R4UrIK0! z;6*^<-J%s3Yk*5mz)+0+$5K3LV69PsLrH#lrZ> zXr6^!$r!q1hqEFuDs-4sl$V2=jQUhfMrx{xRKr7+N>L^!Qw0iut4g-}4_vZtbMH0)HCkr@LcL%}2g`%FWsNTP($QYj>* z(IycY6aiEBfq9m*)&3XM*WD=mWx^Pmrt&#S=Ed4YbD(F7!HdgG0i%Oc4uV09RY^^U z2_nzBt$_6jfOd(u$sR@o*;VlVbXc~{#=-P!QM1yws79?J)Ts+tj~zgU;G8rm6p_A= z6TYGVUoWj7z;FM50{qyO1OydzxqwVu`Er+6B@scene_manager, event); +} + +bool brainfuck_back_event_callback(void* context) { + furi_assert(context); + BFApp* brainfuck = context; + return scene_manager_handle_back_event(brainfuck->scene_manager); +} + +BFApp* brainfuck_alloc() { + BFApp* brainfuck = malloc(sizeof(BFApp)); + + brainfuck->dataSize = 0; + brainfuck->view_dispatcher = view_dispatcher_alloc(); + brainfuck->scene_manager = scene_manager_alloc(&brainfuck_scene_handlers, brainfuck); + view_dispatcher_enable_queue(brainfuck->view_dispatcher); + view_dispatcher_set_event_callback_context(brainfuck->view_dispatcher, brainfuck); + view_dispatcher_set_custom_event_callback( + brainfuck->view_dispatcher, brainfuck_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + brainfuck->view_dispatcher, brainfuck_back_event_callback); + + // Open GUI record + brainfuck->gui = furi_record_open(RECORD_GUI); + view_dispatcher_attach_to_gui( + brainfuck->view_dispatcher, brainfuck->gui, ViewDispatcherTypeFullscreen); + + // Open Notification record + brainfuck->notifications = furi_record_open(RECORD_NOTIFICATION); + + // Submenu + brainfuck->submenu = submenu_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewMenu, submenu_get_view(brainfuck->submenu)); + + // Popup + brainfuck->popup = popup_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewPopup, popup_get_view(brainfuck->popup)); + + // Text Input + brainfuck->text_input = text_input_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, + brainfuckViewTextInput, + text_input_get_view(brainfuck->text_input)); + + // Textbox + brainfuck->text_box = text_box_alloc(); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewTextBox, text_box_get_view(brainfuck->text_box)); + brainfuck->text_box_store = furi_string_alloc(); + + // Dev environment + brainfuck->BF_dev_env = bf_dev_env_alloc(brainfuck); + view_dispatcher_add_view( + brainfuck->view_dispatcher, brainfuckViewDev, bf_dev_env_get_view(brainfuck->BF_dev_env)); + + // File path + brainfuck->BF_file_path = furi_string_alloc(); + + return brainfuck; +} + +void brainfuck_free(BFApp* brainfuck) { + furi_assert(brainfuck); + + // Submenu + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewMenu); + submenu_free(brainfuck->submenu); + + // Popup + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewPopup); + popup_free(brainfuck->popup); + + // TextInput + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextInput); + text_input_free(brainfuck->text_input); + + // TextBox + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewTextBox); + text_box_free(brainfuck->text_box); + furi_string_free(brainfuck->text_box_store); + + //dev env + view_dispatcher_remove_view(brainfuck->view_dispatcher, brainfuckViewDev); + bf_dev_env_free(brainfuck->BF_dev_env); + + // View Dispatcher + view_dispatcher_free(brainfuck->view_dispatcher); + + // Scene Manager + scene_manager_free(brainfuck->scene_manager); + + // GUI + furi_record_close(RECORD_GUI); + brainfuck->gui = NULL; + + // Notifications + furi_record_close(RECORD_NOTIFICATION); + brainfuck->notifications = NULL; + + free(brainfuck); +} + +void brainfuck_show_loading_popup(void* context, bool show) { + BFApp* brainfuck = context; + TaskHandle_t timer_task = xTaskGetHandle(configTIMER_SERVICE_TASK_NAME); + + if(show) { + // Raise timer priority so that animations can play + vTaskPrioritySet(timer_task, configMAX_PRIORITIES - 1); + view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewLoading); + } else { + // Restore default timer priority + vTaskPrioritySet(timer_task, configTIMER_TASK_PRIORITY); + } +} + +int32_t brainfuck_app(void* p) { + UNUSED(p); + BFApp* brainfuck = brainfuck_alloc(); + if(!brainfuck) { + return 0; + } + + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_mkdir(storage, "/ext/brainfuck"); + + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneStart); + + view_dispatcher_run(brainfuck->view_dispatcher); + + brainfuck_free(brainfuck); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/brainfuck/brainfuck.h b/applications/plugins/brainfuck/brainfuck.h new file mode 100644 index 000000000..2e58321a6 --- /dev/null +++ b/applications/plugins/brainfuck/brainfuck.h @@ -0,0 +1,3 @@ +#pragma once + +typedef struct BFApp BFApp; \ No newline at end of file diff --git a/applications/plugins/brainfuck/brainfuck_i.h b/applications/plugins/brainfuck/brainfuck_i.h new file mode 100644 index 000000000..d3d27dcbd --- /dev/null +++ b/applications/plugins/brainfuck/brainfuck_i.h @@ -0,0 +1,89 @@ +#pragma once + +typedef struct BFDevEnv BFDevEnv; +typedef struct BFExecEnv BFExecEnv; +typedef unsigned char byte; + +#include "brainfuck.h" +#include "worker.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "scenes/brainfuck_scene.h" + +#include "views/bf_dev_env.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define BF_INST_BUFFER_SIZE 2048 +#define BF_OUTPUT_SIZE 512 +#define BF_STACK_INITIAL_SIZE 128 +#define BF_INPUT_BUFFER_SIZE 64 +#define BF_STACK_STEP_SIZE 32 + +enum brainfuckCustomEvent { + // Reserve first 100 events for button types and indexes, starting from 0 + brainfuckCustomEventReserved = 100, + + brainfuckCustomEventViewExit, + brainfuckCustomEventWorkerExit, + brainfuckCustomEventByteInputDone, + brainfuckCustomEventTextInputDone, +}; + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +struct BFApp { + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notifications; + SceneManager* scene_manager; + Submenu* submenu; + Popup* popup; + TextInput* text_input; + TextBox* text_box; + FuriString* text_box_store; + FuriString* BF_file_path; + BFDevEnv* BF_dev_env; + int dataSize; + char dataBuffer[BF_INST_BUFFER_SIZE]; + char inputBuffer[BF_INPUT_BUFFER_SIZE]; +}; + +typedef enum { + brainfuckViewMenu, + brainfuckViewPopup, + brainfuckViewLoading, + brainfuckViewTextInput, + brainfuckViewTextBox, + brainfuckViewWidget, + brainfuckViewDev, + brainfuckViewExec, +} brainfuckView; diff --git a/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png b/applications/plugins/brainfuck/icons/ButtonRightSmall_3x5.png new file mode 100644 index 0000000000000000000000000000000000000000..b9d5f87db1ca55141449cfcc3bf054417eaa84e1 GIT binary patch literal 1738 zcmcIl%Wm676lEHu%>zLLWYHwZf?zfc+Tjd`l=wgx!?E02K7gZd!)A>b-AnNYDb z=UD-0O?$90FBm_PwI0iHnuo^kzrHc_RD{NpUPPi|OHR_A(^5V@-5v4MBkl`h`li))oId$h zr-Twrdf1}K>IcLLELU%T22?9W66_DYYiq$%XiVz52r!<_X6DQ`RXN6%@B5fgOeq2c zsup?8<|wc3bqoVp@iHyyRONcZ$YOO|hXyEJO(84Rw0YIq1cu=`E3jpfW=b6}iq3{+ z*&1Ed+b2+^)%#K6YP2XM-j|g+F1g%3k$HWuD^^TYt*VLogtqnH|4=CSx?pi!PM7uw zj^$Klz+C~>TIwr;tx~dDl_RC5T~K>nMV(qE)xUm{=0eS?`;DS@fE=(|h6bc&Ap((k zBdZr!eqejwSR^211&yE&1gqKkz)Gaa;ylnO3Wj-Avz*J}AT&UfnWiG(Hm6?C6^L<1 zqL@1bP64XW zqKrwxT^V~c?$~}TQ}}Y&^h4H0l>o-Xk=)_jK{NqDuJ0r$_IQFAaV$sJiQn_7p}()Y zrKYNklmK^aLl-wVr`&ul_BH)&R_4UgD(ZOFr}nOx%X#N!gVeJ4h)=Wyh? zQXrf4V!h1m}&`|!B-3a0&FC= whJ($~<(FI>9{%<2-NwbcKNmOR7sY+;Hox}g7dMW6Yj&IA_U_=9M~Bb;1{rl65&!@I literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspaceSelected_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..c79cfb6c61feb86ebd442c0ed12f4b64f54fad54 GIT binary patch literal 1977 zcmcIlPmkL~6n8~;E4ErxK@Syhu)IBh%Gfjh8(T@MY_jY|s+VY+s>`jD@i?(I@fh2? z$u1HH-~bYW8wkWF0I3oeB;bHV#g$9H09Q_Y0mSlbCuxwD?OurFjOY2y@BRC|c^`Lo zx7XKRUXvtgy|dHagY^bHS1&&Y|6jb){Tr4`+1~c1^ys%&|A5WqLDO$a(s!R-{fn-^ zezkkIw{?1Y3a|HndGa1C&nG)?X8`e1@m!L=`WgVQtVF&2eBZz8F)>zYAo^ULjuX(9 zr23s{LfHY&))cu^$qyPzv#==d`(3#uQZCynMhs(Jc3hR%rfK6hWF6~R z!`oEh~hv@veyfbW(Rni=DeXm z^$5Z`!lXw`Psg5FyG5{vai!`59|D@wiz$mB3Xdib?N&T<+v-f1@!a7vA$z+;YK z5sTIkCmbUZm>9XlaFJ~}w(dBVV}zvi3(mI%J1jC$oUiB`2nHC7YxGqH)7?S=$1 z%OIAc$WQII#7LyR2q3%0vb@u3byE>WBlu9<4030yWfRx7k*-2m0rNtUjwui=8q)#S zl6WxZKCmCWfv9()3`%`m_EptnWp~};H1T|2wL^{2d=bF&FuQ?MXg%2dgMO*&W-Em&~-?Dnv0yx+GSj;J1TEH zrrxbiyV<+(ggw08mR6hQ3mY$POJ6>gR^(5mPac1ye(=qeU(K8EUQ@4Km!#jV)gM0m S{KuQcwCrr{wjXUieE%Qfkx0w{ literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png b/applications/plugins/brainfuck/icons/KeyBackspace_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..00e66428d76e298f0c2c784e1fad66b98d832b8a GIT binary patch literal 1979 zcmcIl&2Jk;6yK<6DpJCQ9tu=wwY`8~yfgdhUD+*;Z5m76V5P1aZW+(c+N;<*+ucp< zM2JIgNJ#XCg!l_Us>B6Fa6sb3iA(+pBqYRvTSa)gYr94YX)ah=&(7O7zxVZf^FHft z-`iM!b6t|8jm}nU2iBYLTwlEc|6jh<{Tr5*bm!it^z7GLzrkj8(C`|P^xYTNpVOB!4C3F%ToBi#Ebo`P!g8W;Uzv%+b1HDT zSWDQ+kOSxi1S-{15o8mZ@WueD^>Tu$mk1=KhZl!&IU@a^VU*ZxNTUIV8yQ-4Ik1|V zUtY^qtEuM~xb}iqb8a@EYjPH(OPMNIut^6?q6G`?oZlW+mG}FW%@NfCjvTXS4^2l^ zN;T|gRa@)x($7)21Y3%hP1V$w`&gb@y1fg;QVOF1c94MiT*`Ng*~TMCUjmcs+q3T< z!qh{ZR-)xFint52(+|8nc(HThcfMEc>?Z{EIf`t9YlHa)U U{I8QgKgs81r@h^Jwz>D@AE;?dx&QzG literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png b/applications/plugins/brainfuck/icons/KeyInputSelected_30x11.png new file mode 100644 index 0000000000000000000000000000000000000000..4c04a08566958747a8977da87a884723241f8ad2 GIT binary patch literal 1992 zcmcIl&5zqe6!)s#!fv&vUJ62Bxg4lq>=}Q?R?;e)EZs=!CEBLy!UZPwII%YI7~8wa z?x9GnIB-BcajEzZkcuiq#mAwv7sRDPs?;8P=ERW;((-I4X^@ufUWnw3=lRX=ef{3N zFWQ?|R+e9176f6Xwcgx<^-Xxro<0TtcNWDTVOdDGuB-}oAHVSjY)<#;Ze0-W-9Gy_ zU4;Fa_C{yz@bC~`uRVHl1D2(D{lgR>&gIX7aOdmug7C^>*xAmu-3`a%Lz()#%jEGe z25mv8ULMEP+hLj5WxX(}Nq_$QKoUd0CS5mN?8Xh&57!S8cJ*Mh;~ngHwl7`2w6a_s zJAg1`85PIFL6kb-HQ%3@gI!JPXIbnh%4jr_N1Du&otcVdV2sdNKECa)32y z#%dB&k07ifOgcn&RP5-LcL{bdE>&INgHJPhGG*?E!Tw29UDs(vX+|TDwVE{voRY)P zcRWioh(Rle5rz@*b&PDH*~m03Q?)F^(gITW1?QXG+s!kPpD*d_a}O|P*>p7KRhX}L zQJ`yGWL0z@b#;v*tP|pUR5MLOpQ3$`gwR5CFprvhxRIe% z=L4%M`Q-dS`1M&V8}L# z_E0rcp;X1TTCvnFEBzdVbFigo-c(h2xsT0UlOTWg!myQ`nv{0Aa^SG)iK literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeyInput_30x11.png b/applications/plugins/brainfuck/icons/KeyInput_30x11.png new file mode 100644 index 0000000000000000000000000000000000000000..d23e24aaf79f293d10680c29a582ecb0fbecd6ed GIT binary patch literal 1994 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 literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png b/applications/plugins/brainfuck/icons/KeySaveSelected_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..eeb3569d3accc5a5c56829b12c85079172b56729 GIT binary patch literal 1853 zcmcIlPiW*+7#}^zw%YZuf+B)3d$6*;fxiXAXK-X$#&ks`?Z1FR>QX z26aVbT~%`&N5w=X1OWo&yGcQZD9KMx7+O3JvM4Pgkw_&Y^~HA4kU?pcLYz)%lYCqz zD405=sj4ZsOlbo2yrZFUJVocl(hfu!>phe>@9d^rUFW&j&H}!)!;|9lBv{%Lg~)s2 z4%()#|Dlit(}3xA)*qFJ1uF0J7`Su5Y9oEA+srsEMAi|aKWWt3B%(w#g-G)oQNqL^ zf1*@0Ucg(l;0+nNrXfra);gN*63r#X84bG_S5Oapz-U2_2No;}caH=0Jhz?X1x*6p zZZ%{Or9=^P?a(oNj2+}NPLO5lb>xS(hK#yzQ(Fto(z;~|u)ZaN?XnW(`pULU1i&$^ zrW-ON3~kFtm|7MJL)}ES1tUU3N-T@l>$)*vdoGLM%c1>)tfeXjj8tQ`U&jXGx~+pC zJw&zve}0HDBg6^Jz?Y@lahswqGEXq5ZvEhVyV+dJL>TqqMZUhgD7BZGrskL?B8nzU zEO0}S#T1Md#k9-SH0hSMuhLzKa_I5y_(QtDUmB14ku-9rOM~*GXvjh71`cJarlUj3 ze7uCJ^@AP<(j#0_!EzB61Df%LF0|x7U8vqkd`@?cmVP{k{EyPdWes{X>2la%Rk=(? zE%&0TDeAxbb=w#db1i`F%Wmf5GAz>Wv>@jW_p)ho-#0CeC9cGbyZ*s9Cn^o)Rq=_$h#NIZix%+wt GFa8a7)lmol literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/KeySave_24x11.png b/applications/plugins/brainfuck/icons/KeySave_24x11.png new file mode 100644 index 0000000000000000000000000000000000000000..e7dba987a04dad7dd96001913c55566dbde96c8e GIT binary patch literal 1863 zcmcIlO^6&t6dpxnjjSF75f9pQJZy|LUDdzSZ6$n-*6-2D5s-9_fx~uK( zotfQBVDX?Q(UXLzXF6aX)%U*l z-d9y`w^q+Do_PF3p-@5qOL5h2N9RU z^i-~BIziNECdw*wjUcQeOxncsbnKa>(*%1MPoPck0jC)~9$50g-#!ks+4LGwn$d`f zMy;%ZsA3Rsk2!`#ELuW>2#g3fF>;CFBHMCo-El0(@X1&g%&$qdl~*F4Kd~*B3^?Z1 z^bEmDf}0)Wn??sYC6l9$X;6esLO7#_ZCmDy?ZqU3l|%anS#wn!7%f39-Qp(l9fyJ- z(?=x}n~2%2PcX9#VmYdECvH{tWzv)!s%sn^Z&a(TMEXG=KBQ~smzBm!)h4cOBfSV| zapw6l2`LyY2x(Vnan#Li4>BO#dXPeox2Fr~f_P*4)DM)gJ3Y$sMNw8+?gqit>2PpJ znU9yygm%~yKzf8rCa_fc*^nlp(uJ1%rwg^aiBIX^Xz9mu$p0vPT2|JhQCGkYtEqW1 zTD})enxg%?Uw4c#Ggk#{pLa8zmSLH8=LI=?xR>pc=yYsHAgcQUxz^arx`9fR>e$sw zj@}Uy75!kQXF{tT9e=F+z^*!*3|n>nI6oucWq!(t2og`=40-O!tAE1z^ID@;X)nEd zVK7xqj`wg%Ed2Op{k=a*YZpKHX787$ zzhKuN9(?M5ojmn%xcU1}OP>$^`ZD?cW@lIaTn=x3xBtP#z16o~{qVMZ>hecsD?jQQ ME3387mS5lf8`39Il>h($ literal 0 HcmV?d00001 diff --git a/applications/plugins/brainfuck/icons/bfico.png b/applications/plugins/brainfuck/icons/bfico.png new file mode 100644 index 0000000000000000000000000000000000000000..b25368fb53e03b73878a9aa17a044c2d8dffea4a GIT binary patch literal 1822 zcma)6eM}o=7(YOmKyZGbBNNT#*c_9!cReT{7dirMfhxy3z6O4=Yws<+(e}=}D=ip7*(DuemTUCTeXIf*>)bd_xg@E9&+&;9yN=+jdK75 zNgO}WHlAeI8at9@wST^cjE;Wo&AI%pY})Zgdviz1zO505+S5*dbUq^E9Q0s*?vo}AfY~MGw zu|Mzolpned^&i|}@9r|UW{rFobKix^-<~<~`{a9D*2O#=KX^8w_sE*&4b4qS;g_z_ z%>ItO>znt#TrbxizB$-4;%pp9ch2lSSGO*{^-4$cf!Wn9^V_xE*PDC9E7G@MaPIMA z4|>R~WYsWTmK4E!G2Km^j|*v-P2Ti%9v(J(z9sA0(ByRSCR5*&W!-zmFpiA<@d0Pp79Ia%{^SR_JA^Z1H*V=KHF3l8&g!tuPanB6%y+!KYUx`BAxQK?$`gWgbjBe_ zcqCU+E|=2M$QM1=orh$50Da& zB0#ljt(w4KIErS9tc@x%g0It(iCNdsPPiQFXx3II!iD;`{ux2YWdl}(wAWi=})ahK5Ey>0jm~fzeL6R4UrIK0! z;6*^<-J%s3Yk*5mz)+0+$5K3LV69PsLrH#lrZ> zXr6^!$r!q1hqEFuDs-4sl$V2=jQUhfMrx{xRKr7+N>L^!Qw0iut4g-}4_vZtbMH0)HCkr@LcL%}2g`%FWsNTP($QYj>* z(IycY6aiEBfq9m*)&3XM*WD=mWx^Pmrt&#S=Ed4YbD(F7!HdgG0i%Oc4uV09RY^^U z2_nzBt$_6jfOd(u$sR@o*;VlVbXc~{#=-P!QM1yws79?J)Ts+tj~zgU;G8rm6p_A= z6TYGVUoWj7z;FM50{qyO1OydzxqwVu`Er+6B@ + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) brainfuckScene##id, +typedef enum { +#include "brainfuck_scene_config.h" + brainfuckSceneNum, +} brainfuckScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers brainfuck_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "brainfuck_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 "brainfuck_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 "brainfuck_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h new file mode 100644 index 000000000..0efc41641 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_config.h @@ -0,0 +1,6 @@ +ADD_SCENE(brainfuck, start, Start) +ADD_SCENE(brainfuck, file_select, FileSelect) +ADD_SCENE(brainfuck, file_create, FileCreate) +ADD_SCENE(brainfuck, dev_env, DevEnv) +ADD_SCENE(brainfuck, exec_env, ExecEnv) +ADD_SCENE(brainfuck, set_input, SetInput) \ No newline at end of file diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c new file mode 100644 index 000000000..475e9e573 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_dev.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_dev_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewDev); +} + +bool brainfuck_scene_dev_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_dev_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c new file mode 100644 index 000000000..d344f7271 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_exec.c @@ -0,0 +1,16 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_exec_env_on_enter(void* context) { + BFApp* app = context; + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextBox); +} + +bool brainfuck_scene_exec_env_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_exec_env_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c new file mode 100644 index 000000000..9f8885977 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_create.c @@ -0,0 +1,50 @@ +#include "../brainfuck_i.h" + +void file_name_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +char tmpName[64] = {}; +byte empty[1] = {0x00}; +void brainfuck_scene_file_create_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "New script name"); + text_input_set_result_callback( + text_input, file_name_text_input_callback, app, tmpName, 64, true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_file_create_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + UNUSED(app); + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + furi_string_cat_printf(app->BF_file_path, "/ext/brainfuck/%s.b", tmpName); + + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(app->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(app->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)empty, 1); + buffered_file_stream_close(stream); + + //scene_manager_next_scene(app->scene_manager, brainfuckSceneFileSelect); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_file_create_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c new file mode 100644 index 000000000..33c06ee81 --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_file_select.c @@ -0,0 +1,34 @@ +#include "../brainfuck_i.h" + +void brainfuck_scene_file_select_on_enter(void* context) { + BFApp* app = context; + + DialogsApp* dialogs = furi_record_open("dialogs"); + FuriString* path; + path = furi_string_alloc(); + furi_string_set(path, "/ext/brainfuck"); + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options(&browser_options, ".b", &I_bfico); + browser_options.base_path = "/ext/brainfuck"; + browser_options.hide_ext = false; + + bool selected = dialog_file_browser_show(dialogs, path, path, &browser_options); + + if(selected) { + furi_string_set(app->BF_file_path, path); + scene_manager_next_scene(app->scene_manager, brainfuckSceneDevEnv); + } else { + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneStart); + } +} + +bool brainfuck_scene_file_select_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void brainfuck_scene_file_select_on_exit(void* context) { + UNUSED(context); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c new file mode 100644 index 000000000..efb9237cb --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_set_input.c @@ -0,0 +1,35 @@ +#include "../brainfuck_i.h" + +void set_input_text_input_callback(void* context) { + BFApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, brainfuckCustomEventTextInputDone); +} + +void brainfuck_scene_set_input_on_enter(void* context) { + BFApp* app = context; + TextInput* text_input = app->text_input; + + text_input_set_header_text(text_input, "Edit input buffer"); + text_input_set_result_callback( + text_input, set_input_text_input_callback, app, app->inputBuffer, 64, true); + + view_dispatcher_switch_to_view(app->view_dispatcher, brainfuckViewTextInput); +} + +bool brainfuck_scene_set_input_on_event(void* context, SceneManagerEvent event) { + BFApp* app = context; + + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == brainfuckCustomEventTextInputDone) { + scene_manager_search_and_switch_to_previous_scene( + app->scene_manager, brainfuckSceneDevEnv); + } + } + return consumed; +} + +void brainfuck_scene_set_input_on_exit(void* context) { + BFApp* app = context; + scene_manager_search_and_switch_to_previous_scene(app->scene_manager, brainfuckSceneDevEnv); +} diff --git a/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c new file mode 100644 index 000000000..8eaaf751a --- /dev/null +++ b/applications/plugins/brainfuck/scenes/brainfuck_scene_start.c @@ -0,0 +1,55 @@ +#include "../brainfuck_i.h" +enum SubmenuIndex { + SubmenuIndexNew, + SubmenuIndexOpen, + SubmenuIndexAbout, +}; + +void brainfuck_scene_start_submenu_callback(void* context, uint32_t index) { + BFApp* brainfuck = context; + view_dispatcher_send_custom_event(brainfuck->view_dispatcher, index); +} +void brainfuck_scene_start_on_enter(void* context) { + BFApp* brainfuck = context; + + Submenu* submenu = brainfuck->submenu; + submenu_add_item( + submenu, "New", SubmenuIndexNew, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item( + submenu, "Open", SubmenuIndexOpen, brainfuck_scene_start_submenu_callback, brainfuck); + submenu_add_item( + submenu, "About", SubmenuIndexAbout, brainfuck_scene_start_submenu_callback, brainfuck); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(brainfuck->scene_manager, brainfuckSceneStart)); + view_dispatcher_switch_to_view(brainfuck->view_dispatcher, brainfuckViewMenu); +} + +bool brainfuck_scene_start_on_event(void* context, SceneManagerEvent event) { + BFApp* brainfuck = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexNew) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileCreate); + consumed = true; + } else if(event.event == SubmenuIndexOpen) { + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneFileSelect); + consumed = true; + } else if(event.event == SubmenuIndexAbout) { + text_box_set_text( + brainfuck->text_box, + "FlipperBrainfuck\n\nAn F0 brainfuck intepretor\nBy github.com/Nymda"); + scene_manager_next_scene(brainfuck->scene_manager, brainfuckSceneExecEnv); + consumed = true; + } + scene_manager_set_scene_state(brainfuck->scene_manager, brainfuckSceneStart, event.event); + } + + return consumed; +} + +void brainfuck_scene_start_on_exit(void* context) { + BFApp* brainfuck = context; + submenu_reset(brainfuck->submenu); +} diff --git a/applications/plugins/brainfuck/views/bf_dev_env.c b/applications/plugins/brainfuck/views/bf_dev_env.c new file mode 100644 index 000000000..c5f194500 --- /dev/null +++ b/applications/plugins/brainfuck/views/bf_dev_env.c @@ -0,0 +1,419 @@ +#include "bf_dev_env.h" +#include + +typedef struct BFDevEnv { + View* view; + DevEnvOkCallback callback; + void* context; + BFApp* appDev; +} BFDevEnv; + +typedef struct { + uint32_t row; + uint32_t col; +} BFDevEnvModel; + +typedef struct { + int up; + int down; + int left; + int right; +} bMapping; + +static bool bf_dev_process_up(BFDevEnv* devEnv); +static bool bf_dev_process_down(BFDevEnv* devEnv); +static bool bf_dev_process_left(BFDevEnv* devEnv); +static bool bf_dev_process_right(BFDevEnv* devEnv); +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event); + +BFApp* appDev; +FuriThread* workerThread; + +char bfChars[9] = {'<', '>', '[', ']', '+', '-', '.', ',', 0x00}; + +int selectedButton = 0; +int saveNotifyCountdown = 0; +int execCountdown = 0; + +char dspLine0[25] = {}; +char dspLine1[25] = {}; +char dspLine2[25] = {}; + +static bMapping buttonMappings[12] = { + {8, 8, 7, 1}, //0 + {8, 8, 0, 2}, //1 + {9, 9, 1, 3}, //2 + {9, 9, 2, 4}, //3 + {10, 10, 3, 5}, //4 + {10, 10, 4, 6}, //5 + {11, 11, 5, 7}, //6 + {11, 11, 6, 0}, //7 + + {0, 0, 11, 9}, //8 + {3, 3, 8, 10}, //9 + {5, 5, 9, 11}, //10 + {6, 6, 10, 8} //11 +}; + +#define BT_X 14 +#define BT_Y 14 +static void bf_dev_draw_button(Canvas* canvas, int x, int y, bool selected, const char* lbl) { + UNUSED(lbl); + + if(selected) { + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); + canvas_invert_color(canvas); + } else { + canvas_draw_rbox(canvas, x, y, BT_X, BT_Y, 3); + canvas_invert_color(canvas); + canvas_draw_rbox(canvas, x + 2, y - 1, BT_X - 2, BT_Y - 1, 3); + canvas_invert_color(canvas); + canvas_draw_rframe(canvas, x, y, BT_X, BT_Y, 3); + canvas_set_font(canvas, FontBatteryPercent); + canvas_draw_str_aligned( + canvas, x + (BT_X / 2), y + (BT_Y / 2) - 1, AlignCenter, AlignCenter, lbl); + } +} + +void bf_save_changes() { + //remove old file + Storage* storage = furi_record_open(RECORD_STORAGE); + storage_simply_remove(storage, furi_string_get_cstr(appDev->BF_file_path)); + + //save new file + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_WRITE, FSOM_CREATE_ALWAYS); + stream_write(stream, (const uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); +} + +static void bf_dev_draw_callback(Canvas* canvas, void* _model) { + UNUSED(_model); + + if(saveNotifyCountdown > 0) { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, "SAVED"); + saveNotifyCountdown--; + return; + } + + bf_dev_draw_button(canvas, 1, 36, (selectedButton == 0), "+"); //T 0 + bf_dev_draw_button(canvas, 17, 36, (selectedButton == 1), "-"); //T 1 + bf_dev_draw_button(canvas, 33, 36, (selectedButton == 2), "<"); //T 2 + bf_dev_draw_button(canvas, 49, 36, (selectedButton == 3), ">"); //T 3 + bf_dev_draw_button(canvas, 65, 36, (selectedButton == 4), "["); //B 0 + bf_dev_draw_button(canvas, 81, 36, (selectedButton == 5), "]"); //B 1 + bf_dev_draw_button(canvas, 97, 36, (selectedButton == 6), "."); //B 2 + bf_dev_draw_button(canvas, 113, 36, (selectedButton == 7), ","); //B 3 + + //backspace, input, run, save + canvas_draw_icon( + canvas, + 1, + 52, + (selectedButton == 8) ? &I_KeyBackspaceSelected_24x11 : &I_KeyBackspace_24x11); + canvas_draw_icon( + canvas, 45, 52, (selectedButton == 9) ? &I_KeyInputSelected_30x11 : &I_KeyInput_30x11); + canvas_draw_icon( + canvas, 77, 52, (selectedButton == 10) ? &I_KeyRunSelected_24x11 : &I_KeyRun_24x11); + canvas_draw_icon( + canvas, 103, 52, (selectedButton == 11) ? &I_KeySaveSelected_24x11 : &I_KeySave_24x11); + + if(saveNotifyCountdown > 0) { + canvas_draw_icon(canvas, 98, 54, &I_ButtonRightSmall_3x5); + saveNotifyCountdown--; + } + + //textbox + //grossly overcomplicated. not fixing it. + canvas_draw_rframe(canvas, 1, 1, 126, 33, 2); + canvas_set_font(canvas, FontBatteryPercent); + + int dbOffset = 0; + if(appDev->dataSize > 72) { + dbOffset = (appDev->dataSize - 72); + } + + memset(dspLine0, 0x00, 25); + memset(dspLine1, 0x00, 25); + memset(dspLine2, 0x00, 25); + + int tpM = 0; + int tp0 = 0; + int tp1 = 0; + int tp2 = 0; + + for(int p = dbOffset; p < appDev->dataSize; p++) { + if(tpM < 24 * 1) { + dspLine0[tp0] = appDev->dataBuffer[p]; + tp0++; + } else if(tpM < 24 * 2) { + dspLine1[tp1] = appDev->dataBuffer[p]; + tp1++; + } else if(tpM < 24 * 3) { + dspLine2[tp2] = appDev->dataBuffer[p]; + tp2++; + } + tpM++; + } + + canvas_draw_str_aligned(canvas, 3, 8, AlignLeft, AlignCenter, dspLine0); + canvas_draw_str_aligned(canvas, 3, 17, AlignLeft, AlignCenter, dspLine1); + canvas_draw_str_aligned(canvas, 3, 26, AlignLeft, AlignCenter, dspLine2); +} + +static bool bf_dev_input_callback(InputEvent* event, void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + bool consumed = false; + + if(event->type == InputTypeShort) { + if(event->key == InputKeyRight) { + consumed = bf_dev_process_right(devEnv); + } else if(event->key == InputKeyLeft) { + consumed = bf_dev_process_left(devEnv); + } else if(event->key == InputKeyUp) { + consumed = bf_dev_process_up(devEnv); + } else if(event->key == InputKeyDown) { + consumed = bf_dev_process_down(devEnv); + } + } else if(event->key == InputKeyOk) { + consumed = bf_dev_process_ok(devEnv, event); + } + + return consumed; +} + +static bool bf_dev_process_up(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].up; + return true; +} + +static bool bf_dev_process_down(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].down; + return true; +} + +static bool bf_dev_process_left(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].left; + return true; +} + +static bool bf_dev_process_right(BFDevEnv* devEnv) { + UNUSED(devEnv); + selectedButton = buttonMappings[selectedButton].right; + return true; +} + +static bool bf_dev_process_ok(BFDevEnv* devEnv, InputEvent* event) { + UNUSED(devEnv); + UNUSED(event); + + if(event->type != InputTypePress) { + return false; + } + + switch(selectedButton) { + case 0: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '+'; + appDev->dataSize++; + } + break; + } + + case 1: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '-'; + appDev->dataSize++; + } + break; + } + + case 2: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '<'; + appDev->dataSize++; + } + break; + } + + case 3: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '>'; + appDev->dataSize++; + } + break; + } + + case 4: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '['; + appDev->dataSize++; + } + break; + } + + case 5: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = ']'; + appDev->dataSize++; + } + break; + } + + case 6: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = '.'; + appDev->dataSize++; + } + break; + } + + case 7: { + if(appDev->dataSize < BF_INST_BUFFER_SIZE) { + appDev->dataBuffer[appDev->dataSize] = ','; + appDev->dataSize++; + } + break; + } + + case 8: { + if(appDev->dataSize > 0) { + appDev->dataSize--; + appDev->dataBuffer[appDev->dataSize] = (uint32_t)0x00; + } + break; + } + + case 9: { + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneSetInput); + break; + } + + case 10: { + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + bf_save_changes(); + + initWorker(appDev); + text_box_set_focus(appDev->text_box, TextBoxFocusEnd); + text_box_set_text(appDev->text_box, workerGetOutput()); + + workerThread = furi_thread_alloc_ex("Worker", 2048, (void*)beginWorker, NULL); + furi_thread_start(workerThread); + + scene_manager_next_scene(appDev->scene_manager, brainfuckSceneExecEnv); + break; + } + + case 11: { + bf_save_changes(); + saveNotifyCountdown = 3; + break; + } + } + + bool consumed = false; + return consumed; +} + +static void bf_dev_enter_callback(void* context) { + furi_assert(context); + BFDevEnv* devEnv = context; + + with_view_model( + devEnv->view, + BFDevEnvModel * model, + { + model->col = 0; + model->row = 0; + }, + true); + + appDev = devEnv->appDev; + selectedButton = 0; + + //exit the running thread if required + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + //clear the bf instruction buffer + memset(appDev->dataBuffer, 0x00, BF_INST_BUFFER_SIZE * sizeof(char)); + + //open the file + Storage* storage = furi_record_open(RECORD_STORAGE); + Stream* stream = buffered_file_stream_alloc(storage); + buffered_file_stream_open( + stream, furi_string_get_cstr(appDev->BF_file_path), FSAM_READ, FSOM_OPEN_EXISTING); + + //read into the buffer + appDev->dataSize = stream_size(stream); + stream_read(stream, (uint8_t*)appDev->dataBuffer, appDev->dataSize); + buffered_file_stream_close(stream); + + //replaces any invalid characters with an underscore. strips out newlines, comments, etc + for(int i = 0; i < appDev->dataSize; i++) { + if(!strchr(bfChars, appDev->dataBuffer[i])) { + appDev->dataBuffer[i] = '_'; + } + } + + //find the end of the file to begin editing + int tptr = 0; + while(appDev->dataBuffer[tptr] != 0x00) { + tptr++; + } + appDev->dataSize = tptr; +} + +BFDevEnv* bf_dev_env_alloc(BFApp* appDev) { + BFDevEnv* devEnv = malloc(sizeof(BFDevEnv)); + + devEnv->view = view_alloc(); + devEnv->appDev = appDev; + view_allocate_model(devEnv->view, ViewModelTypeLocking, sizeof(BFDevEnvModel)); + + with_view_model( + devEnv->view, + BFDevEnvModel * model, + { + model->col = 0; + model->row = 0; + }, + true); + + view_set_context(devEnv->view, devEnv); + view_set_draw_callback(devEnv->view, bf_dev_draw_callback); + view_set_input_callback(devEnv->view, bf_dev_input_callback); + view_set_enter_callback(devEnv->view, bf_dev_enter_callback); + return devEnv; +} + +void bf_dev_env_free(BFDevEnv* devEnv) { + if(getStatus() != 0) { + killThread(); + furi_thread_join(workerThread); + } + + furi_assert(devEnv); + view_free(devEnv->view); + free(devEnv); +} + +View* bf_dev_env_get_view(BFDevEnv* devEnv) { + furi_assert(devEnv); + return devEnv->view; +} diff --git a/applications/plugins/brainfuck/views/bf_dev_env.h b/applications/plugins/brainfuck/views/bf_dev_env.h new file mode 100644 index 000000000..31059544b --- /dev/null +++ b/applications/plugins/brainfuck/views/bf_dev_env.h @@ -0,0 +1,15 @@ +#pragma once +#include "../brainfuck_i.h" +#include + +typedef void (*DevEnvOkCallback)(InputType type, void* context); + +BFDevEnv* bf_dev_env_alloc(BFApp* application); + +void bf_dev_set_file_path(FuriString* path); + +void bf_dev_env_free(BFDevEnv* devEnv); + +View* bf_dev_env_get_view(BFDevEnv* devEnv); + +void bf_dev_env_set_ok(BFDevEnv* devEnv, DevEnvOkCallback callback, void* context); diff --git a/applications/plugins/brainfuck/worker.c b/applications/plugins/brainfuck/worker.c new file mode 100644 index 000000000..1b05ac3fd --- /dev/null +++ b/applications/plugins/brainfuck/worker.c @@ -0,0 +1,276 @@ +#include "worker.h" + +bool killswitch = false; + +int status = 0; //0: idle, 1: running, 2: failure + +char* inst = 0; +int instCount = 0; +int instPtr = 0; +int runOpCount = 0; + +char* wOutput = 0; +int wOutputPtr = 0; + +char* wInput = 0; +int wInputPtr = 0; + +uint8_t* bfStack = 0; +int stackPtr = 0; +int stackSize = BF_STACK_INITIAL_SIZE; +int stackSizeReal = 0; + +BFApp* wrkrApp = 0; + +void killThread() { + killswitch = true; +} + +bool validateInstPtr() { + if(instPtr > instCount || instPtr < 0) { + return false; + } + return true; +} + +bool validateStackPtr() { + if(stackPtr > stackSize || stackPtr < 0) { + return false; + } + return true; +} + +char* workerGetOutput() { + return wOutput; +} + +int getStackSize() { + return stackSizeReal; +} + +int getOpCount() { + return runOpCount; +} + +int getStatus() { + return status; +} + +void initWorker(BFApp* app) { + wrkrApp = app; + + //rebuild output + if(wOutput) { + free(wOutput); + } + wOutput = (char*)malloc(BF_OUTPUT_SIZE); + wOutputPtr = 0; + + //rebuild stack + if(bfStack) { + free(bfStack); + } + bfStack = (uint8_t*)malloc(BF_STACK_INITIAL_SIZE); + memset(bfStack, 0x00, BF_STACK_INITIAL_SIZE); + stackSize = BF_STACK_INITIAL_SIZE; + stackSizeReal = 0; + stackPtr = 0; + + //set instructions + inst = wrkrApp->dataBuffer; + instCount = wrkrApp->dataSize; + instPtr = 0; + runOpCount = 0; + + //set input + wInput = wrkrApp->inputBuffer; + wInputPtr = 0; + + //set status + status = 0; +} + +void rShift() { + runOpCount++; + stackPtr++; + if(!validateStackPtr()) { + status = 2; + return; + } + + while(stackPtr > stackSize) { + stackSize += BF_STACK_STEP_SIZE; + void* tmp = realloc(bfStack, stackSize); + + if(!tmp) { + status = 2; + return; + } + + memset((tmp + stackSize) - BF_STACK_STEP_SIZE, 0x00, BF_STACK_STEP_SIZE); + bfStack = (uint8_t*)tmp; + }; + if(stackPtr > stackSizeReal) { + stackSizeReal = stackPtr; + } +} + +void lShift() { + runOpCount++; + stackPtr--; + if(!validateStackPtr()) { + status = 2; + return; + } +} + +void inc() { + runOpCount++; + if(!validateStackPtr()) { + status = 2; + return; + } + bfStack[stackPtr]++; +} + +void dec() { + runOpCount++; + if(!validateStackPtr()) { + status = 2; + return; + } + bfStack[stackPtr]--; +} + +void print() { + runOpCount++; + wOutput[wOutputPtr] = bfStack[stackPtr]; + wOutputPtr++; + if(wOutputPtr > (BF_OUTPUT_SIZE - 1)) { + wOutputPtr = 0; + } +} + +void input() { + runOpCount++; + + bfStack[stackPtr] = (uint8_t)wInput[wInputPtr]; + if(wInput[wInputPtr] == 0x00 || wInputPtr >= 64) { + wInputPtr = 0; + } else { + wInputPtr++; + } +} + +void loop() { + runOpCount++; + if(bfStack[stackPtr] == 0) { + int loopCount = 1; + while(loopCount > 0) { + instPtr++; + if(!validateInstPtr()) { + status = 2; + return; + } + if(inst[instPtr] == '[') { + loopCount++; + } else if(inst[instPtr] == ']') { + loopCount--; + } + } + } +} + +void endLoop() { + runOpCount++; + if(bfStack[stackPtr] != 0) { + int loopCount = 1; + while(loopCount > 0) { + instPtr--; + if(!validateInstPtr()) { + status = 2; + return; + } + if(inst[instPtr] == ']') { + loopCount++; + } else if(inst[instPtr] == '[') { + loopCount--; + } + } + } +} + +static const NotificationSequence led_on = { + &message_blue_255, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_off = { + &message_green_0, + NULL, +}; + +void beginWorker() { + status = 1; + while(inst[instPtr] != 0x00) { + if(runOpCount % 500 == 0) { + text_box_set_text(wrkrApp->text_box, workerGetOutput()); + notification_message(wrkrApp->notifications, &led_on); + } + + if(status == 2) { + status = 0; + break; + } + if(killswitch) { + status = 0; + killswitch = false; + break; + } + switch(inst[instPtr]) { + case '>': + rShift(); + break; + case '<': + lShift(); + break; + + case '+': + inc(); + break; + + case '-': + dec(); + break; + + case '.': + print(); + break; + + case ',': + input(); + break; + + case '[': + loop(); + break; + + case ']': + endLoop(); + break; + + default: + break; + } + instPtr++; + if(!validateInstPtr()) { + status = 0; + break; + } + } + + notification_message(wrkrApp->notifications, &led_off); + text_box_set_text(wrkrApp->text_box, workerGetOutput()); + status = 0; +} \ No newline at end of file diff --git a/applications/plugins/brainfuck/worker.h b/applications/plugins/brainfuck/worker.h new file mode 100644 index 000000000..b12e364c3 --- /dev/null +++ b/applications/plugins/brainfuck/worker.h @@ -0,0 +1,9 @@ +#include "brainfuck_i.h" + +void initWorker(BFApp* application); +char* workerGetOutput(); +int getStackSize(); +int getOpCount(); +int getStatus(); +void beginWorker(); +void killThread(); \ No newline at end of file diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam new file mode 100644 index 000000000..9fddb52b0 --- /dev/null +++ b/applications/plugins/geigercounter/application.fam @@ -0,0 +1,13 @@ +App( + appid="Geiger_Coutner", + name="Geiger Counter", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipper_geiger_app", + cdefines=["APP_GEIGER"], + requires=[ + "gui", + ], + stack_size=1 * 1024, + fap_icon="geiger.png", + fap_category="GPIO", +) diff --git a/applications/plugins/geigercounter/flipper_geiger.c b/applications/plugins/geigercounter/flipper_geiger.c new file mode 100644 index 000000000..709db9a26 --- /dev/null +++ b/applications/plugins/geigercounter/flipper_geiger.c @@ -0,0 +1,227 @@ +// CC0 1.0 Universal (CC0 1.0) +// Public Domain Dedication +// https://github.com/nmrr + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SCREEN_SIZE_X 128 +#define SCREEN_SIZE_Y 64 + +// FOR J305 GEIGER TUBE +#define CONVERSION_FACTOR 0.0081 + +typedef enum { + EventTypeInput, + ClockEventTypeTick, + EventGPIO, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} EventApp; + +typedef struct { + uint32_t cps, cpm; + uint32_t line[SCREEN_SIZE_X / 2]; + float coef; + uint8_t data; +} mutexStruct; + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + + mutexStruct displayStruct; + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block((ValueMutex*)ctx); + memcpy(&displayStruct, geigerMutex, sizeof(mutexStruct)); + release_mutex((ValueMutex*)ctx, geigerMutex); + + char buffer[32]; + if(displayStruct.data == 0) + snprintf( + buffer, sizeof(buffer), "%ld cps - %ld cpm", displayStruct.cps, displayStruct.cpm); + else if(displayStruct.data == 1) + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f uSv/h", + displayStruct.cps, + ((double)displayStruct.cpm * (double)CONVERSION_FACTOR)); + else + snprintf( + buffer, + sizeof(buffer), + "%ld cps - %.2f mSv/y", + displayStruct.cps, + (((double)displayStruct.cpm * (double)CONVERSION_FACTOR)) * (double)8.76); + + for(int i = 0; i < SCREEN_SIZE_X; i += 2) { + float Y = SCREEN_SIZE_Y - (displayStruct.line[i / 2] * displayStruct.coef); + + canvas_draw_line(canvas, i, Y, i, SCREEN_SIZE_Y); + canvas_draw_line(canvas, i + 1, Y, i + 1, SCREEN_SIZE_Y); + } + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, 64, 10, AlignCenter, AlignBottom, buffer); +} + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + EventApp event = {.type = EventTypeInput, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void clock_tick(void* ctx) { + furi_assert(ctx); + + uint32_t randomNumber = furi_hal_random_get(); + randomNumber &= 0xFFF; + if(randomNumber == 0) randomNumber = 1; + + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, randomNumber, 50); + + FuriMessageQueue* queue = ctx; + EventApp event = {.type = ClockEventTypeTick}; + furi_message_queue_put(queue, &event, 0); +} + +static void gpiocallback(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* queue = ctx; + EventApp event = {.type = EventGPIO}; + furi_message_queue_put(queue, &event, 0); +} + +int32_t flipper_geiger_app() { + EventApp event; + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(EventApp)); + + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeInterruptFall, GpioPullUp, GpioSpeedVeryHigh); + furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 5, 50); + + mutexStruct mutexVal; + mutexVal.cps = 0; + mutexVal.cpm = 0; + for(int i = 0; i < SCREEN_SIZE_X / 2; i++) mutexVal.line[i] = 0; + mutexVal.coef = 1; + mutexVal.data = 0; + + uint32_t counter = 0; + + ValueMutex state_mutex; + init_mutex(&state_mutex, &mutexVal, sizeof(mutexVal)); + + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, draw_callback, &state_mutex); + view_port_input_callback_set(view_port, input_callback, event_queue); + + furi_hal_gpio_add_int_callback(&gpio_ext_pa7, gpiocallback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + FuriTimer* timer = furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, event_queue); + furi_timer_start(timer, 1000); + + // ENABLE 5V pin + furi_hal_power_enable_otg(); + + while(1) { + FuriStatus event_status = furi_message_queue_get(event_queue, &event, FuriWaitForever); + + uint8_t screenRefresh = 0; + + if(event_status == FuriStatusOk) { + if(event.type == EventTypeInput) { + if(event.input.key == InputKeyBack) { + break; + } else if(event.input.key == InputKeyOk && event.input.type == InputTypeShort) { + counter = 0; + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + geigerMutex->cps = 0; + geigerMutex->cpm = 0; + for(int i = 0; i < SCREEN_SIZE_X / 2; i++) geigerMutex->line[i] = 0; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if((event.input.key == InputKeyLeft && + event.input.type == InputTypeShort)) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + if(geigerMutex->data != 0) + geigerMutex->data--; + else + geigerMutex->data = 2; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if((event.input.key == InputKeyRight && + event.input.type == InputTypeShort)) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + if(geigerMutex->data != 2) + geigerMutex->data++; + else + geigerMutex->data = 0; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } + } else if(event.type == ClockEventTypeTick) { + mutexStruct* geigerMutex = (mutexStruct*)acquire_mutex_block(&state_mutex); + + for(int i = 0; i < SCREEN_SIZE_X / 2 - 1; i++) + geigerMutex->line[SCREEN_SIZE_X / 2 - 1 - i] = + geigerMutex->line[SCREEN_SIZE_X / 2 - 2 - i]; + + geigerMutex->line[0] = counter; + geigerMutex->cps = counter; + counter = 0; + + geigerMutex->cpm = geigerMutex->line[0]; + uint32_t max = geigerMutex->line[0]; + for(int i = 1; i < SCREEN_SIZE_X / 2; i++) { + if(i < 60) geigerMutex->cpm += geigerMutex->line[i]; + if(geigerMutex->line[i] > max) max = geigerMutex->line[i]; + } + + if(max > 0) + geigerMutex->coef = ((float)(SCREEN_SIZE_Y - 15)) / ((float)max); + else + geigerMutex->coef = 1; + + screenRefresh = 1; + release_mutex(&state_mutex, geigerMutex); + } else if(event.type == EventGPIO) { + counter++; + } + } + + if(screenRefresh == 1) view_port_update(view_port); + } + + furi_hal_power_disable_otg(); + + furi_hal_gpio_disable_int_callback(&gpio_ext_pa7); + furi_hal_gpio_remove_int_callback(&gpio_ext_pa7); + furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); + + furi_message_queue_free(event_queue); + delete_mutex(&state_mutex); + gui_remove_view_port(gui, view_port); + view_port_free(view_port); + furi_timer_free(timer); + furi_record_close(RECORD_GUI); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/geigercounter/geiger.png b/applications/plugins/geigercounter/geiger.png new file mode 100644 index 0000000000000000000000000000000000000000..d41e1915b6821ab19f982f1c99cad821d5a38abe GIT binary patch literal 8048 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 +#include + +#include +#include + +#include +#include + +#include "clock_app.h" + +/* + This is a modified version of the default clock app intended for use overnight + Up / Down control the displays brightness. Down at brightness 0 turns the notification LED on and off. +*/ + +int brightness = 5; +bool led = false; +NotificationApp* notif = 0; + +const NotificationMessage message_red_dim = { + .type = NotificationMessageTypeLedRed, + .data.led.value = 0xFF / 16, +}; + +const NotificationMessage message_red_off = { + .type = NotificationMessageTypeLedRed, + .data.led.value = 0x00, +}; + +static const NotificationSequence led_on = { + &message_red_dim, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_off = { + &message_red_off, + &message_do_not_reset, + NULL, +}; + +static const NotificationSequence led_reset = { + &message_red_0, + NULL, +}; + +void set_backlight_brightness(float brightness) { + notif->settings.display_brightness = brightness; + notification_message(notif, &sequence_display_backlight_on); +} + +void handle_up() { + if(brightness < 100) { + led = false; + notification_message(notif, &led_off); + brightness += 5; + } + set_backlight_brightness((float)(brightness / 100.f)); +} + +void handle_down() { + if(brightness > 0) { + brightness -= 5; + if(brightness == 0) { //trigger only on the first brightness 5 -> 0 transition + led = true; + notification_message(notif, &led_on); + } + } else if(brightness == 0) { //trigger on every down press afterwards + led = !led; + if(led) { + notification_message(notif, &led_on); + } else { + notification_message(notif, &led_off); + } + } + set_backlight_brightness((float)(brightness / 100.f)); +} + +static void clock_input_callback(InputEvent* input_event, FuriMessageQueue* event_queue) { + furi_assert(event_queue); + PluginEvent event = {.type = EventTypeKey, .input = *input_event}; + furi_message_queue_put(event_queue, &event, FuriWaitForever); +} + +static void clock_render_callback(Canvas* const canvas, void* ctx) { + //canvas_clear(canvas); + //canvas_set_color(canvas, ColorBlack); + + //avoids a bug with the brightness being reverted after the backlight-off period + set_backlight_brightness((float)(brightness / 100.f)); + + ClockState* state = ctx; + if(furi_mutex_acquire(state->mutex, 200) != FuriStatusOk) { + //FURI_LOG_D(TAG, "Can't obtain mutex, requeue render"); + PluginEvent event = {.type = EventTypeTick}; + furi_message_queue_put(state->event_queue, &event, 0); + return; + } + + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + char time_string[TIME_LEN]; + char date_string[DATE_LEN]; + char meridian_string[MERIDIAN_LEN]; + char timer_string[20]; + + if(state->time_format == LocaleTimeFormat24h) { + snprintf( + time_string, TIME_LEN, CLOCK_TIME_FORMAT, curr_dt.hour, curr_dt.minute, curr_dt.second); + } else { + bool pm = curr_dt.hour > 12; + bool pm12 = curr_dt.hour >= 12; + snprintf( + time_string, + TIME_LEN, + CLOCK_TIME_FORMAT, + pm ? curr_dt.hour - 12 : curr_dt.hour, + curr_dt.minute, + curr_dt.second); + + snprintf( + meridian_string, + MERIDIAN_LEN, + MERIDIAN_FORMAT, + pm12 ? MERIDIAN_STRING_PM : MERIDIAN_STRING_AM); + } + + if(state->date_format == LocaleDateFormatYMD) { + snprintf( + date_string, DATE_LEN, CLOCK_ISO_DATE_FORMAT, curr_dt.year, curr_dt.month, curr_dt.day); + } else if(state->date_format == LocaleDateFormatMDY) { + snprintf( + date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.month, curr_dt.day, curr_dt.year); + } else { + snprintf( + date_string, DATE_LEN, CLOCK_RFC_DATE_FORMAT, curr_dt.day, curr_dt.month, curr_dt.year); + } + + bool timer_running = state->timer_running; + uint32_t timer_start_timestamp = state->timer_start_timestamp; + uint32_t timer_stopped_seconds = state->timer_stopped_seconds; + + furi_mutex_release(state->mutex); + + canvas_set_font(canvas, FontBigNumbers); + + if(timer_start_timestamp != 0) { + int32_t elapsed_secs = timer_running ? (curr_ts - timer_start_timestamp) : + timer_stopped_seconds; + snprintf(timer_string, 20, "%.2ld:%.2ld", elapsed_secs / 60, elapsed_secs % 60); + canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignCenter, time_string); // DRAW TIME + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignTop, timer_string); // DRAW TIMER + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 20, AlignCenter, AlignTop, date_string); // DRAW DATE + elements_button_left(canvas, "Reset"); + } else { + canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, time_string); + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 65, 17, AlignCenter, AlignCenter, date_string); + + if(state->time_format == LocaleTimeFormat12h) + canvas_draw_str_aligned(canvas, 64, 47, AlignCenter, AlignCenter, meridian_string); + } + if(timer_running) { + elements_button_center(canvas, "Stop"); + } else if(timer_start_timestamp != 0 && !timer_running) { + elements_button_center(canvas, "Start"); + } +} + +static void clock_state_init(ClockState* const state) { + state->time_format = locale_get_time_format(); + + state->date_format = locale_get_date_format(); + + //FURI_LOG_D(TAG, "Time format: %s", state->settings.time_format == H12 ? "12h" : "24h"); + //FURI_LOG_D(TAG, "Date format: %s", state->settings.date_format == Iso ? "ISO 8601" : "RFC 5322"); + //furi_hal_rtc_get_datetime(&state->datetime); +} + +// Runs every 1000ms by default +static void clock_tick(void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + PluginEvent event = {.type = EventTypeTick}; + // It's OK to loose this event if system overloaded + furi_message_queue_put(event_queue, &event, 0); +} + +void timer_start_stop(ClockState* plugin_state) { + // START/STOP TIMER + FuriHalRtcDateTime curr_dt; + furi_hal_rtc_get_datetime(&curr_dt); + uint32_t curr_ts = furi_hal_rtc_datetime_to_timestamp(&curr_dt); + + if(plugin_state->timer_running) { + // Update stopped seconds + plugin_state->timer_stopped_seconds = curr_ts - plugin_state->timer_start_timestamp; + } else { + if(plugin_state->timer_start_timestamp == 0) { + // Set starting timestamp if this is first time + plugin_state->timer_start_timestamp = curr_ts; + } else { + // Timer was already running, need to slightly readjust so we don't + // count the intervening time + plugin_state->timer_start_timestamp = curr_ts - plugin_state->timer_stopped_seconds; + } + } + plugin_state->timer_running = !plugin_state->timer_running; +} + +void timer_reset_seconds(ClockState* plugin_state) { + if(plugin_state->timer_start_timestamp != 0) { + // Reset seconds + plugin_state->timer_running = false; + plugin_state->timer_start_timestamp = 0; + plugin_state->timer_stopped_seconds = 0; + } +} + +int32_t clock_app(void* p) { + UNUSED(p); + ClockState* plugin_state = malloc(sizeof(ClockState)); + + plugin_state->event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent)); + if(plugin_state->event_queue == NULL) { + FURI_LOG_E(TAG, "Cannot create event queue"); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Event queue created"); + + plugin_state->mutex = furi_mutex_alloc(FuriMutexTypeNormal); + if(plugin_state->mutex == NULL) { + FURI_LOG_E(TAG, "Cannot create mutex"); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Mutex created"); + + clock_state_init(plugin_state); + + // Set system callbacks + ViewPort* view_port = view_port_alloc(); + view_port_draw_callback_set(view_port, clock_render_callback, plugin_state); + view_port_input_callback_set(view_port, clock_input_callback, plugin_state->event_queue); + + FuriTimer* timer = + furi_timer_alloc(clock_tick, FuriTimerTypePeriodic, plugin_state->event_queue); + + if(timer == NULL) { + FURI_LOG_E(TAG, "Cannot create timer"); + furi_mutex_free(plugin_state->mutex); + furi_message_queue_free(plugin_state->event_queue); + free(plugin_state); + return 255; + } + //FURI_LOG_D(TAG, "Timer created"); + + // Open GUI and register view_port + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + furi_timer_start(timer, furi_kernel_get_tick_frequency()); + //FURI_LOG_D(TAG, "Timer started"); + + notif = furi_record_open(RECORD_NOTIFICATION); + float tmpBrightness = notif->settings.display_brightness; + + notification_message(notif, &sequence_display_backlight_enforce_on); + notification_message(notif, &led_off); + + // Main loop + PluginEvent event; + for(bool processing = true; processing;) { + FuriStatus event_status = furi_message_queue_get(plugin_state->event_queue, &event, 100); + + if(event_status != FuriStatusOk) continue; + + if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue; + // press events + if(event.type == EventTypeKey) { + if(event.input.type == InputTypeLong) { + switch(event.input.key) { + case InputKeyLeft: + // Reset seconds + timer_reset_seconds(plugin_state); + break; + case InputKeyOk: + // Toggle timer + timer_start_stop(plugin_state); + break; + case InputKeyBack: + // Exit the plugin + processing = false; + break; + default: + break; + } + } else if(event.input.type == InputTypeShort) { + switch(event.input.key) { + case InputKeyUp: + handle_up(); + break; + case InputKeyDown: + handle_down(); + break; + default: + break; + } + } + } /*else if(event.type == EventTypeTick) { + furi_hal_rtc_get_datetime(&plugin_state->datetime); + }*/ + + view_port_update(view_port); + furi_mutex_release(plugin_state->mutex); + } + + furi_timer_free(timer); + view_port_enabled_set(view_port, false); + gui_remove_view_port(gui, view_port); + furi_record_close(RECORD_GUI); + view_port_free(view_port); + furi_message_queue_free(plugin_state->event_queue); + furi_mutex_free(plugin_state->mutex); + free(plugin_state); + + set_backlight_brightness(tmpBrightness); + notification_message(notif, &sequence_display_backlight_enforce_auto); + notification_message(notif, &led_reset); + + return 0; +} \ No newline at end of file diff --git a/applications/plugins/nightstand/clock_app.h b/applications/plugins/nightstand/clock_app.h new file mode 100644 index 000000000..693bdfac0 --- /dev/null +++ b/applications/plugins/nightstand/clock_app.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +#define TAG "Clock" + +#define CLOCK_ISO_DATE_FORMAT "%.4d-%.2d-%.2d" +#define CLOCK_RFC_DATE_FORMAT "%.2d-%.2d-%.4d" +#define CLOCK_TIME_FORMAT "%.2d:%.2d:%.2d" + +#define MERIDIAN_FORMAT "%s" +#define MERIDIAN_STRING_AM "AM" +#define MERIDIAN_STRING_PM "PM" + +#define TIME_LEN 12 +#define DATE_LEN 14 +#define MERIDIAN_LEN 3 + +typedef enum { + EventTypeTick, + EventTypeKey, +} EventType; + +typedef struct { + EventType type; + InputEvent input; +} PluginEvent; + +typedef struct { + LocaleDateFormat date_format; + LocaleTimeFormat time_format; + FuriHalRtcDateTime datetime; + FuriMutex* mutex; + FuriMessageQueue* event_queue; + uint32_t timer_start_timestamp; + uint32_t timer_stopped_seconds; + bool timer_running; +} ClockState; diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam new file mode 100644 index 000000000..27e73a0ce --- /dev/null +++ b/applications/plugins/pomodoro/application.fam @@ -0,0 +1,12 @@ +App( + appid="Pomodoro2", + name="Pomodoro 2", + apptype=FlipperAppType.EXTERNAL, + entry_point="flipp_pomodoro_app", + requires=["gui", "notification", "dolphin"], + stack_size=1 * 1024, + fap_category="Tools", + fap_icon_assets="images", + fap_icon="flipp_pomodoro_10.png", + fap_icon_assets_symbol="flipp_pomodoro", +) \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_10.png b/applications/plugins/pomodoro/flipp_pomodoro_10.png new file mode 100644 index 0000000000000000000000000000000000000000..977d16a584f63307ceb028a48f57713402abc520 GIT binary patch literal 157 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>3HNky4ABT~ zo#4oKK!Jm0_y2m6v$J|4wU)(+%sJ4oYSlc8Nnua6+`q%bGDG3H*bnyJge())*BAQu z?-*^XlzwJ?r0uexhV_H}+Gscene_manager); +}; + +static void flipp_pomodoro_app_tick_event_callback(void* ctx) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + scene_manager_handle_custom_event(app->scene_manager, FlippPomodoroAppCustomEventTimerTick); +}; + +static bool flipp_pomodoro_app_custom_event_callback(void* ctx, uint32_t event) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + switch(event) { + case FlippPomodoroAppCustomEventStageSkip: + flipp_pomodoro__toggle_stage(app->state); + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); + return CustomEventConsumed; + case FlippPomodoroAppCustomEventStageComplete: + if(flipp_pomodoro__get_stage(app->state) == FlippPomodoroStageFocus) { + // REGISTER a deed on work stage complete to get an acheivement + DOLPHIN_DEED(DolphinDeedPluginGameWin); + }; + + flipp_pomodoro__toggle_stage(app->state); + notification_message( + app->notification_app, + stage_start_notification_sequence_map[flipp_pomodoro__get_stage(app->state)]); + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStateUpdated); + return CustomEventConsumed; + default: + break; + } + return scene_manager_handle_custom_event(app->scene_manager, event); +}; + +FlippPomodoroApp* flipp_pomodoro_app_alloc() { + FlippPomodoroApp* app = malloc(sizeof(FlippPomodoroApp)); + app->state = flipp_pomodoro__new(); + + app->scene_manager = scene_manager_alloc(&flipp_pomodoro_scene_handlers, app); + app->gui = furi_record_open(RECORD_GUI); + app->notification_app = furi_record_open(RECORD_NOTIFICATION); + + app->view_dispatcher = view_dispatcher_alloc(); + 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, flipp_pomodoro_app_custom_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flipp_pomodoro_app_tick_event_callback, 1000); + view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flipp_pomodoro_app_back_event_callback); + + app->timer_view = flipp_pomodoro_view_timer_alloc(); + + view_dispatcher_add_view( + app->view_dispatcher, + FlippPomodoroAppViewTimer, + flipp_pomodoro_view_timer_get_view(app->timer_view)); + + scene_manager_next_scene(app->scene_manager, FlippPomodoroSceneTimer); + + return app; +}; + +void flipp_pomodoro_app_free(FlippPomodoroApp* app) { + view_dispatcher_remove_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + view_dispatcher_free(app->view_dispatcher); + scene_manager_free(app->scene_manager); + flipp_pomodoro_view_timer_free(app->timer_view); + flipp_pomodoro__destroy(app->state); + free(app); + furi_record_close(RECORD_GUI); + furi_record_close(RECORD_NOTIFICATION); +}; + +int32_t flipp_pomodoro_app(void* p) { + UNUSED(p); + FlippPomodoroApp* app = flipp_pomodoro_app_alloc(); + + view_dispatcher_run(app->view_dispatcher); + + flipp_pomodoro_app_free(app); + + return 0; +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app.h b/applications/plugins/pomodoro/flipp_pomodoro_app.h new file mode 100644 index 000000000..54102e1f3 --- /dev/null +++ b/applications/plugins/pomodoro/flipp_pomodoro_app.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "views/flipp_pomodoro_timer_view.h" + +#include "modules/flipp_pomodoro.h" + +typedef enum { + // Reserve first 100 events for button types and indexes, starting from 0 + FlippPomodoroAppCustomEventStageSkip = 100, + FlippPomodoroAppCustomEventStageComplete, // By Expiration + FlippPomodoroAppCustomEventTimerTick, + FlippPomodoroAppCustomEventStateUpdated, +} FlippPomodoroAppCustomEvent; + +typedef struct { + SceneManager* scene_manager; + ViewDispatcher* view_dispatcher; + Gui* gui; + NotificationApp* notification_app; + FlippPomodoroTimerView* timer_view; + FlippPomodoroState* state; +} FlippPomodoroApp; + +typedef enum { + FlippPomodoroAppViewTimer, +} FlippPomodoroAppView; \ No newline at end of file diff --git a/applications/plugins/pomodoro/flipp_pomodoro_app_i.h b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h new file mode 100644 index 000000000..492ef9c2d --- /dev/null +++ b/applications/plugins/pomodoro/flipp_pomodoro_app_i.h @@ -0,0 +1,31 @@ +#pragma once + +#define FURI_DEBUG 1 + +/** + * Index of dependencies for the main app + */ + +// Platform Imports + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// App resource imports + +#include "helpers/time.h" +#include "helpers/notifications.h" +#include "modules/flipp_pomodoro.h" +#include "flipp_pomodoro_app.h" +#include "scenes/flipp_pomodoro_scene.h" +#include "views/flipp_pomodoro_timer_view.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" diff --git a/applications/plugins/pomodoro/helpers/debug.h b/applications/plugins/pomodoro/helpers/debug.h new file mode 100644 index 000000000..13b8f2998 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/debug.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +#define TAG "FlippPomodoro" \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.c b/applications/plugins/pomodoro/helpers/notifications.c new file mode 100644 index 000000000..388a3f11d --- /dev/null +++ b/applications/plugins/pomodoro/helpers/notifications.c @@ -0,0 +1,49 @@ +#include + +const NotificationSequence work_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_b5, + &message_delay_250, + + &message_note_d5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_green_255, + &message_delay_1000, + &message_green_0, + &message_delay_250, + &message_green_255, + &message_delay_1000, + + NULL, +}; + +const NotificationSequence rest_start_notification = { + &message_display_backlight_on, + + &message_vibro_on, + + &message_note_d5, + &message_delay_250, + + &message_note_b5, + &message_delay_250, + + &message_sound_off, + &message_vibro_off, + + &message_red_255, + &message_delay_1000, + &message_red_0, + &message_delay_250, + &message_red_255, + &message_delay_1000, + + NULL, +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/helpers/notifications.h b/applications/plugins/pomodoro/helpers/notifications.h new file mode 100644 index 000000000..c6cd0428f --- /dev/null +++ b/applications/plugins/pomodoro/helpers/notifications.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../modules/flipp_pomodoro.h" +#include + +extern const NotificationSequence work_start_notification; +extern const NotificationSequence rest_start_notification; + +/// @brief Defines a notification sequence that should indicate start of specific pomodoro stage. +const NotificationSequence* stage_start_notification_sequence_map[] = { + [FlippPomodoroStageFocus] = &work_start_notification, + [FlippPomodoroStageRest] = &rest_start_notification, + [FlippPomodoroStageLongBreak] = &rest_start_notification, +}; diff --git a/applications/plugins/pomodoro/helpers/time.c b/applications/plugins/pomodoro/helpers/time.c new file mode 100644 index 000000000..7fb0d13c2 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/time.c @@ -0,0 +1,20 @@ +#include +#include +#include "time.h" + +const int TIME_SECONDS_IN_MINUTE = 60; +const int TIME_MINUTES_IN_HOUR = 60; + +uint32_t time_now() { + return furi_hal_rtc_get_timestamp(); +}; + +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end) { + const uint32_t duration_seconds = end - begin; + + uint32_t minutes = (duration_seconds / TIME_MINUTES_IN_HOUR) % TIME_MINUTES_IN_HOUR; + uint32_t seconds = duration_seconds % TIME_SECONDS_IN_MINUTE; + + return ( + TimeDifference){.total_seconds = duration_seconds, .minutes = minutes, .seconds = seconds}; +}; diff --git a/applications/plugins/pomodoro/helpers/time.h b/applications/plugins/pomodoro/helpers/time.h new file mode 100644 index 000000000..7a7d90bf2 --- /dev/null +++ b/applications/plugins/pomodoro/helpers/time.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include + +extern const int TIME_SECONDS_IN_MINUTE; +extern const int TIME_MINUTES_IN_HOUR; + +/// @brief Container for a time period +typedef struct { + uint8_t seconds; + uint8_t minutes; + uint32_t total_seconds; +} TimeDifference; + +/// @brief Time by the moment of calling +/// @return A timestamp(seconds percision) +uint32_t time_now(); + +/// @brief Calculates difference between two provided timestamps +/// @param begin - start timestamp of the period +/// @param end - end timestamp of the period to measure +/// @return TimeDifference struct +TimeDifference time_difference_seconds(uint32_t begin, uint32_t end); diff --git a/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png b/applications/plugins/pomodoro/images/flipp_pomodoro_focus_64/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..7ead27c4e2d36b8155c088954b50e87cfc2a7e0c GIT binary patch literal 1242 zcmV<01SR{4P)Px(lu1NERCr$9TjA21Fbr$o|DnGf?7|sWvg8oxx9!hvB?McRWQTU|*X#B3TJY!R z=kGS}_j`J`**&)Vw&zxVv!J$UF9E=U9-ljCWYA`Sm<8by0OO%sk~!fH6WG0Re+S61 zro#jZKo?O|MMX;O1P z3~E)$ATQ`>cON!#t#s#j>6ps#`V!VDre6XW|C9g0fOlQKs96>eo(;7XjwJy z0AU9V0nnKXG6NW;l}@wqq4ky7jyRZs#yH~|YZ6p6N7va2PXQJaXKhh+mkI>v0ae9k zl1DUV70sT2IuZkgNh`_=G4gcZkO0&86#%j+F&Uut#59kRbLEoflfwhS5bna)vFBN&lf>H~CCmMCAw$0$?QOv_4dA z#EhIrY>CKhcMq6No+X_}+{*MA^>e}%3#yZ$YVpTmXch!C&m&CPV8n)+stqDKBV&xd z!{Ve2U;_jKN*SY*B&!rN&&o7gGC&fjDBM+yk^|CZu3YtRBtrxMD$@4Z2Lo8Z%=DCW z;(ki2*(*9Sh+RZ6;M74P0~ktZf^(UG0AQfyuHgcJx=DOSWrPOe+;Z_l3(=z$Gp_QqX4RAiXk(14(NEmDL2#wkl`_7$y)9vjRr2%(hwUENNj*Z z3~$W=$|GDeM7^GYn7xBZs$#1=3*0>kGB#E6QPoCCWi}w`Krq;8kWr~y>8j$4{_Zo} z<8U>NE;$aXfSLZfe&KkNiY5BX4&aLS0Zv;(!CwWyNjCe-M;t2v$Q4$DPj~9lTc@Er;@X4modNz;I}2ex6TzF;3Pl#^wY+tVtWjLyQ@Dtw>$#C6Upc4 zF;9ZDRo^9nT#>t|&sy*pc)l)yJFCwoOq4V$!0CmEi<&}}o$fmKskIE)>U;qYU|9lc zic^6-k$mnb>g7!(KkJ*J8U5Z#eKg;*H$p0Qs|(Dq;wb>CgBbdr;nHEqo%f$ezIq0G zlVo{-t8v!mo=ASghzjBs>DXh{9MFBR3$$9ts_k|(%L7ypu_DTJ+`T&Mx~=bIqN^D2 z?ZeLWSs?ya_)7Jj)P9E@YVAn^E{cqDR{&Ij-gO=RKterCeI=&uAJ=~aU>`r1pABk8 z0+o5WAa)KuXzguXz)5ZvYz2<Px(c}YY;RCr$PThVgdFbKT+|Bt?ooskD~yRaZj?R1itCT9u5vMfkV-mll|^Lps( z^Z7CLe!trmyEcw}$9toHkyD4K(*Q81$L9(f9<&Y+9T27g7%x47^Z{3x!1h}E3Xo%s z%7haD1vy49>lDgjMoYzRx2zNp-beH^PCgm5wS|~fVU)^*G6RNTy8i0aVf{kcvor*vjHqiP?SwK)31AGlBS&UjNE z*{~e~fuVrF-hX2N6AmSKv_=h@?M3w-fsy$M{dr;#ieB99K6Y!K=O?&~|V<3VBB zXP_W0upNXkVeGp!piBEKK(#FV`%!C)n1R|Y1LyX+XFwKE=Q*lRBy^~*Gxd)c@YV9n z+2JY?I|_G#Q}$%%0pA6(aW~5>6gzfZHW;7;GU};XE=&-!y9Aa&R4#TcYn_N;=Y@zG zJZgK&cE$j;ZjquR@kI7KFxJ+&zRG|M1b?xS4K;|)QNU4oL;x`MPB8%0Zd4u_uiMqsD0XfAp=$d5VbG=%m<2u zaES)60mjxoa!wUOTZd?mIxR+>L1n;A6NUyv3}EV-jlnEakznp*z~LX&07bkI0M(#T zt5xb9bq$pPbP`m0wleT2Wr0e|?^EmFgU$va*wNHEU^cTX)1F0kR^x^J_^_qu&2kM`nSC~4 zA|M$U?K?p=TdM$2j_%$^IFb5WVU;=x0IPzxCp+}L68P1cE5~6a=-oY^-5=PKU`qtE zc|HSUW%jdodsd*ILk7$qTy_K>y(7C-y2Yo=0Tnjv2;P>Nr#iYtYLx)mq)b~o0T|S_ z!o3s#1r%lYP-?ic1|?=%1S<~gR(>TBYCFIEvh+;$c2mETAP5{9gzUoXe}A&IPdU2E z4*Ug)>}3^DB3mPx&^+`lQRCr$9TFY|7KnSb<|D)4Mrt85Nki@Rzq`CCqeIbO9~JfI#>N0A^{7XkV>=I{+v{C^+@`sci1l02DlW zt_B~Czl{MXxU0(IqZr^bk_C;xVwn)FQ!?N=A^NSf4yx*}0%OFr0}ut91?tDH1jGW4 z`h1pog-Op8zntJq5Cf3eHLRmU8i1IyXZX)4reLk?0;}oigUHrb!8)*PB>;@`Vg(D) z8$D;8<0WnIDkfyKM!{C+xdecBG69tZV%*mPU_nL4 z^);)2;InSoz-j^O_0@4cjuQYQQ;~`3fZ$8P9(WcER1RMKqw}W>m<5380xF;uFlBD# zNe(YWR%(GQ1I7b%#;}4}f%WPFZ2MzSw`(yJKhnZ06lS2sr1Em|o9IVQ| zcaK@iWA-V_ay0-<)+=M;4V2dm-vMTYu=PG5qb++FCTs5!#1;T3OQ;hdt+6bMbYSM9 z7Pi4giNkMiBD2FV1hu?K3m8UaF}4_>Za6z0fznCfZsb;rU^nx2UL|x{H|T=UVgS1t z+Yx;5mGavTqB83iAD|x6kIzJK6%&>+U>w$FumixqU;r{ZsVY04Uol{;f!gX@onR(r ztZqcWWo5y%RkZJtZ-hc|nY^)8PH;u$Pi;`GRfueH1x9lR3r2?D<^wuy3uQkNMizkT ztFcSFz$_cG`3MaAoB_#>=?Y=m24~p702r;$%*zKy61+zuuiz10d#O3R^B7Y-aw+38Dit|1e=Qmr)0kM1UiHm#yyU~9b_Y0vbRh0}1N>=~?002ovPDHLkV1jt$ BPx&@<~KNRCr$1TG5W=FbLcE|Bs$ktIdc~z&1&GH}kS9H6>t-A#Gpp_xt_x;P3T% z{XDtl*f0J2GyqRv?SswsJl4IQ24EKh*q}Waj{|Tx&~^6a{9ieBLH#ETKtUjI(DjP$ zR(n2no-Too1|T6rL1*`$Z2SfQD$u&%44YpMK!;KGo)Ulq-RSpZAD;~n-Twg~3N{PW zul;5~jufAh;7kw$kQo~&Q@gG&)d000&kER#`fn3K76i18?e|rSivaTbE@i+6RdC=@ zGq0{kwdXTI zrZ=w~LYRSz0mUW+t%q~K03mX2CcFMLr-*CD0MrE}c8v5hqNNyS0=K8SNJ}baGX5g{ zIrq0BI8zFt!u|;&3M3l35bKr#z#yfrVc1fN6X*tvea2_`IE*%CGouFYgRzq$@~NCd zs21QXWD{K0Ce`}R)<@u0t#S(khEX&%cBV3`#c1%MGv4;Z?~XSv(one2bM z0K0f5coev8iKzdp{w~!3m1e9cM&L5{s{Y*cr6YoD2ABY{x{sa*L=zw@ys0Mb0$^61 zM@iTKBYhaxyG&?{V5QjJX{P?!u3XcL&Ny#vSf8Z*JCmSy z+S6KbAOaf!%p{2QVd)$&JN~NoX9BB~?@|D4%TluF%Yk_%Zo%?Hk_15Xa#V3XD+eTG z)yEh#eUsO45&&5nL;=|A)dR{7Kph5T{-8K+C4dC38hhEyc^e&&qRymh$XT+{2NDRi zXzJ?*&+eswB$e=vHpm9GdL0AO?JblCpku_UzY>&^DaCHJ*2gzp0@*aiILv^}*ik3d yO9^*MAaly<02ACU1~7Yn5?6gB)j|K;?)e3CDgm3;8 +#include +#include "../helpers/time.h" +#include "flipp_pomodoro.h" + +PomodoroStage stages_sequence[] = { + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + + FlippPomodoroStageFocus, + FlippPomodoroStageLongBreak, +}; + +char* current_stage_label[] = { + [FlippPomodoroStageFocus] = "Continue focus for:", + [FlippPomodoroStageRest] = "Keep rest for:", + [FlippPomodoroStageLongBreak] = "Long Break for:", +}; + +char* next_stage_label[] = { + [FlippPomodoroStageFocus] = "Focus", + [FlippPomodoroStageRest] = "Short Break", + [FlippPomodoroStageLongBreak] = "Long Break", +}; + +PomodoroStage flipp_pomodoro__stage_by_index(int index) { + const int one_loop_size = sizeof(stages_sequence); + return stages_sequence[index % one_loop_size]; +} + +void flipp_pomodoro__toggle_stage(FlippPomodoroState* state) { + furi_assert(state); + state->current_stage_index = state->current_stage_index + 1; + state->started_at_timestamp = time_now(); +}; + +PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state) { + furi_assert(state); + return flipp_pomodoro__stage_by_index(state->current_stage_index); +}; + +char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state) { + furi_assert(state); + return current_stage_label[flipp_pomodoro__get_stage(state)]; +}; + +char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state) { + furi_assert(state); + return next_stage_label[flipp_pomodoro__stage_by_index(state->current_stage_index + 1)]; +}; + +void flipp_pomodoro__destroy(FlippPomodoroState* state) { + furi_assert(state); + free(state); +}; + +uint32_t flipp_pomodoro__current_stage_total_duration(FlippPomodoroState* state) { + const int32_t stage_duration_seconds_map[] = { + [FlippPomodoroStageFocus] = 25 * TIME_SECONDS_IN_MINUTE, + [FlippPomodoroStageRest] = 5 * TIME_SECONDS_IN_MINUTE, + [FlippPomodoroStageLongBreak] = 30 * TIME_SECONDS_IN_MINUTE, + }; + + return stage_duration_seconds_map[flipp_pomodoro__get_stage(state)]; +}; + +uint32_t flipp_pomodoro__stage_expires_timestamp(FlippPomodoroState* state) { + return state->started_at_timestamp + flipp_pomodoro__current_stage_total_duration(state); +}; + +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state) { + const uint32_t stage_ends_at = flipp_pomodoro__stage_expires_timestamp(state); + return time_difference_seconds(time_now(), stage_ends_at); +}; + +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state) { + const uint32_t expired_by = flipp_pomodoro__stage_expires_timestamp(state); + const uint8_t seamless_change_span_seconds = 1; + return (time_now() - seamless_change_span_seconds) >= expired_by; +}; + +FlippPomodoroState* flipp_pomodoro__new() { + FlippPomodoroState* state = malloc(sizeof(FlippPomodoroState)); + const uint32_t now = time_now(); + state->started_at_timestamp = now; + state->current_stage_index = 0; + return state; +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/modules/flipp_pomodoro.h b/applications/plugins/pomodoro/modules/flipp_pomodoro.h new file mode 100644 index 000000000..251a77469 --- /dev/null +++ b/applications/plugins/pomodoro/modules/flipp_pomodoro.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include "../helpers/time.h" + +/// @brief Options of pomodoro stages +typedef enum { + FlippPomodoroStageFocus, + FlippPomodoroStageRest, + FlippPomodoroStageLongBreak, +} PomodoroStage; + +/// @brief State of the pomodoro timer +typedef struct { + PomodoroStage stage; + uint8_t current_stage_index; + uint32_t started_at_timestamp; +} FlippPomodoroState; + +/// @brief Generates initial state +/// @returns A new pre-populated state for pomodoro timer +FlippPomodoroState* flipp_pomodoro__new(); + +/// @brief Extract current stage of pomodoro +/// @param state - pointer to the state of pomorodo +/// @returns Current stage value +PomodoroStage flipp_pomodoro__get_stage(FlippPomodoroState* state); + +/// @brief Destroys state of timer and it's dependencies +void flipp_pomodoro__destroy(FlippPomodoroState* state); + +/// @brief Get remaining stage time. +/// @param state - pointer to the state of pomorodo +/// @returns Time difference to the end of current stage +TimeDifference flipp_pomodoro__stage_remaining_duration(FlippPomodoroState* state); + +/// @brief Label of currently active stage +/// @param state - pointer to the state of pomorodo +/// @returns A string that explains current stage +char* flipp_pomodoro__current_stage_label(FlippPomodoroState* state); + +/// @brief Label of transition to the next stage +/// @param state - pointer to the state of pomorodo. +/// @returns string with the label of the "skipp" button +char* flipp_pomodoro__next_stage_label(FlippPomodoroState* state); + +/// @brief Check if current stage is expired +/// @param state - pointer to the state of pomorodo. +/// @returns expriations status - true means stage is expired +bool flipp_pomodoro__is_stage_expired(FlippPomodoroState* state); + +/// @brief Rotate stage of the timer +/// @param state - pointer to the state of pomorodo. +void flipp_pomodoro__toggle_stage(FlippPomodoroState* state); diff --git a/applications/plugins/pomodoro/scenes/.keep b/applications/plugins/pomodoro/scenes/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h b/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h new file mode 100644 index 000000000..f95daeb30 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/config/flipp_pomodoro_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(flipp_pomodoro, timer, Timer) \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c new file mode 100644 index 000000000..5856ac947 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.c @@ -0,0 +1,30 @@ +#include "flipp_pomodoro_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const flipp_pomodoro_scene_on_enter_handlers[])(void*) = { +#include "config/flipp_pomodoro_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 flipp_pomodoro_scene_on_event_handlers[])(void* ctx, SceneManagerEvent event) = { +#include "config/flipp_pomodoro_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 flipp_pomodoro_scene_on_exit_handlers[])(void* ctx) = { +#include "config/flipp_pomodoro_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers flipp_pomodoro_scene_handlers = { + .on_enter_handlers = flipp_pomodoro_scene_on_enter_handlers, + .on_event_handlers = flipp_pomodoro_scene_on_event_handlers, + .on_exit_handlers = flipp_pomodoro_scene_on_exit_handlers, + .scene_num = FlippPomodoroSceneNum, +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h new file mode 100644 index 000000000..3b8a9052a --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene.h @@ -0,0 +1,27 @@ +#include + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlippPomodoroScene##id, +typedef enum { +#include "config/flipp_pomodoro_scene_config.h" + FlippPomodoroSceneNum, +} FlippPomodoroScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flipp_pomodoro_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "config/flipp_pomodoro_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* ctx, SceneManagerEvent event); +#include "config/flipp_pomodoro_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* ctx); +#include "config/flipp_pomodoro_scene_config.h" +#undef ADD_SCENE diff --git a/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c new file mode 100644 index 000000000..2190dbdb7 --- /dev/null +++ b/applications/plugins/pomodoro/scenes/flipp_pomodoro_scene_timer.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include "../flipp_pomodoro_app.h" +#include "../views/flipp_pomodoro_timer_view.h" + +enum { SceneEventConusmed = true, SceneEventNotConusmed = false }; + +uint8_t ExitSignal = 0; + +void flipp_pomodoro_scene_timer_sync_view_state(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + flipp_pomodoro_view_timer_set_state( + flipp_pomodoro_view_timer_get_view(app->timer_view), app->state); +}; + +void flipp_pomodoro_scene_timer_on_next_stage(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + view_dispatcher_send_custom_event(app->view_dispatcher, FlippPomodoroAppCustomEventStageSkip); +}; + +void flipp_pomodoro_scene_timer_on_enter(void* ctx) { + furi_assert(ctx); + + FlippPomodoroApp* app = ctx; + + view_dispatcher_switch_to_view(app->view_dispatcher, FlippPomodoroAppViewTimer); + flipp_pomodoro_scene_timer_sync_view_state(app); + flipp_pomodoro_view_timer_set_on_right_cb( + app->timer_view, flipp_pomodoro_scene_timer_on_next_stage, app); +}; + +void flipp_pomodoro_scene_timer_handle_custom_event( + FlippPomodoroApp* app, + FlippPomodoroAppCustomEvent custom_event) { + if(custom_event == FlippPomodoroAppCustomEventTimerTick && + flipp_pomodoro__is_stage_expired(app->state)) { + view_dispatcher_send_custom_event( + app->view_dispatcher, FlippPomodoroAppCustomEventStageComplete); + } + + if(custom_event == FlippPomodoroAppCustomEventStateUpdated) { + flipp_pomodoro_scene_timer_sync_view_state(app); + } +}; + +bool flipp_pomodoro_scene_timer_on_event(void* ctx, SceneManagerEvent event) { + furi_assert(ctx); + FlippPomodoroApp* app = ctx; + + switch(event.type) { + case SceneManagerEventTypeCustom: + flipp_pomodoro_scene_timer_handle_custom_event(app, event.event); + return SceneEventConusmed; + case SceneManagerEventTypeBack: + return ExitSignal; + default: + break; + }; + return SceneEventNotConusmed; +}; + +void flipp_pomodoro_scene_timer_on_exit(void* ctx) { + UNUSED(ctx); +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/.keep b/applications/plugins/pomodoro/views/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c new file mode 100644 index 000000000..e8e0383b7 --- /dev/null +++ b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.c @@ -0,0 +1,195 @@ +#include "flipp_pomodoro_timer_view.h" +#include +#include +#include +#include +#include "../helpers/debug.h" +#include "../flipp_pomodoro_app.h" +#include "../modules/flipp_pomodoro.h" + +// Auto-compiled icons +#include "flipp_pomodoro_icons.h" + +enum { + ViewInputConsumed = true, + ViewInputNotConusmed = false, +}; + +struct FlippPomodoroTimerView { + View* view; + FlippPomodoroTimerViewInputCb right_cb; + void* right_cb_ctx; +}; + +typedef struct { + IconAnimation* icon; + FlippPomodoroState* state; +} FlippPomodoroTimerViewModel; + +static const Icon* stage_background_image[] = { + [FlippPomodoroStageFocus] = &A_flipp_pomodoro_focus_64, + [FlippPomodoroStageRest] = &A_flipp_pomodoro_rest_64, + [FlippPomodoroStageLongBreak] = &A_flipp_pomodoro_rest_64, +}; + +static void + flipp_pomodoro_view_timer_draw_countdown(Canvas* canvas, TimeDifference remaining_time) { + canvas_set_font(canvas, FontBigNumbers); + const uint8_t right_border_margin = 1; + + const uint8_t countdown_box_height = canvas_height(canvas) * 0.4; + const uint8_t countdown_box_width = canvas_width(canvas) * 0.5; + const uint8_t countdown_box_x = + canvas_width(canvas) - countdown_box_width - right_border_margin; + const uint8_t countdown_box_y = 15; + + elements_bold_rounded_frame( + canvas, countdown_box_x, countdown_box_y, countdown_box_width, countdown_box_height); + + FuriString* timer_string = furi_string_alloc(); + furi_string_printf(timer_string, "%02u:%02u", remaining_time.minutes, remaining_time.seconds); + const char* remaining_stage_time_string = furi_string_get_cstr(timer_string); + canvas_draw_str_aligned( + canvas, + countdown_box_x + (countdown_box_width / 2), + countdown_box_y + (countdown_box_height / 2), + AlignCenter, + AlignCenter, + remaining_stage_time_string); + + furi_string_free(timer_string); +}; + +static void draw_str_with_drop_shadow( + Canvas* canvas, + uint8_t x, + uint8_t y, + Align horizontal, + Align vertical, + const char* str) { + canvas_set_color(canvas, ColorWhite); + for(int x_off = -2; x_off <= 2; x_off++) { + for(int y_off = -2; y_off <= 2; y_off++) { + canvas_draw_str_aligned(canvas, x + x_off, y + y_off, horizontal, vertical, str); + } + } + canvas_set_color(canvas, ColorBlack); + canvas_draw_str_aligned(canvas, x, y, horizontal, vertical, str); +} + +static void + flipp_pomodoro_view_timer_draw_current_stage_label(Canvas* canvas, FlippPomodoroState* state) { + canvas_set_font(canvas, FontPrimary); + draw_str_with_drop_shadow( + canvas, + canvas_width(canvas), + 0, + AlignRight, + AlignTop, + flipp_pomodoro__current_stage_label(state)); +} + +static void flipp_pomodoro_view_timer_draw_callback(Canvas* canvas, void* _model) { + if(!_model) { + return; + }; + + FlippPomodoroTimerViewModel* model = _model; + + canvas_clear(canvas); + if(model->icon) { + canvas_draw_icon_animation(canvas, 0, 0, model->icon); + } + + flipp_pomodoro_view_timer_draw_countdown( + canvas, flipp_pomodoro__stage_remaining_duration(model->state)); + + flipp_pomodoro_view_timer_draw_current_stage_label(canvas, model->state); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontSecondary); + elements_button_right(canvas, flipp_pomodoro__next_stage_label(model->state)); +}; + +bool flipp_pomodoro_view_timer_input_callback(InputEvent* event, void* ctx) { + furi_assert(ctx); + furi_assert(event); + FlippPomodoroTimerView* timer = ctx; + + const bool should_trigger_right_event_cb = (event->type == InputTypePress) && + (event->key == InputKeyRight) && + (timer->right_cb != NULL); + + if(should_trigger_right_event_cb) { + furi_assert(timer->right_cb); + furi_assert(timer->right_cb_ctx); + timer->right_cb(timer->right_cb_ctx); + return ViewInputConsumed; + }; + + return ViewInputNotConusmed; +}; + +View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer) { + furi_assert(timer); + return timer->view; +}; + +void flipp_pomodoro_view_timer_assign_animation(View* view) { + with_view_model( + view, + FlippPomodoroTimerViewModel * model, + { + furi_assert(model->state); + if(model->icon) { + icon_animation_free(model->icon); + } + model->icon = icon_animation_alloc( + stage_background_image[flipp_pomodoro__get_stage(model->state)]); + view_tie_icon_animation(view, model->icon); + icon_animation_start(model->icon); + }, + true); +} + +FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc() { + FlippPomodoroTimerView* timer = malloc(sizeof(FlippPomodoroTimerView)); + timer->view = view_alloc(); + + view_allocate_model(timer->view, ViewModelTypeLockFree, sizeof(FlippPomodoroTimerViewModel)); + view_set_context(flipp_pomodoro_view_timer_get_view(timer), timer); + view_set_draw_callback(timer->view, flipp_pomodoro_view_timer_draw_callback); + view_set_input_callback(timer->view, flipp_pomodoro_view_timer_input_callback); + + return timer; +}; + +void flipp_pomodoro_view_timer_set_on_right_cb( + FlippPomodoroTimerView* timer, + FlippPomodoroTimerViewInputCb right_cb, + void* right_cb_ctx) { + furi_assert(right_cb); + furi_assert(right_cb_ctx); + timer->right_cb = right_cb; + timer->right_cb_ctx = right_cb_ctx; +}; + +void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state) { + furi_assert(view); + furi_assert(state); + with_view_model( + view, FlippPomodoroTimerViewModel * model, { model->state = state; }, false); + flipp_pomodoro_view_timer_assign_animation(view); +}; + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer) { + furi_assert(timer); + with_view_model( + timer->view, + FlippPomodoroTimerViewModel * model, + { icon_animation_free(model->icon); }, + false); + view_free(timer->view); + + free(timer); +}; \ No newline at end of file diff --git a/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h new file mode 100644 index 000000000..929a0eba3 --- /dev/null +++ b/applications/plugins/pomodoro/views/flipp_pomodoro_timer_view.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "../modules/flipp_pomodoro.h" + +typedef struct FlippPomodoroTimerView FlippPomodoroTimerView; + +typedef void (*FlippPomodoroTimerViewInputCb)(void* ctx); + +FlippPomodoroTimerView* flipp_pomodoro_view_timer_alloc(); + +View* flipp_pomodoro_view_timer_get_view(FlippPomodoroTimerView* timer); + +void flipp_pomodoro_view_timer_free(FlippPomodoroTimerView* timer); + +void flipp_pomodoro_view_timer_set_state(View* view, FlippPomodoroState* state); + +void flipp_pomodoro_view_timer_set_on_right_cb( + FlippPomodoroTimerView* timer, + FlippPomodoroTimerViewInputCb right_cb, + void* right_cb_ctx); diff --git a/applications/plugins/scrambler/LICENSE b/applications/plugins/scrambler/LICENSE new file mode 100644 index 000000000..4a6de25cb --- /dev/null +++ b/applications/plugins/scrambler/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 RaZe + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/applications/plugins/scrambler/README.md b/applications/plugins/scrambler/README.md new file mode 100644 index 000000000..7e4700bcd --- /dev/null +++ b/applications/plugins/scrambler/README.md @@ -0,0 +1,16 @@ +# Setting up the Rubik's Cube Scrambler + +## Installation +To install the Rubik's Cube Scrambler, simply add the `rubiks_cube_scrambler` folder to your `application_users` folder. + +## Cleaning the code and removing old files +Run `./fbt -c fap_rubiks_cube_scrambler` to clean the code and remove any old binaries or compilation artifacts. + +## Compiling the FAP +To compile the FAP, run `./fbt fap_rubiks_cube_scrambler`. + +## Launching the app +To run the Rubik's Cube Scrambler directly from the Flip.x0, use `./fbt launch_app APPSRC=rubiks_cube_scrambler`. + +# A special thanks to Tanish for their c scrambler example 🙏 +https://github.com/TanishBhongade/RubiksCubeScrambler-C/ diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam new file mode 100644 index 000000000..8d87a4a62 --- /dev/null +++ b/applications/plugins/scrambler/application.fam @@ -0,0 +1,20 @@ +# COMPILE ISTRUCTIONS: + +# Clean the code and remove old binaries/compilation artefact +# ./fbt -c fap_rubiks_cube_scrambler + +# Compile FAP +# ./fbt fap_rubiks_cube_scrambler + +# Run application directly inside the Flip.x0 +# ./fbt launch_app APPSRC=rubiks_cube_scrambler + +App( + appid="Rubiks_Cube_Scrambler", + name="Rubik's Cube Scrambler", + apptype=FlipperAppType.EXTERNAL, + entry_point="rubiks_cube_scrambler_main", + stack_size=1 * 1024, + fap_category="Misc", + fap_icon="cube.png", +) diff --git a/applications/plugins/scrambler/assets/1.png b/applications/plugins/scrambler/assets/1.png new file mode 100644 index 0000000000000000000000000000000000000000..d2099ea34bc3e6b6bb9718f9c15e6b412f7e94ef GIT binary patch literal 1964 zcmbuAe^AnA9LFDOp6Oj)S?yZg#2qc0%&qIrvJ@<*Y1+(l2}4D*G;3Wc5-1XC&8x*M z($s7qXy#P{YN#_sq+9FE>iCw5ga~f_=m!Xt2q*}SuC~7IcDuIz_XN%nKS&8LdxJ z!8mP1jR2M<0Mvc}wyr~fCD8yBd;gE=RhDK`SD_<>aa|4}yR}J1O486pijc44S_F?Z zw5AOTF=r#UNh_0jtRNu%+T*GLQ-sF1HTMiDdjk!TepY>~gaR?i48O77g8~4;JU~(0 z0^mE)iYZ#kFU$>%r|TtyOlsm#vbrFK9blpiCAW9Aw^yg&%?^T9RmUe8KAtbVo=8$J z>t+Y&+mjEaAIpbth_V2jxS>usbJ#3cm7m4F;@Ce|toym7w|ZajA=YK9Il%l>L*4H% zhTZEb>3V#ZIZGsc88E`H>hlB>i{6}uD6PdR1&}KKt7MWo9R*1aJWMiGw^`Ru*)$9{ zXH_6B#_67s|05{(eVqo^z4-wXO3L8udV1@JU}&zR({PYtAml|6Zj_NL%O;W z%h{;NJ*#hg!O0k;IsAgs3U)nWU0?8O>6db5`3=;98@v_oXZZWT@^1gy9;uIKx|ljD zaa!%BXO<+5YRXoLWlgu-){?5^vTEL;%Z?S+UV)|I9HdiIvq33VE@P}^v*XmlrAE-f4hJkruA z))inc!rb%ka0fSo(67+^lcCpj^AFPm=f#_7uAcBhV5y`gwsPvbZGzcS({L_k^&W-N z#&1%oPHC(u*K+1}XTB}beGz|q;T0xNC!;9?v3{x6=n7*@MS)>2ENl`nhpN@!OKF?G zsi0l!gNQHz$}vS=@C#>v?>bLF?#X{*zklx1`XT=+MZj3zUXwQ|w#1<_8ESLe8V{=D|{G zq5%8-gf>ykJ-ua$hKFLZ?D8iW5y29}T=%dNV#1E<@)H2=K3HP!*&p8x`TyK`HPqai zvJoSsCI-pu3drz9IC<9wcnK&M=Edje6V&rNd#!6AcpK=q03RJo5;$5pl;P^6jFrza z?@?0a)%yZ-gc?`V3+4F!&Q0gQK(no+nWCymI3X3U=25j%*=u3Y$tCW;S#P%#KWn?idpKqd-0`pJKq4K})Yp6QaqNTRKG<3A zsiiP48|f4se8IOhDd}brz9l&;6K8`sO+IA t&>(7LWYlC4adU~LN{0T4ZGxGm4Eb^njH~-D1pu`$c)I$ztaD0e0szja7u)~< literal 0 HcmV?d00001 diff --git a/applications/plugins/scrambler/rubiks_cube_scrambler.c b/applications/plugins/scrambler/rubiks_cube_scrambler.c new file mode 100644 index 000000000..4c845b883 --- /dev/null +++ b/applications/plugins/scrambler/rubiks_cube_scrambler.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include + +#include "scrambler.h" +#include "furi_hal_random.h" + +int scrambleStarted = 0; +char scramble_str[100] = {0}; +char scramble_start[100] = {0}; +char scramble_end[100] = {0}; +int notifications_enabled = 0; + +static void success_vibration() { + furi_hal_vibro_on(false); + furi_hal_vibro_on(true); + furi_delay_ms(50); + furi_hal_vibro_on(false); + return; +} +void split_array(char original[], int size, char first[], char second[]) { + int mid = size / 2; + if(size % 2 != 0) { + mid++; + } + int first_index = 0, second_index = 0; + for(int i = 0; i < size; i++) { + if(i < mid) { + first[first_index++] = original[i]; + } else { + if(i == mid && (original[i] == '2' || original[i] == '\'')) { + continue; + } + second[second_index++] = original[i]; + } + } + first[first_index] = '\0'; + second[second_index] = '\0'; +} + +static void draw_callback(Canvas* canvas, void* ctx) { + UNUSED(ctx); + canvas_clear(canvas); + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 4, 13, "Rubik's Cube Scrambler"); + + if(scrambleStarted) { + genScramble(); + scrambleReplace(); + strcpy(scramble_str, printData()); + if(notifications_enabled) { + success_vibration(); + } + split_array(scramble_str, strlen(scramble_str), scramble_start, scramble_end); + scrambleStarted = 0; + } + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned(canvas, 64, 28, AlignCenter, AlignCenter, scramble_start); + canvas_draw_str_aligned(canvas, 64, 38, AlignCenter, AlignCenter, scramble_end); + elements_button_center(canvas, "New"); + + elements_button_left(canvas, notifications_enabled ? "On" : "Off"); +}; + +static void input_callback(InputEvent* input_event, void* ctx) { + furi_assert(ctx); + FuriMessageQueue* event_queue = ctx; + furi_message_queue_put(event_queue, input_event, FuriWaitForever); +} + +int32_t rubiks_cube_scrambler_main(void* p) { + UNUSED(p); + InputEvent event; + + FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + ViewPort* view_port = view_port_alloc(); + + view_port_draw_callback_set(view_port, draw_callback, NULL); + + view_port_input_callback_set(view_port, input_callback, event_queue); + + Gui* gui = furi_record_open(RECORD_GUI); + gui_add_view_port(gui, view_port, GuiLayerFullscreen); + + while(true) { + furi_check(furi_message_queue_get(event_queue, &event, FuriWaitForever) == FuriStatusOk); + + if(event.key == InputKeyOk && event.type == InputTypeShort) { + scrambleStarted = 1; + } + if(event.key == InputKeyLeft && event.type == InputTypeShort) { + if(notifications_enabled) { + notifications_enabled = 0; + } else { + notifications_enabled = 1; + success_vibration(); + } + } + if(event.key == InputKeyBack) { + break; + } + } + + furi_message_queue_free(event_queue); + + gui_remove_view_port(gui, view_port); + + view_port_free(view_port); + furi_record_close(RECORD_GUI); + return 0; +} diff --git a/applications/plugins/scrambler/scrambler.c b/applications/plugins/scrambler/scrambler.c new file mode 100644 index 000000000..ea5291940 --- /dev/null +++ b/applications/plugins/scrambler/scrambler.c @@ -0,0 +1,102 @@ +/* +Authors: Tanish Bhongade and RaZe +*/ + +#include +#include +#include +#include "furi_hal_random.h" +#include +#include +#include "scrambler.h" + +// 6 moves along with direction +char moves[6] = {'R', 'U', 'F', 'B', 'L', 'D'}; +char dir[4] = {' ', '\'', '2'}; +const int SLEN = 20; +#define RESULT_SIZE 100 +// Structure which holds main scramble +struct GetScramble { + char mainScramble[25][3]; +}; +struct GetScramble a; // Its object + +// Function prototypes to avoid bugs +void scrambleReplace(); +void genScramble(); +void valid(); +int getRand(int upr, int lwr); +char* printData(); +void writeToFile(); + +// Main function +/* int main(){ + genScramble ();//Calling genScramble + scrambleReplace();//Calling scrambleReplace + valid();//Calling valid to validate the scramble + printData ();//Printing the final scramble + //writeToFile();//If you want to write to a file, please uncomment this + + return 0; +} */ + +void genScramble() { + // Stage 1 + for(int i = 0; i < SLEN; i++) { + strcpy(a.mainScramble[i], "00"); + } + // This makes array like this 00 00 00....... +} + +void scrambleReplace() { + // Stage 2 + // Actual process begins here + + // Initialize the mainScramble array with all the possible moves + for(int i = 0; i < SLEN; i++) { + a.mainScramble[i][0] = moves[furi_hal_random_get() % 6]; + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + + // Perform the Fisher-Yates shuffle + for(int i = 6 - 1; i > 0; i--) { + int j = rand() % (i + 1); + char temp[3]; + strcpy(temp, a.mainScramble[i]); + strcpy(a.mainScramble[i], a.mainScramble[j]); + strcpy(a.mainScramble[j], temp); + } + + // Select the first 10 elements as the scramble, using only the first three elements of the dir array + for(int i = 0; i < SLEN; i++) { + a.mainScramble[i][1] = dir[furi_hal_random_get() % 3]; + } + for(int i = 1; i < SLEN; i++) { + while(a.mainScramble[i][0] == a.mainScramble[i - 2][0] || + a.mainScramble[i][0] == a.mainScramble[i - 1][0]) { + a.mainScramble[i][0] = moves[furi_hal_random_get() % 5]; + } + } +} + +// Let this function be here for now till I find out what is causing the extra space bug in the scrambles +void remove_double_spaces(char* str) { + int i, j; + int len = strlen(str); + for(i = 0, j = 0; i < len; i++, j++) { + if(str[i] == ' ' && str[i + 1] == ' ') { + i++; + } + str[j] = str[i]; + } + str[j] = '\0'; +} +char* printData() { + static char result[RESULT_SIZE]; + int offset = 0; + for(int loop = 0; loop < SLEN; loop++) { + offset += snprintf(result + offset, RESULT_SIZE - offset, "%s ", a.mainScramble[loop]); + } + remove_double_spaces(result); + return result; +} diff --git a/applications/plugins/scrambler/scrambler.h b/applications/plugins/scrambler/scrambler.h new file mode 100644 index 000000000..4b56c565d --- /dev/null +++ b/applications/plugins/scrambler/scrambler.h @@ -0,0 +1,3 @@ +void scrambleReplace(); +void genScramble(); +char* printData(); From cb2e8fbb52b699e7ea83a97eb8114c3770c2adc7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Wed, 8 Feb 2023 04:08:24 +0000 Subject: [PATCH 111/231] Add [GPIO] tag to geiger counter app --- applications/plugins/geigercounter/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/geigercounter/application.fam b/applications/plugins/geigercounter/application.fam index 9fddb52b0..71be5a161 100644 --- a/applications/plugins/geigercounter/application.fam +++ b/applications/plugins/geigercounter/application.fam @@ -1,6 +1,6 @@ App( appid="Geiger_Coutner", - name="Geiger Counter", + name="[GPIO] Geiger Counter", apptype=FlipperAppType.EXTERNAL, entry_point="flipper_geiger_app", cdefines=["APP_GEIGER"], From e3d473bf429bd254b53550ebcbbb0d389b49240c Mon Sep 17 00:00:00 2001 From: Sergey Gavrilov Date: Wed, 8 Feb 2023 07:41:22 +0300 Subject: [PATCH 112/231] [FL-2435] SD over SPI improvements (#2204) * get rid of BSP layer * sector_cache: init in any case * new sd-spi driver: init * Delete stm32_adafruit_sd.c.old * Delete spi_sd_hal.c.old * Storage: faster api lock primitive * Threads: priority control * Flags: correct error code * Spi: dma mode * SD-card: use DMA for big blocks of SPI data * Fix wrong SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE value * do not memset cache if it is NULL * remove top-level timeouts * sd hal: disable debug * Furi HAL: DMA * Furi HAL RFID: use furi_hal_dma * Furi HAL DMA: tests * Furi HAL DMA: docs * rollback "Furi HAL RFID: use furi_hal_dma" * 4 channels taken from DMA manager for core HAL. * Furi HAL DMA: live fast, die young * RPC tests: increase message buffer * SPI HAL: use second DMA instance * sd hal: new CID getter * SPI hal: use non-DMA version if kernel is not running * IR hal: generalize DMA definition * storage: add CID data to sd info * RFID hal: generalize DMA definition * SUBGHZ hal: generalize DMA definition. Core hal moved to DMA2. * Storage: small optimizations, removal of extra mutex * Storage: redundant macro * SD hal: more timeouts * SPI hal: DMA init * Target: make furi_hal_spi_dma_init symbol private * Update F18 api symbols Co-authored-by: Aleksandr Kutuzov --- applications/debug/unit_tests/rpc/rpc_test.c | 2 +- applications/services/storage/storage_cli.c | 21 +- .../services/storage/storage_external_api.c | 9 +- applications/services/storage/storage_glue.c | 18 +- applications/services/storage/storage_glue.h | 3 - .../services/storage/storage_message.h | 3 +- .../services/storage/storage_processing.c | 39 +- .../services/storage/storage_sd_api.h | 10 + .../services/storage/storages/storage_ext.c | 33 +- .../scenes/storage_settings_scene_sd_info.c | 21 +- firmware/targets/f18/api_symbols.csv | 6 +- firmware/targets/f18/furi_hal/furi_hal.c | 1 + firmware/targets/f7/api_symbols.csv | 6 +- firmware/targets/f7/fatfs/sd_spi_io.c | 877 +++++++++++++ firmware/targets/f7/fatfs/sd_spi_io.h | 157 +++ firmware/targets/f7/fatfs/sector_cache.c | 5 - firmware/targets/f7/fatfs/spi_sd_hal.c | 98 -- firmware/targets/f7/fatfs/stm32_adafruit_sd.c | 1113 ----------------- firmware/targets/f7/fatfs/stm32_adafruit_sd.h | 245 ---- firmware/targets/f7/fatfs/user_diskio.c | 36 +- firmware/targets/f7/fatfs/user_diskio.h | 2 +- firmware/targets/f7/furi_hal/furi_hal.c | 1 + .../targets/f7/furi_hal/furi_hal_infrared.c | 121 +- firmware/targets/f7/furi_hal/furi_hal_rfid.c | 46 +- firmware/targets/f7/furi_hal/furi_hal_spi.c | 231 +++- .../targets/f7/furi_hal/furi_hal_subghz.c | 49 +- firmware/targets/f7/src/update.c | 4 +- .../targets/furi_hal_include/furi_hal_spi.h | 20 + furi/core/event_flag.c | 2 +- furi/core/thread.c | 9 + furi/core/thread.h | 12 + 31 files changed, 1539 insertions(+), 1661 deletions(-) create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.c create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.h delete mode 100644 firmware/targets/f7/fatfs/spi_sd_hal.c delete mode 100644 firmware/targets/f7/fatfs/stm32_adafruit_sd.c delete mode 100644 firmware/targets/f7/fatfs/stm32_adafruit_sd.h diff --git a/applications/debug/unit_tests/rpc/rpc_test.c b/applications/debug/unit_tests/rpc/rpc_test.c index 5b52df2fa..76acf6be9 100644 --- a/applications/debug/unit_tests/rpc/rpc_test.c +++ b/applications/debug/unit_tests/rpc/rpc_test.c @@ -89,7 +89,7 @@ static void test_rpc_setup(void) { } furi_check(rpc_session[0].session); - rpc_session[0].output_stream = furi_stream_buffer_alloc(1000, 1); + rpc_session[0].output_stream = furi_stream_buffer_alloc(4096, 1); rpc_session_set_send_bytes_callback(rpc_session[0].session, output_bytes_callback); rpc_session[0].close_session_semaphore = xSemaphoreCreateBinary(); rpc_session[0].terminate_semaphore = xSemaphoreCreateBinary(); diff --git a/applications/services/storage/storage_cli.c b/applications/services/storage/storage_cli.c index eeaa7fce8..ff2055697 100644 --- a/applications/services/storage/storage_cli.c +++ b/applications/services/storage/storage_cli.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -61,28 +60,26 @@ static void storage_cli_info(Cli* cli, FuriString* path) { } } else if(furi_string_cmp_str(path, STORAGE_EXT_PATH_PREFIX) == 0) { SDInfo sd_info; - SD_CID sd_cid; FS_Error error = storage_sd_info(api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); if(error != FSE_OK) { storage_cli_print_error(error); } else { printf( "Label: %s\r\nType: %s\r\n%luKiB total\r\n%luKiB free\r\n" - "%02x%2.2s %5.5s %i.%i\r\nSN:%04lx %02i/%i\r\n", + "%02x%s %s v%i.%i\r\nSN:%04lx %02i/%i\r\n", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, sd_info.kb_free, - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); } } else { storage_cli_print_usage(); diff --git a/applications/services/storage/storage_external_api.c b/applications/services/storage/storage_external_api.c index 6929a9cbd..c5dfd533e 100644 --- a/applications/services/storage/storage_external_api.c +++ b/applications/services/storage/storage_external_api.c @@ -12,9 +12,7 @@ #define TAG "StorageAPI" -#define S_API_PROLOGUE \ - FuriSemaphore* semaphore = furi_semaphore_alloc(1, 0); \ - furi_check(semaphore != NULL); +#define S_API_PROLOGUE FuriApiLock lock = api_lock_alloc_locked(); #define S_FILE_API_PROLOGUE \ Storage* storage = file->storage; \ @@ -24,13 +22,12 @@ furi_check( \ furi_message_queue_put(storage->message_queue, &message, FuriWaitForever) == \ FuriStatusOk); \ - furi_semaphore_acquire(semaphore, FuriWaitForever); \ - furi_semaphore_free(semaphore); + api_lock_wait_unlock_and_free(lock) #define S_API_MESSAGE(_command) \ SAReturn return_data; \ StorageMessage message = { \ - .semaphore = semaphore, \ + .lock = lock, \ .command = _command, \ .data = &data, \ .return_data = &return_data, \ diff --git a/applications/services/storage/storage_glue.c b/applications/services/storage/storage_glue.c index 22f2e3dfa..cccf4046a 100644 --- a/applications/services/storage/storage_glue.c +++ b/applications/services/storage/storage_glue.c @@ -31,29 +31,13 @@ void storage_file_clear(StorageFile* obj) { /****************** storage data ******************/ void storage_data_init(StorageData* storage) { - storage->mutex = furi_mutex_alloc(FuriMutexTypeNormal); - furi_check(storage->mutex != NULL); storage->data = NULL; storage->status = StorageStatusNotReady; StorageFileList_init(storage->files); } -bool storage_data_lock(StorageData* storage) { - return (furi_mutex_acquire(storage->mutex, FuriWaitForever) == FuriStatusOk); -} - -bool storage_data_unlock(StorageData* storage) { - return (furi_mutex_release(storage->mutex) == FuriStatusOk); -} - StorageStatus storage_data_status(StorageData* storage) { - StorageStatus status; - - storage_data_lock(storage); - status = storage->status; - storage_data_unlock(storage); - - return status; + return storage->status; } const char* storage_data_status_text(StorageData* storage) { diff --git a/applications/services/storage/storage_glue.h b/applications/services/storage/storage_glue.h index 6fdc70099..501c26abc 100644 --- a/applications/services/storage/storage_glue.h +++ b/applications/services/storage/storage_glue.h @@ -38,8 +38,6 @@ void storage_file_set(StorageFile* obj, const StorageFile* src); void storage_file_clear(StorageFile* obj); void storage_data_init(StorageData* storage); -bool storage_data_lock(StorageData* storage); -bool storage_data_unlock(StorageData* storage); StorageStatus storage_data_status(StorageData* storage); const char* storage_data_status_text(StorageData* storage); void storage_data_timestamp(StorageData* storage); @@ -57,7 +55,6 @@ struct StorageData { const FS_Api* fs_api; StorageApi api; void* data; - FuriMutex* mutex; StorageStatus status; StorageFileList_t files; uint32_t timestamp; diff --git a/applications/services/storage/storage_message.h b/applications/services/storage/storage_message.h index 987268017..3edb1018e 100644 --- a/applications/services/storage/storage_message.h +++ b/applications/services/storage/storage_message.h @@ -1,5 +1,6 @@ #pragma once #include +#include #ifdef __cplusplus extern "C" { @@ -130,7 +131,7 @@ typedef enum { } StorageCommand; typedef struct { - FuriSemaphore* semaphore; + FuriApiLock lock; StorageCommand command; SAData* data; SAReturn* return_data; diff --git a/applications/services/storage/storage_processing.c b/applications/services/storage/storage_processing.c index 795a5d11c..b1ea5d29b 100644 --- a/applications/services/storage/storage_processing.c +++ b/applications/services/storage/storage_processing.c @@ -2,15 +2,7 @@ #include #include -#define FS_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->fs_api->_fn; \ - storage_data_unlock(_storage); - -#define ST_CALL(_storage, _fn) \ - storage_data_lock(_storage); \ - ret = _storage->api._fn; \ - storage_data_unlock(_storage); +#define FS_CALL(_storage, _fn) ret = _storage->fs_api->_fn; static StorageData* storage_get_storage_by_type(Storage* app, StorageType type) { furi_check(type == ST_EXT || type == ST_INT); @@ -44,16 +36,11 @@ static const char* remove_vfs(const char* path) { static StorageType storage_get_type_by_path(Storage* app, const char* path) { StorageType type = ST_ERROR; - if(strlen(path) >= strlen(STORAGE_EXT_PATH_PREFIX) && - memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { + if(memcmp(path, STORAGE_EXT_PATH_PREFIX, strlen(STORAGE_EXT_PATH_PREFIX)) == 0) { type = ST_EXT; - } else if( - strlen(path) >= strlen(STORAGE_INT_PATH_PREFIX) && - memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { + } else if(memcmp(path, STORAGE_INT_PATH_PREFIX, strlen(STORAGE_INT_PATH_PREFIX)) == 0) { type = ST_INT; - } else if( - strlen(path) >= strlen(STORAGE_ANY_PATH_PREFIX) && - memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { + } else if(memcmp(path, STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == 0) { type = ST_ANY; } @@ -68,21 +55,15 @@ static StorageType storage_get_type_by_path(Storage* app, const char* path) { } static void storage_path_change_to_real_storage(FuriString* path, StorageType real_storage) { - if(memcmp( - furi_string_get_cstr(path), STORAGE_ANY_PATH_PREFIX, strlen(STORAGE_ANY_PATH_PREFIX)) == - 0) { + if(furi_string_search(path, STORAGE_ANY_PATH_PREFIX) == 0) { switch(real_storage) { case ST_EXT: - furi_string_set_char(path, 0, STORAGE_EXT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_EXT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_EXT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_EXT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_EXT_PATH_PREFIX), STORAGE_EXT_PATH_PREFIX); break; case ST_INT: - furi_string_set_char(path, 0, STORAGE_INT_PATH_PREFIX[0]); - furi_string_set_char(path, 1, STORAGE_INT_PATH_PREFIX[1]); - furi_string_set_char(path, 2, STORAGE_INT_PATH_PREFIX[2]); - furi_string_set_char(path, 3, STORAGE_INT_PATH_PREFIX[3]); + furi_string_replace_at( + path, 0, strlen(STORAGE_INT_PATH_PREFIX), STORAGE_INT_PATH_PREFIX); break; default: break; @@ -604,7 +585,7 @@ void storage_process_message_internal(Storage* app, StorageMessage* message) { break; } - furi_semaphore_release(message->semaphore); + api_lock_unlock(message->lock); } void storage_process_message(Storage* app, StorageMessage* message) { diff --git a/applications/services/storage/storage_sd_api.h b/applications/services/storage/storage_sd_api.h index f83360955..880640394 100644 --- a/applications/services/storage/storage_sd_api.h +++ b/applications/services/storage/storage_sd_api.h @@ -23,6 +23,16 @@ typedef struct { uint16_t cluster_size; uint16_t sector_size; char label[SD_LABEL_LENGTH]; + + uint8_t manufacturer_id; + char oem_id[3]; + char product_name[6]; + uint8_t product_revision_major; + uint8_t product_revision_minor; + uint32_t product_serial_number; + uint8_t manufacturing_month; + uint16_t manufacturing_year; + FS_Error error; } SDInfo; diff --git a/applications/services/storage/storages/storage_ext.c b/applications/services/storage/storages/storage_ext.c index 0c81a0006..530c88f85 100644 --- a/applications/services/storage/storages/storage_ext.c +++ b/applications/services/storage/storages/storage_ext.c @@ -26,12 +26,10 @@ static FS_Error storage_ext_parse_error(SDError error); static bool sd_mount_card(StorageData* storage, bool notify) { bool result = false; - uint8_t counter = BSP_SD_MaxMountRetryCount(); + uint8_t counter = sd_max_mount_retry_count(); uint8_t bsp_result; SDData* sd_data = storage->data; - storage_data_lock(storage); - while(result == false && counter > 0 && hal_sd_detect()) { if(notify) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); @@ -41,9 +39,9 @@ static bool sd_mount_card(StorageData* storage, bool notify) { if((counter % 2) == 0) { // power reset sd card - bsp_result = BSP_SD_Init(true); + bsp_result = sd_init(true); } else { - bsp_result = BSP_SD_Init(false); + bsp_result = sd_init(false); } if(bsp_result) { @@ -91,7 +89,6 @@ static bool sd_mount_card(StorageData* storage, bool notify) { } storage_data_timestamp(storage); - storage_data_unlock(storage); return result; } @@ -100,14 +97,12 @@ FS_Error sd_unmount_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); storage->status = StorageStatusNotReady; error = FR_DISK_ERR; // TODO do i need to close the files? - f_mount(0, sd_data->path, 0); - storage_data_unlock(storage); + return storage_ext_parse_error(error); } @@ -120,8 +115,6 @@ FS_Error sd_format_card(StorageData* storage) { SDData* sd_data = storage->data; SDError error; - storage_data_lock(storage); - work_area = malloc(_MAX_SS); error = f_mkfs(sd_data->path, FM_ANY, 0, work_area, _MAX_SS); free(work_area); @@ -138,8 +131,6 @@ FS_Error sd_format_card(StorageData* storage) { storage->status = StorageStatusOK; } while(false); - storage_data_unlock(storage); - return storage_ext_parse_error(error); #endif } @@ -156,14 +147,12 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { memset(sd_info, 0, sizeof(SDInfo)); // get fs info - storage_data_lock(storage); error = f_getlabel(sd_data->path, sd_info->label, NULL); if(error == FR_OK) { #ifndef FURI_RAM_EXEC error = f_getfree(sd_data->path, &free_clusters, &fs); #endif } - storage_data_unlock(storage); if(error == FR_OK) { // calculate size @@ -210,6 +199,20 @@ FS_Error sd_card_info(StorageData* storage, SDInfo* sd_info) { #endif } + SD_CID cid; + SdSpiStatus status = sd_get_cid(&cid); + + if(status == SdSpiStatusOK) { + sd_info->manufacturer_id = cid.ManufacturerID; + memcpy(sd_info->oem_id, cid.OEM_AppliID, sizeof(cid.OEM_AppliID)); + memcpy(sd_info->product_name, cid.ProdName, sizeof(cid.ProdName)); + sd_info->product_revision_major = cid.ProdRev >> 4; + sd_info->product_revision_minor = cid.ProdRev & 0x0F; + sd_info->product_serial_number = cid.ProdSN; + sd_info->manufacturing_year = 2000 + cid.ManufactYear; + sd_info->manufacturing_month = cid.ManufactMonth; + } + return storage_ext_parse_error(error); } diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c index a7991bc19..81c786d0c 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_sd_info.c @@ -1,5 +1,4 @@ #include "../storage_settings.h" -#include static void storage_settings_scene_sd_info_dialog_callback(DialogExResult result, void* context) { StorageSettings* app = context; @@ -12,9 +11,7 @@ void storage_settings_scene_sd_info_on_enter(void* context) { DialogEx* dialog_ex = app->dialog_ex; SDInfo sd_info; - SD_CID sd_cid; FS_Error sd_status = storage_sd_info(app->fs_api, &sd_info); - BSP_SD_GetCIDRegister(&sd_cid); scene_manager_set_scene_state(app->scene_manager, StorageSettingsSDInfo, sd_status); @@ -31,19 +28,19 @@ void storage_settings_scene_sd_info_on_enter(void* context) { furi_string_printf( app->text_string, "Label: %s\nType: %s\n%lu KiB total\n%lu KiB free\n" - "%02X%2.2s %5.5s %i.%i\nSN:%04lX %02i/%i", + "%02X%s %s v%i.%i\nSN:%04lX %02i/%i", sd_info.label, sd_api_get_fs_type_text(sd_info.fs_type), sd_info.kb_total, sd_info.kb_free, - sd_cid.ManufacturerID, - sd_cid.OEM_AppliID, - sd_cid.ProdName, - sd_cid.ProdRev >> 4, - sd_cid.ProdRev & 0xf, - sd_cid.ProdSN, - sd_cid.ManufactMonth, - sd_cid.ManufactYear + 2000); + sd_info.manufacturer_id, + sd_info.oem_id, + sd_info.product_name, + sd_info.product_revision_major, + sd_info.product_revision_minor, + sd_info.product_serial_number, + sd_info.manufacturing_month, + sd_info.manufacturing_year); dialog_ex_set_text( dialog_ex, furi_string_get_cstr(app->text_string), 4, 1, AlignLeft, AlignTop); } diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index e4a7dfdd5..ff6dbc239 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.1,, +Version,+,12.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1062,10 +1062,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,+,furi_hal_switch,void,void* Function,+,furi_hal_uart_deinit,void,FuriHalUartId @@ -1231,6 +1233,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1244,6 +1247,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" diff --git a/firmware/targets/f18/furi_hal/furi_hal.c b/firmware/targets/f18/furi_hal/furi_hal.c index ad35a0074..0a68fdb69 100644 --- a/firmware/targets/f18/furi_hal/furi_hal.c +++ b/firmware/targets/f18/furi_hal/furi_hal.c @@ -51,6 +51,7 @@ void furi_hal_init() { furi_hal_version_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_speaker_init(); FURI_LOG_I(TAG, "Speaker OK"); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index c1a979859..a1f34f106 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.1,, +Version,+,12.2,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1325,10 +1325,12 @@ Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,-,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1524,6 +1526,7 @@ Function,+,furi_thread_flags_wait,uint32_t,"uint32_t, uint32_t, uint32_t" Function,+,furi_thread_free,void,FuriThread* Function,+,furi_thread_get_current,FuriThread*, Function,+,furi_thread_get_current_id,FuriThreadId, +Function,+,furi_thread_get_current_priority,FuriThreadPriority, Function,+,furi_thread_get_heap_size,size_t,FuriThread* Function,+,furi_thread_get_id,FuriThreadId,FuriThread* Function,+,furi_thread_get_name,const char*,FuriThreadId @@ -1537,6 +1540,7 @@ Function,+,furi_thread_mark_as_service,void,FuriThread* Function,+,furi_thread_resume,void,FuriThreadId Function,+,furi_thread_set_callback,void,"FuriThread*, FuriThreadCallback" Function,+,furi_thread_set_context,void,"FuriThread*, void*" +Function,+,furi_thread_set_current_priority,void,FuriThreadPriority Function,+,furi_thread_set_name,void,"FuriThread*, const char*" Function,+,furi_thread_set_priority,void,"FuriThread*, FuriThreadPriority" Function,+,furi_thread_set_stack_size,void,"FuriThread*, size_t" diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c new file mode 100644 index 000000000..93b837e85 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -0,0 +1,877 @@ +#include "sd_spi_io.h" +#include "sector_cache.h" +#include +#include +#include + +// #define SD_SPI_DEBUG 1 +#define TAG "SdSpi" + +#ifdef SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH 6 +#define SD_DUMMY_BYTE 0xFF +#define SD_ANSWER_RETRY_COUNT 8 +#define SD_IDLE_RETRY_COUNT 100 +#define SD_BLOCK_SIZE 512 + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +static inline void sd_spi_select_card() { + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); + furi_delay_us(10); // Entry guard time for some SD cards +} + +static inline void sd_spi_deselect_card() { + furi_delay_us(10); // Exit guard time for some SD cards + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); +} + +static void sd_spi_bus_to_ground() { + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + + sd_spi_select_card(); + furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); +} + +static void sd_spi_bus_rise_up() { + sd_spi_deselect_card(); + + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); +} + +static inline uint8_t sd_spi_read_byte(void) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_byte(uint8_t data) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); +} + +static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static uint8_t sd_spi_wait_for_data_and_read(void) { + uint8_t retry_count = SD_ANSWER_RETRY_COUNT; + uint8_t responce; + + // Wait until we get a valid data + do { + responce = sd_spi_read_byte(); + retry_count--; + + } while((responce == SD_DUMMY_BYTE) && retry_count); + + return responce; +} + +static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); + uint8_t byte; + + do { + byte = sd_spi_read_byte(); + if(furi_hal_cortex_timer_is_expired(timer)) { + return SdSpiStatusTimeout; + } + } while((byte != data)); + + return SdSpiStatusOK; +} + +static inline void sd_spi_deselect_card_and_purge() { + sd_spi_deselect_card(); + sd_spi_read_byte(); +} + +static inline void sd_spi_purge_crc() { + sd_spi_read_byte(); + sd_spi_read_byte(); +} + +static SdSpiCmdAnswer + sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { + uint8_t frame[SD_CMD_LENGTH]; + SdSpiCmdAnswer cmd_answer = { + .r1 = SD_DUMMY_BYTE, + .r2 = SD_DUMMY_BYTE, + .r3 = SD_DUMMY_BYTE, + .r4 = SD_DUMMY_BYTE, + .r5 = SD_DUMMY_BYTE, + }; + + // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes + // R1b identical to R1 + Busy information + // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes + + frame[0] = ((uint8_t)cmd | 0x40); + frame[1] = (uint8_t)(arg >> 24); + frame[2] = (uint8_t)(arg >> 16); + frame[3] = (uint8_t)(arg >> 8); + frame[4] = (uint8_t)(arg); + frame[5] = (crc | 0x01); + + sd_spi_select_card(); + sd_spi_write_bytes(frame, sizeof(frame)); + + switch(answer_type) { + case SdSpiCmdAnswerTypeR1: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + break; + case SdSpiCmdAnswerTypeR1B: + // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + + // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B + // reassert card + sd_spi_deselect_card(); + furi_delay_us(1000); + sd_spi_deselect_card(); + + // and wait for it to be ready + while(sd_spi_read_byte() != 0xFF) { + }; + + break; + case SdSpiCmdAnswerTypeR2: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + break; + case SdSpiCmdAnswerTypeR3: + case SdSpiCmdAnswerTypeR7: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + cmd_answer.r3 = sd_spi_read_byte(); + cmd_answer.r4 = sd_spi_read_byte(); + cmd_answer.r5 = sd_spi_read_byte(); + break; + default: + break; + } + return cmd_answer; +} + +static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { + SdSpiDataResponce responce = sd_spi_read_byte(); + // read busy response byte + sd_spi_read_byte(); + + switch(responce & 0x1F) { + case SdSpiDataResponceOK: + // TODO: check timings + sd_spi_deselect_card(); + sd_spi_select_card(); + + // wait for 0xFF + if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static SdSpiStatus sd_spi_init_spi_mode_v1(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v1"); + + do { + retry_count++; + + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode_v2(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v2"); + + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + sd_spi_debug("ACMD41 is illegal command"); + retry_count = 0; + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { + sd_spi_debug("CMD55 failed"); + return SdSpiStatusError; + } + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode(void) { + SdSpiCmdAnswer response; + uint8_t retry_count; + + // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and + // wait for In Idle State Response (R1 Format) equal to 0x01 + retry_count = 0; + do { + retry_count++; + response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("CMD0 failed"); + return SdSpiStatusError; + } + } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); + + // CMD8 (SEND_IF_COND) to check the power supply status + // and wait until response (R7 Format) equal to 0xAA and + response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); + sd_spi_deselect_card_and_purge(); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) { + sd_spi_debug("Init mode v1 failed"); + return SdSpiStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) { + sd_spi_debug("Init mode v2 failed"); + return SdSpiStatusError; + } + + // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response + response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_debug("CMD58 failed"); + return SdSpiStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return SdSpiStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD9 (SEND_CSD): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CSD data + for(counter = 0; counter < 16; counter++) { + csd_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + /************************************************************************* + CSD header decoding + *************************************************************************/ + + csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; + csd->Reserved1 = csd_data[0] & 0x3F; + csd->TAAC = csd_data[1]; + csd->NSAC = csd_data[2]; + csd->MaxBusClkFrec = csd_data[3]; + csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); + csd->RdBlockLen = csd_data[5] & 0x0F; + csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; + csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; + csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; + csd->DSRImpl = (csd_data[6] & 0x10) >> 4; + + /************************************************************************* + CSD v1/v2 decoding + *************************************************************************/ + + if(sd_high_capacity == 0) { + csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); + csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | + ((csd_data[8] & 0xC0) >> 6); + csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; + csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); + csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; + csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; + csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | + ((csd_data[10] & 0x80) >> 7); + } else { + csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | + ((csd_data[7] & 0xC0) >> 6); + csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | + csd_data[9]; + csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); + } + + csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; + csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); + csd->WrProtectGrSize = (csd_data[11] & 0x7F); + csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; + csd->Reserved2 = (csd_data[12] & 0x60) >> 5; + csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; + csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); + csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; + csd->Reserved3 = (csd_data[13] & 0x1F); + csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; + csd->CopyFlag = (csd_data[14] & 0x40) >> 6; + csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; + csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; + csd->FileFormat = (csd_data[14] & 0x0C) >> 2; + csd->Reserved4 = (csd_data[14] & 0x03); + csd->crc = (csd_data[15] & 0xFE) >> 1; + csd->Reserved5 = (csd_data[15] & 0x01); + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD10 (SEND_CID): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CID data + for(counter = 0; counter < 16; counter++) { + cid_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + Cid->ManufacturerID = cid_data[0]; + memcpy(Cid->OEM_AppliID, cid_data + 1, 2); + memcpy(Cid->ProdName, cid_data + 3, 5); + Cid->ProdRev = cid_data[8]; + Cid->ProdSN = cid_data[9] << 24; + Cid->ProdSN |= cid_data[10] << 16; + Cid->ProdSN |= cid_data[11] << 8; + Cid->ProdSN |= cid_data[12]; + Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; + Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; + Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; + Cid->Reserved2 = 1; + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static SdSpiStatus + sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + SdSpiStatusOK) { + // Read the data block + sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } else { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_cmd_write_blocks( + uint32_t* data, + uint32_t address, + uint32_t blocks, + uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors) + response = sd_spi_send_cmd( + SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN + // TODO: check bytes count + sd_spi_write_byte(SD_DUMMY_BYTE); + sd_spi_write_byte(SD_DUMMY_BYTE); + + // Send the data start token + sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); + sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // Read data response + SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); + sd_spi_deselect_card_and_purge(); + + if(data_responce != SdSpiDataResponceOK) { + return SdSpiStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return SdSpiStatusOK; +} + +uint8_t sd_max_mount_retry_count() { + return 10; +} + +SdSpiStatus sd_init(bool power_reset) { + // Slow speed init + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; + + // We reset card in spi_lock context, so it is safe to disturb spi bus + if(power_reset) { + sd_spi_debug("Power reset"); + + // disable power and set low on all bus pins + furi_hal_power_disable_external_3_3v(); + sd_spi_bus_to_ground(); + hal_sd_detect_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + hal_sd_detect_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + SdSpiStatus status = SdSpiStatusError; + + // Send 80 dummy clocks with CS high + sd_spi_deselect_card(); + for(uint8_t i = 0; i < 80; i++) { + sd_spi_write_byte(SD_DUMMY_BYTE); + } + + for(uint8_t i = 0; i < 128; i++) { + status = sd_spi_init_spi_mode(); + if(status == SdSpiStatusOK) { + // SD initialized and init to SPI mode properly + sd_spi_debug("SD init OK after %d retries", i); + break; + } + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + + // Init sector cache + sector_cache_init(); + + return status; +} + +SdSpiStatus sd_get_card_state(void) { + SdSpiCmdAnswer response; + + // Send CMD13 (SEND_STATUS) to get SD status + response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); + sd_spi_deselect_card_and_purge(); + + // Return status OK if response is valid + if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { + return SdSpiStatusOK; + } + + return SdSpiStatusError; +} + +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { + SdSpiStatus status; + + status = sd_spi_get_csd(&(card_info->Csd)); + + if(status != SdSpiStatusOK) { + return status; + } + + status = sd_spi_get_cid(&(card_info->Cid)); + + if(status != SdSpiStatusOK) { + return status; + } + + if(sd_high_capacity == 1) { + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 512; + card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)card_info->LogBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } else { + card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1); + card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2)); + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen); + card_info->CardCapacity *= card_info->CardBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } + + return status; +} + +SdSpiStatus + sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + SdSpiStatus status = SdSpiStatusError; + + bool single_sector_read = (blocks == 1); + + if(single_sector_read) { + if(sd_cache_get(address, data)) { + return SdSpiStatusOK; + } + + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + + if(status == SdSpiStatusOK) { + sd_cache_put(address, data); + } + } else { + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + } + + return status; +} + +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + sd_cache_invalidate_range(address, address + blocks); + SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); + return status; +} + +SdSpiStatus sd_get_cid(SD_CID* cid) { + SdSpiStatus status; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + memset(cid, 0, sizeof(SD_CID)); + status = sd_spi_get_cid(cid); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h new file mode 100644 index 000000000..8850eceb7 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -0,0 +1,157 @@ +#pragma once +#include +#include + +#define __IO volatile + +#define SD_TIMEOUT_MS (1000) + +typedef enum { + SdSpiStatusOK, + SdSpiStatusError, + SdSpiStatusTimeout, +} SdSpiStatus; + +/** + * @brief Card Specific Data: CSD Register + */ +typedef struct { + /* Header part */ + uint8_t CSDStruct : 2; /* CSD structure */ + uint8_t Reserved1 : 6; /* Reserved */ + uint8_t TAAC : 8; /* Data read access-time 1 */ + uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ + uint16_t CardComdClasses : 12; /* Card command classes */ + uint8_t RdBlockLen : 4; /* Max. read data block length */ + uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ + uint8_t WrBlockMisalign : 1; /* Write block misalignment */ + uint8_t RdBlockMisalign : 1; /* Read block misalignment */ + uint8_t DSRImpl : 1; /* DSR implemented */ + + /* v1 or v2 struct */ + union csd_version { + struct { + uint8_t Reserved1 : 2; /* Reserved */ + uint16_t DeviceSize : 12; /* Device Size */ + uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ + uint8_t DeviceSizeMul : 3; /* Device size multiplier */ + } v1; + struct { + uint8_t Reserved1 : 6; /* Reserved */ + uint32_t DeviceSize : 22; /* Device Size */ + uint8_t Reserved2 : 1; /* Reserved */ + } v2; + } version; + + uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ + uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ + uint8_t WrProtectGrSize : 7; /* Write protect group size */ + uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ + uint8_t Reserved2 : 2; /* Reserved */ + uint8_t WrSpeedFact : 3; /* Write speed factor */ + uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ + uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ + uint8_t Reserved3 : 5; /* Reserved */ + uint8_t FileFormatGrouop : 1; /* File format group */ + uint8_t CopyFlag : 1; /* Copy flag (OTP) */ + uint8_t PermWrProtect : 1; /* Permanent write protection */ + uint8_t TempWrProtect : 1; /* Temporary write protection */ + uint8_t FileFormat : 2; /* File Format */ + uint8_t Reserved4 : 2; /* Reserved */ + uint8_t crc : 7; /* Reserved */ + uint8_t Reserved5 : 1; /* always 1*/ + +} SD_CSD; + +/** + * @brief Card Identification Data: CID Register + */ +typedef struct { + uint8_t ManufacturerID; /* ManufacturerID */ + char OEM_AppliID[2]; /* OEM/Application ID */ + char ProdName[5]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + uint32_t ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint8_t ManufactYear; /* Manufacturing Year */ + uint8_t ManufactMonth; /* Manufacturing Month */ + uint8_t CID_CRC; /* CID CRC */ + uint8_t Reserved2; /* always 1 */ +} SD_CID; + +/** + * @brief SD Card information structure + */ +typedef struct { + SD_CSD Csd; + SD_CID Cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ + uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ +} SD_CardInfo; + +/** + * @brief SD card max mount retry count + * + * @return uint8_t + */ +uint8_t sd_max_mount_retry_count(); + +/** + * @brief Init sd card + * + * @param power_reset reset card power + * @return SdSpiStatus + */ +SdSpiStatus sd_init(bool power_reset); + +/** + * @brief Get card state + * + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_state(void); + +/** + * @brief Get card info + * + * @param card_info + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info); + +/** + * @brief Read blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Write blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Get card CSD register + * + * @param Cid + * @return SdSpiStatus + */ +SdSpiStatus sd_get_cid(SD_CID* cid); \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sector_cache.c b/firmware/targets/f7/fatfs/sector_cache.c index d23c1d5ad..efb520ec8 100644 --- a/firmware/targets/f7/fatfs/sector_cache.c +++ b/firmware/targets/f7/fatfs/sector_cache.c @@ -8,7 +8,6 @@ #define SECTOR_SIZE 512 #define N_SECTORS 8 -#define TAG "SDCache" typedef struct { uint32_t itr; @@ -20,15 +19,11 @@ static SectorCache* cache = NULL; void sector_cache_init() { if(cache == NULL) { - // TODO: tuneup allocation order, to place cache in mem pool (MEM2) cache = memmgr_alloc_from_pool(sizeof(SectorCache)); } if(cache != NULL) { - FURI_LOG_I(TAG, "Init"); memset(cache, 0, sizeof(SectorCache)); - } else { - FURI_LOG_E(TAG, "Init failed"); } } diff --git a/firmware/targets/f7/fatfs/spi_sd_hal.c b/firmware/targets/f7/fatfs/spi_sd_hal.c deleted file mode 100644 index bfe046b58..000000000 --- a/firmware/targets/f7/fatfs/spi_sd_hal.c +++ /dev/null @@ -1,98 +0,0 @@ -#include -#include - -#define SD_DUMMY_BYTE 0xFF - -const uint32_t SpiTimeout = 1000; -uint8_t SD_IO_WriteByte(uint8_t Data); - -/****************************************************************************** - BUS OPERATIONS - *******************************************************************************/ - -/** - * @brief SPI Write byte(s) to device - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -static void SPIx_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - furi_check(furi_hal_spi_bus_trx( - furi_hal_sd_spi_handle, (uint8_t*)DataIn, DataOut, DataLength, SpiTimeout)); -} - -/** - * @brief SPI Write a byte to device - * @param Value: value to be written - * @retval None - */ -__attribute__((unused)) static void SPIx_Write(uint8_t Value) { - furi_check(furi_hal_spi_bus_tx(furi_hal_sd_spi_handle, (uint8_t*)&Value, 1, SpiTimeout)); -} - -/****************************************************************************** - LINK OPERATIONS - *******************************************************************************/ - -/********************************* LINK SD ************************************/ -/** - * @brief Initialize the SD Card and put it into StandBy State (Ready for - * data transfer). - * @retval None - */ -void SD_IO_Init(void) { - uint8_t counter = 0; - - /* SD chip select high */ - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - furi_delay_us(10); - - /* Send dummy byte 0xFF, 10 times with CS high */ - /* Rise CS and MOSI for 80 clocks cycles */ - for(counter = 0; counter <= 200; counter++) { - /* Send dummy byte 0xFF */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - } -} - -/** - * @brief Set SD interface Chip Select state - * @param val: 0 (low) or 1 (high) state - * @retval None - */ -void SD_IO_CSState(uint8_t val) { - /* Some SD Cards are prone to fail if CLK-ed too soon after CS transition. Worst case found: 8us */ - if(val == 1) { - furi_delay_us(10); // Exit guard time for some SD cards - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - } else { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_delay_us(10); // Entry guard time for some SD cards - } -} - -/** - * @brief Write byte(s) on the SD - * @param DataIn: Pointer to data buffer to write - * @param DataOut: Pointer to data buffer for read data - * @param DataLength: number of bytes to write - * @retval None - */ -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength) { - /* Send the byte */ - SPIx_WriteReadData(DataIn, DataOut, DataLength); -} - -/** - * @brief Write a byte on the SD. - * @param Data: byte to send. - * @retval Data written - */ -uint8_t SD_IO_WriteByte(uint8_t Data) { - uint8_t tmp; - - /* Send the byte */ - SPIx_WriteReadData(&Data, &tmp, 1); - return tmp; -} diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c b/firmware/targets/f7/fatfs/stm32_adafruit_sd.c deleted file mode 100644 index 998adee29..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.c +++ /dev/null @@ -1,1113 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.c - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file provides a set of functions needed to manage the SD card - * mounted on the Adafruit 1.8" TFT LCD shield (reference ID 802), - * that is used with the STM32 Nucleo board through SPI interface. - * It implements a high level communication layer for read and write - * from/to this memory. The needed STM32XXxx hardware resources (SPI and - * GPIO) are defined in stm32XXxx_nucleo.h file, and the initialization is - * performed in SD_IO_Init() function declared in stm32XXxx_nucleo.c - * file. - * You can easily tailor this driver to any other development board, - * by just adapting the defines for hardware resources and - * SD_IO_Init() function. - * - * +-------------------------------------------------------+ - * | Pin assignment | - * +-------------------------+---------------+-------------+ - * | STM32XXxx SPI Pins | SD | Pin | - * +-------------------------+---------------+-------------+ - * | SD_SPI_CS_PIN | ChipSelect | 1 | - * | SD_SPI_MOSI_PIN / MOSI | DataIn | 2 | - * | | GND | 3 (0 V) | - * | | VDD | 4 (3.3 V)| - * | SD_SPI_SCK_PIN / SCLK | Clock | 5 | - * | | GND | 6 (0 V) | - * | SD_SPI_MISO_PIN / MISO | DataOut | 7 | - * +-------------------------+---------------+-------------+ - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* File Info : ----------------------------------------------------------------- - User NOTES -1. How to use this driver: --------------------------- - - This driver does not need a specific component driver for the micro SD device - to be included with. - -2. Driver description: ---------------------- - + Initialization steps: - o Initialize the micro SD card using the BSP_SD_Init() function. - o Checking the SD card presence is not managed because SD detection pin is - not physically mapped on the Adafruit shield. - o The function BSP_SD_GetCardInfo() is used to get the micro SD card information - which is stored in the structure "SD_CardInfo". - - + Micro SD card operations - o The micro SD card can be accessed with read/write block(s) operations once - it is ready for access. The access can be performed in polling - mode by calling the functions BSP_SD_ReadBlocks()/BSP_SD_WriteBlocks() - - o The SD erase block(s) is performed using the function BSP_SD_Erase() with - specifying the number of blocks to erase. - o The SD runtime status is returned when calling the function BSP_SD_GetStatus(). - -------------------------------------------------------------------------------*/ - -/* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" -#include "stdlib.h" -#include "string.h" -#include "stdio.h" -#include -#include "sector_cache.h" - -/** @addtogroup BSP - * @{ - */ - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/* Private typedef -----------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Types_Definitions - * @{ - */ -typedef struct { - uint8_t r1; - uint8_t r2; - uint8_t r3; - uint8_t r4; - uint8_t r5; -} SD_CmdAnswer_typedef; - -/** - * @} - */ - -/* Private define ------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Defines - * @{ - */ -#define SD_DUMMY_BYTE 0xFF - -#define SD_MAX_FRAME_LENGTH 17 /* Lenght = 16 + 1 */ -#define SD_CMD_LENGTH 6 - -#define SD_MAX_TRY 100 /* Number of try */ - -#define SD_CSD_STRUCT_V1 0x2 /* CSD struct version V1 */ -#define SD_CSD_STRUCT_V2 0x1 /* CSD struct version V2 */ - -/** - * @brief SD ansewer format - */ -typedef enum { - SD_ANSWER_R1_EXPECTED, - SD_ANSWER_R1B_EXPECTED, - SD_ANSWER_R2_EXPECTED, - SD_ANSWER_R3_EXPECTED, - SD_ANSWER_R4R5_EXPECTED, - SD_ANSWER_R7_EXPECTED, -} SD_Answer_type; - -/** - * @brief Start Data tokens: - * Tokens (necessary because at nop/idle (and CS active) only 0xff is - * on the data/command line) - */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Single Block Read */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ \ - 0xFE /* Data token start byte, Start Multiple Block Read */ -#define SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE \ - 0xFE /* Data token start byte, Start Single Block Write */ -#define SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data token start byte, Start Multiple Block Write */ -#define SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE \ - 0xFD /* Data toke stop byte, Stop Multiple Block Write */ - -/** - * @brief Commands: CMDxx = CMD-number | 0x40 - */ -#define SD_CMD_GO_IDLE_STATE 0 /* CMD0 = 0x40 */ -#define SD_CMD_SEND_OP_COND 1 /* CMD1 = 0x41 */ -#define SD_CMD_SEND_IF_COND 8 /* CMD8 = 0x48 */ -#define SD_CMD_SEND_CSD 9 /* CMD9 = 0x49 */ -#define SD_CMD_SEND_CID 10 /* CMD10 = 0x4A */ -#define SD_CMD_STOP_TRANSMISSION 12 /* CMD12 = 0x4C */ -#define SD_CMD_SEND_STATUS 13 /* CMD13 = 0x4D */ -#define SD_CMD_SET_BLOCKLEN 16 /* CMD16 = 0x50 */ -#define SD_CMD_READ_SINGLE_BLOCK 17 /* CMD17 = 0x51 */ -#define SD_CMD_READ_MULT_BLOCK 18 /* CMD18 = 0x52 */ -#define SD_CMD_SET_BLOCK_COUNT 23 /* CMD23 = 0x57 */ -#define SD_CMD_WRITE_SINGLE_BLOCK 24 /* CMD24 = 0x58 */ -#define SD_CMD_WRITE_MULT_BLOCK 25 /* CMD25 = 0x59 */ -#define SD_CMD_PROG_CSD 27 /* CMD27 = 0x5B */ -#define SD_CMD_SET_WRITE_PROT 28 /* CMD28 = 0x5C */ -#define SD_CMD_CLR_WRITE_PROT 29 /* CMD29 = 0x5D */ -#define SD_CMD_SEND_WRITE_PROT 30 /* CMD30 = 0x5E */ -#define SD_CMD_SD_ERASE_GRP_START 32 /* CMD32 = 0x60 */ -#define SD_CMD_SD_ERASE_GRP_END 33 /* CMD33 = 0x61 */ -#define SD_CMD_UNTAG_SECTOR 34 /* CMD34 = 0x62 */ -#define SD_CMD_ERASE_GRP_START 35 /* CMD35 = 0x63 */ -#define SD_CMD_ERASE_GRP_END 36 /* CMD36 = 0x64 */ -#define SD_CMD_UNTAG_ERASE_GROUP 37 /* CMD37 = 0x65 */ -#define SD_CMD_ERASE 38 /* CMD38 = 0x66 */ -#define SD_CMD_SD_APP_OP_COND 41 /* CMD41 = 0x69 */ -#define SD_CMD_APP_CMD 55 /* CMD55 = 0x77 */ -#define SD_CMD_READ_OCR 58 /* CMD55 = 0x79 */ - -/** - * @brief SD reponses and error flags - */ -typedef enum { - /* R1 answer value */ - SD_R1_NO_ERROR = (0x00), - SD_R1_IN_IDLE_STATE = (0x01), - SD_R1_ERASE_RESET = (0x02), - SD_R1_ILLEGAL_COMMAND = (0x04), - SD_R1_COM_CRC_ERROR = (0x08), - SD_R1_ERASE_SEQUENCE_ERROR = (0x10), - SD_R1_ADDRESS_ERROR = (0x20), - SD_R1_PARAMETER_ERROR = (0x40), - - /* R2 answer value */ - SD_R2_NO_ERROR = 0x00, - SD_R2_CARD_LOCKED = 0x01, - SD_R2_LOCKUNLOCK_ERROR = 0x02, - SD_R2_ERROR = 0x04, - SD_R2_CC_ERROR = 0x08, - SD_R2_CARD_ECC_FAILED = 0x10, - SD_R2_WP_VIOLATION = 0x20, - SD_R2_ERASE_PARAM = 0x40, - SD_R2_OUTOFRANGE = 0x80, - - /** - * @brief Data response error - */ - SD_DATA_OK = (0x05), - SD_DATA_CRC_ERROR = (0x0B), - SD_DATA_WRITE_ERROR = (0x0D), - SD_DATA_OTHER_ERROR = (0xFF) -} SD_Error; - -/** - * @} - */ - -/* Private macro -------------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Macros - * @{ - */ - -/** - * @} - */ - -/* Private variables ---------------------------------------------------------*/ - -/** @defgroup STM32_ADAFRUIT_SD_Private_Variables - * @{ - */ -__IO uint8_t SdStatus = SD_NOT_PRESENT; - -/* flag_SDHC : - 0 : Standard capacity - 1 : High capacity -*/ -uint16_t flag_SDHC = 0; - -/** - * @} - */ - -/* Private function prototypes -----------------------------------------------*/ -static uint8_t SD_GetCIDRegister(SD_CID* Cid); -static uint8_t SD_GetCSDRegister(SD_CSD* Csd); -static uint8_t SD_GetDataResponse(void); -static uint8_t SD_GoIdleState(void); -static SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer); -static uint8_t SD_WaitData(uint8_t data); -static uint8_t SD_ReadData(void); -/** @defgroup STM32_ADAFRUIT_SD_Private_Function_Prototypes - * @{ - */ -/** - * @} - */ - -/* Private functions ---------------------------------------------------------*/ - -void SD_SPI_Bus_To_Down_State() { - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeOutputPushPull, - GpioPullNo, - GpioSpeedVeryHigh, - GpioAltFnUnused); - - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); - furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); -} - -void SD_SPI_Bus_To_Normal_State() { - furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); - - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->miso, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->mosi, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); - furi_hal_gpio_init_ex( - furi_hal_sd_spi_handle->sck, - GpioModeAltFunctionPushPull, - GpioPullUp, - GpioSpeedVeryHigh, - GpioAltFn5SPI2); -} - -/** @defgroup STM32_ADAFRUIT_SD_Private_Functions - * @{ - */ - -uint8_t BSP_SD_MaxMountRetryCount() { - return 10; -} - -/** - * @brief Initializes the SD/SD communication. - * @param None - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_Init(bool reset_card) { - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - /* We must reset card in spi_lock context */ - if(reset_card) { - /* disable power and set low on all bus pins */ - furi_hal_power_disable_external_3_3v(); - SD_SPI_Bus_To_Down_State(); - hal_sd_detect_set_low(); - furi_delay_ms(250); - - /* reinit bus and enable power */ - SD_SPI_Bus_To_Normal_State(); - hal_sd_detect_init(); - furi_hal_power_enable_external_3_3v(); - furi_delay_ms(100); - } - - /* Configure IO functionalities for SD pin */ - SD_IO_Init(); - - /* SD detection pin is not physically mapped on the Adafruit shield */ - SdStatus = SD_PRESENT; - uint8_t res = BSP_SD_ERROR; - - for(uint8_t i = 0; i < 128; i++) { - res = SD_GoIdleState(); - if(res == BSP_SD_OK) break; - } - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - - sector_cache_init(); - - /* SD initialized and set to SPI mode properly */ - return res; -} - -/** - * @brief Returns information about specific card. - * @param pCardInfo: Pointer to a SD_CardInfo structure that contains all SD - * card information. - * @retval The SD Response: - * - MSD_ERROR: Sequence failed - * - MSD_OK: Sequence succeed - */ -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo) { - uint8_t status; - - status = SD_GetCSDRegister(&(pCardInfo->Csd)); - status |= SD_GetCIDRegister(&(pCardInfo->Cid)); - if(flag_SDHC == 1) { - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 512; - pCardInfo->CardCapacity = ((uint64_t)pCardInfo->Csd.version.v2.DeviceSize + 1UL) * 1024UL * - (uint64_t)pCardInfo->LogBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } else { - pCardInfo->CardCapacity = (pCardInfo->Csd.version.v1.DeviceSize + 1); - pCardInfo->CardCapacity *= (1UL << (pCardInfo->Csd.version.v1.DeviceSizeMul + 2)); - pCardInfo->LogBlockSize = 512; - pCardInfo->CardBlockSize = 1UL << (pCardInfo->Csd.RdBlockLen); - pCardInfo->CardCapacity *= pCardInfo->CardBlockSize; - pCardInfo->LogBlockNbr = (pCardInfo->CardCapacity) / (pCardInfo->LogBlockSize); - } - - return status; -} - -/** - * @brief Reads block(s) from a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param ReadAddr: Address from where data is to be read. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to read - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - uint8_t* cached_data; - - bool single_sector_read = (NumOfBlocks == 1); - if(single_sector_read && (cached_data = sector_cache_get(ReadAddr))) { - memcpy(pData, cached_data, BlockSize); - return BSP_SD_OK; - } - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (ReadAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD17 (SD_CMD_READ_SINGLE_BLOCK) to read one block */ - /* Check if the SD acknowledged the read block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Now look for the data token to signify the start of the data */ - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Read the SD block data : read NumByteToRead data */ - SD_IO_WriteReadData(NULL, (uint8_t*)pData + offset, BlockSize); - - /* Set next read address*/ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } else { - goto error; - } - - /* End the command data read cycle */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - if(single_sector_read) { - sector_cache_put(ReadAddr, (uint8_t*)pData); - } - - retr = BSP_SD_OK; - -error: - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Writes block(s) to a specified address in the SD card, in polling mode. - * @param pData: Pointer to the buffer that will contain the data to transmit - * @param WriteAddr: Address from where data is to be written. The address is counted - * in blocks of 512bytes - * @param NumOfBlocks: Number of SD blocks to write - * @param Timeout: This parameter is used for compatibility with BSP implementation - * @retval SD status - */ -uint8_t BSP_SD_WriteBlocks( - uint32_t* pData, - uint32_t WriteAddr, - uint32_t NumOfBlocks, - uint32_t Timeout) { - UNUSED(Timeout); // FIXME! - uint32_t offset = 0; - uint32_t addr; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - sector_cache_invalidate_range(WriteAddr, WriteAddr + NumOfBlocks); - - /* Send CMD16 (SD_CMD_SET_BLOCKLEN) to set the size of the block and - Check if the SD acknowledged the set block length command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_SET_BLOCKLEN, BlockSize, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Initialize the address */ - addr = (WriteAddr * ((flag_SDHC == 1) ? 1 : BlockSize)); - - /* Data transfer */ - while(NumOfBlocks--) { - /* Send CMD24 (SD_CMD_WRITE_SINGLE_BLOCK) to write blocks and - Check if the SD acknowledged the write block command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_WRITE_SINGLE_BLOCK, addr, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 != SD_R1_NO_ERROR) { - goto error; - } - - /* Send dummy byte for NWR timing : one byte between CMDWRITE and TOKEN */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send the data token to signify the start of the data */ - SD_IO_WriteByte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); - - /* Write the block data to SD */ - SD_IO_WriteReadData((uint8_t*)pData + offset, NULL, BlockSize); - - /* Set next write address */ - offset += BlockSize; - addr = ((flag_SDHC == 1) ? (addr + 1) : (addr + BlockSize)); - - /* Put CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Read data response */ - if(SD_GetDataResponse() != SD_DATA_OK) { - /* Set response value to failure */ - goto error; - } - - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - retr = BSP_SD_OK; - -error: - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Erases the specified memory area of the given SD card. - * @param StartAddr: Start address in Blocks (Size of a block is 512bytes) - * @param EndAddr: End address in Blocks (Size of a block is 512bytes) - * @retval SD status - */ -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) { - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - uint16_t BlockSize = 512; - - /* Send CMD32 (Erase group start) and check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_START, - (StartAddr) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD33 (Erase group end) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd( - SD_CMD_SD_ERASE_GRP_END, - (EndAddr * 512) * (flag_SDHC == 1 ? 1 : BlockSize), - 0xFF, - SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 == SD_R1_NO_ERROR) { - /* Send CMD38 (Erase) and Check if the SD acknowledged the erase command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_ERASE, 0, 0xFF, SD_ANSWER_R1B_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - retr = BSP_SD_OK; - } - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - } - } - - /* Return the reponse */ - return retr; -} - -/** - * @brief Returns the SD status. - * @param None - * @retval The SD status. - */ -uint8_t BSP_SD_GetCardState(void) { - SD_CmdAnswer_typedef retr; - - /* Send CMD13 (SD_SEND_STATUS) to get SD status */ - retr = SD_SendCmd(SD_CMD_SEND_STATUS, 0, 0xFF, SD_ANSWER_R2_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Find SD status according to card state */ - if((retr.r1 == SD_R1_NO_ERROR) && (retr.r2 == SD_R2_NO_ERROR)) { - return BSP_SD_OK; - } - - return BSP_SD_ERROR; -} - -/** - * @brief Reads the SD card SCD register. - * Reading the contents of the CSD register in SPI mode is a simple - * read-block transaction. - * @param Csd: pointer on an SCD register structure - * @retval SD status - */ -uint8_t SD_GetCSDRegister(SD_CSD* Csd) { - uint16_t counter = 0; - uint8_t CSD_Tab[16]; - uint8_t retr = BSP_SD_ERROR; - SD_CmdAnswer_typedef response; - - /* Send CMD9 (CSD register) or CMD10(CSD register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CSD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - for(counter = 0; counter < 16; counter++) { - /* Store CSD register value on CSD_Tab */ - CSD_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /************************************************************************* - CSD header decoding - *************************************************************************/ - - /* Byte 0 */ - Csd->CSDStruct = (CSD_Tab[0] & 0xC0) >> 6; - Csd->Reserved1 = CSD_Tab[0] & 0x3F; - - /* Byte 1 */ - Csd->TAAC = CSD_Tab[1]; - - /* Byte 2 */ - Csd->NSAC = CSD_Tab[2]; - - /* Byte 3 */ - Csd->MaxBusClkFrec = CSD_Tab[3]; - - /* Byte 4/5 */ - Csd->CardComdClasses = (CSD_Tab[4] << 4) | ((CSD_Tab[5] & 0xF0) >> 4); - Csd->RdBlockLen = CSD_Tab[5] & 0x0F; - - /* Byte 6 */ - Csd->PartBlockRead = (CSD_Tab[6] & 0x80) >> 7; - Csd->WrBlockMisalign = (CSD_Tab[6] & 0x40) >> 6; - Csd->RdBlockMisalign = (CSD_Tab[6] & 0x20) >> 5; - Csd->DSRImpl = (CSD_Tab[6] & 0x10) >> 4; - - /************************************************************************* - CSD v1/v2 decoding - *************************************************************************/ - - if(flag_SDHC == 0) { - Csd->version.v1.Reserved1 = ((CSD_Tab[6] & 0x0C) >> 2); - - Csd->version.v1.DeviceSize = ((CSD_Tab[6] & 0x03) << 10) | (CSD_Tab[7] << 2) | - ((CSD_Tab[8] & 0xC0) >> 6); - Csd->version.v1.MaxRdCurrentVDDMin = (CSD_Tab[8] & 0x38) >> 3; - Csd->version.v1.MaxRdCurrentVDDMax = (CSD_Tab[8] & 0x07); - Csd->version.v1.MaxWrCurrentVDDMin = (CSD_Tab[9] & 0xE0) >> 5; - Csd->version.v1.MaxWrCurrentVDDMax = (CSD_Tab[9] & 0x1C) >> 2; - Csd->version.v1.DeviceSizeMul = ((CSD_Tab[9] & 0x03) << 1) | - ((CSD_Tab[10] & 0x80) >> 7); - } else { - Csd->version.v2.Reserved1 = ((CSD_Tab[6] & 0x0F) << 2) | - ((CSD_Tab[7] & 0xC0) >> 6); - Csd->version.v2.DeviceSize = ((CSD_Tab[7] & 0x3F) << 16) | (CSD_Tab[8] << 8) | - CSD_Tab[9]; - Csd->version.v2.Reserved2 = ((CSD_Tab[10] & 0x80) >> 8); - } - - Csd->EraseSingleBlockEnable = (CSD_Tab[10] & 0x40) >> 6; - Csd->EraseSectorSize = ((CSD_Tab[10] & 0x3F) << 1) | ((CSD_Tab[11] & 0x80) >> 7); - Csd->WrProtectGrSize = (CSD_Tab[11] & 0x7F); - Csd->WrProtectGrEnable = (CSD_Tab[12] & 0x80) >> 7; - Csd->Reserved2 = (CSD_Tab[12] & 0x60) >> 5; - Csd->WrSpeedFact = (CSD_Tab[12] & 0x1C) >> 2; - Csd->MaxWrBlockLen = ((CSD_Tab[12] & 0x03) << 2) | ((CSD_Tab[13] & 0xC0) >> 6); - Csd->WriteBlockPartial = (CSD_Tab[13] & 0x20) >> 5; - Csd->Reserved3 = (CSD_Tab[13] & 0x1F); - Csd->FileFormatGrouop = (CSD_Tab[14] & 0x80) >> 7; - Csd->CopyFlag = (CSD_Tab[14] & 0x40) >> 6; - Csd->PermWrProtect = (CSD_Tab[14] & 0x20) >> 5; - Csd->TempWrProtect = (CSD_Tab[14] & 0x10) >> 4; - Csd->FileFormat = (CSD_Tab[14] & 0x0C) >> 2; - Csd->Reserved4 = (CSD_Tab[14] & 0x03); - Csd->crc = (CSD_Tab[15] & 0xFE) >> 1; - Csd->Reserved5 = (CSD_Tab[15] & 0x01); - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -/** - * @brief Reads the SD card CID register. - * Reading the contents of the CID register in SPI mode is a simple - * read-block transaction. - * @param Cid: pointer on an CID register structure - * @retval SD status - */ -uint8_t SD_GetCIDRegister(SD_CID* Cid) { - uint32_t counter = 0; - uint8_t retr = BSP_SD_ERROR; - uint8_t CID_Tab[16]; - SD_CmdAnswer_typedef response; - - /* Send CMD10 (CID register) and Wait for response in the R1 format (0x00 is no errors) */ - response = SD_SendCmd(SD_CMD_SEND_CID, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - if(response.r1 == SD_R1_NO_ERROR) { - if(SD_WaitData(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ) == BSP_SD_OK) { - /* Store CID register value on CID_Tab */ - for(counter = 0; counter < 16; counter++) { - CID_Tab[counter] = SD_IO_WriteByte(SD_DUMMY_BYTE); - } - - /* Get CRC bytes (not really needed by us, but required by SD) */ - SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Byte 0 */ - Cid->ManufacturerID = CID_Tab[0]; - - /* Byte 1 */ - memcpy(Cid->OEM_AppliID, CID_Tab + 1, 2); - - /* Byte 3 */ - memcpy(Cid->ProdName, CID_Tab + 3, 5); - - /* Byte 8 */ - Cid->ProdRev = CID_Tab[8]; - - /* Byte 9 */ - Cid->ProdSN = CID_Tab[9] << 24; - - /* Byte 10 */ - Cid->ProdSN |= CID_Tab[10] << 16; - - /* Byte 11 */ - Cid->ProdSN |= CID_Tab[11] << 8; - - /* Byte 12 */ - Cid->ProdSN |= CID_Tab[12]; - - /* Byte 13 */ - Cid->Reserved1 = (CID_Tab[13] & 0xF0) >> 4; - Cid->ManufactYear = (CID_Tab[13] & 0x0F) << 4; - - /* Byte 14 */ - Cid->ManufactYear |= (CID_Tab[14] & 0xF0) >> 4; - Cid->ManufactMonth = (CID_Tab[14] & 0x0F); - - /* Byte 15 */ - Cid->CID_CRC = (CID_Tab[15] & 0xFE) >> 1; - Cid->Reserved2 = 1; - - retr = BSP_SD_OK; - } - } - - /* Send dummy byte: 8 Clock pulses of delay */ - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Return the reponse */ - return retr; -} - -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid) { - uint8_t retr = BSP_SD_ERROR; - - /* Slow speed init */ - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); - furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; - - memset(Cid, 0, sizeof(SD_CID)); - retr = SD_GetCIDRegister(Cid); - - furi_hal_sd_spi_handle = NULL; - furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); - return retr; -} - -/** - * @brief Sends 5 bytes command to the SD card and get response - * @param Cmd: The user expected command to send to SD card. - * @param Arg: The command argument. - * @param Crc: The CRC. - * @param Answer: SD_ANSWER_NOT_EXPECTED or SD_ANSWER_EXPECTED - * @retval SD status - */ -SD_CmdAnswer_typedef SD_SendCmd(uint8_t Cmd, uint32_t Arg, uint8_t Crc, uint8_t Answer) { - uint8_t frame[SD_CMD_LENGTH], frameout[SD_CMD_LENGTH]; - SD_CmdAnswer_typedef retr = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* R1 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes */ - /* R1b identical to R1 + Busy information */ - /* R2 Lenght = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes */ - - /* Prepare Frame to send */ - frame[0] = (Cmd | 0x40); /* Construct byte 1 */ - frame[1] = (uint8_t)(Arg >> 24); /* Construct byte 2 */ - frame[2] = (uint8_t)(Arg >> 16); /* Construct byte 3 */ - frame[3] = (uint8_t)(Arg >> 8); /* Construct byte 4 */ - frame[4] = (uint8_t)(Arg); /* Construct byte 5 */ - frame[5] = (Crc | 0x01); /* Construct byte 6 */ - - /* Send the command */ - SD_IO_CSState(0); - SD_IO_WriteReadData(frame, frameout, SD_CMD_LENGTH); /* Send the Cmd bytes */ - - switch(Answer) { - case SD_ANSWER_R1_EXPECTED: - retr.r1 = SD_ReadData(); - break; - case SD_ANSWER_R1B_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - /* Set CS High */ - SD_IO_CSState(1); - furi_delay_us(1000); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_ANSWER_R2_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - case SD_ANSWER_R3_EXPECTED: - case SD_ANSWER_R7_EXPECTED: - retr.r1 = SD_ReadData(); - retr.r2 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r3 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r4 = SD_IO_WriteByte(SD_DUMMY_BYTE); - retr.r5 = SD_IO_WriteByte(SD_DUMMY_BYTE); - break; - default: - break; - } - return retr; -} - -/** - * @brief Gets the SD card data response and check the busy flag. - * @param None - * @retval The SD status: Read data response xxx01 - * - status 010: Data accecpted - * - status 101: Data rejected due to a crc error - * - status 110: Data rejected due to a Write error. - * - status 111: Data rejected due to other error. - */ -uint8_t SD_GetDataResponse(void) { - uint8_t dataresponse; - uint8_t rvalue = SD_DATA_OTHER_ERROR; - - dataresponse = SD_IO_WriteByte(SD_DUMMY_BYTE); - SD_IO_WriteByte(SD_DUMMY_BYTE); /* read the busy response byte*/ - - /* Mask unused bits */ - switch(dataresponse & 0x1F) { - case SD_DATA_OK: - rvalue = SD_DATA_OK; - - /* Set CS High */ - SD_IO_CSState(1); - /* Set CS Low */ - SD_IO_CSState(0); - - /* Wait IO line return 0xFF */ - while(SD_IO_WriteByte(SD_DUMMY_BYTE) != 0xFF) - ; - break; - case SD_DATA_CRC_ERROR: - rvalue = SD_DATA_CRC_ERROR; - break; - case SD_DATA_WRITE_ERROR: - rvalue = SD_DATA_WRITE_ERROR; - break; - default: - break; - } - - /* Return response */ - return rvalue; -} - -/** - * @brief Put the SD in Idle state. - * @param None - * @retval SD status - */ -uint8_t SD_GoIdleState(void) { - SD_CmdAnswer_typedef response; - __IO uint8_t counter; - /* Send CMD0 (SD_CMD_GO_IDLE_STATE) to put SD in SPI mode and - wait for In Idle State Response (R1 Format) equal to 0x01 */ - counter = 0; - do { - counter++; - response = SD_SendCmd(SD_CMD_GO_IDLE_STATE, 0, 0x95, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 != SD_R1_IN_IDLE_STATE); - - /* Send CMD8 (SD_CMD_SEND_IF_COND) to check the power supply status - and wait until response (R7 Format) equal to 0xAA and */ - response = SD_SendCmd(SD_CMD_SEND_IF_COND, 0x1AA, 0x87, SD_ANSWER_R7_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - /* initialise card V1 */ - counter = 0; - do { - counter++; - /* initialise card V1 */ - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = //-V519 - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - flag_SDHC = 0; - } else if(response.r1 == SD_R1_IN_IDLE_STATE) { - /* initialise card V2 */ - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = //-V519 - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x40000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - - if((response.r1 & SD_R1_ILLEGAL_COMMAND) == SD_R1_ILLEGAL_COMMAND) { - counter = 0; - do { - counter++; - /* Send CMD55 (SD_CMD_APP_CMD) before any ACMD command: R1 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_APP_CMD, 0, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_IN_IDLE_STATE) { - return BSP_SD_ERROR; - } - /* Send ACMD41 (SD_CMD_SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) */ - response = - SD_SendCmd(SD_CMD_SD_APP_OP_COND, 0x00000000, 0xFF, SD_ANSWER_R1_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(counter >= SD_MAX_TRY) { - return BSP_SD_ERROR; - } - } while(response.r1 == SD_R1_IN_IDLE_STATE); - } - - /* Send CMD58 (SD_CMD_READ_OCR) to initialize SDHC or SDXC cards: R3 response (0x00: no errors) */ - response = SD_SendCmd(SD_CMD_READ_OCR, 0x00000000, 0xFF, SD_ANSWER_R3_EXPECTED); - SD_IO_CSState(1); - SD_IO_WriteByte(SD_DUMMY_BYTE); - if(response.r1 != SD_R1_NO_ERROR) { - return BSP_SD_ERROR; - } - flag_SDHC = (response.r2 & 0x40) >> 6; - } else { - return BSP_SD_ERROR; - } - - return BSP_SD_OK; -} - -/** - * @brief Waits a data until a value different from SD_DUMMY_BITE - * @param None - * @retval the value read - */ -uint8_t SD_ReadData(void) { - uint8_t timeout = 0x08; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - - } while((readvalue == SD_DUMMY_BYTE) && timeout); - - /* Right response got */ - return readvalue; -} - -/** - * @brief Waits a data from the SD card - * @param data : Expected data from the SD card - * @retval BSP_SD_OK or BSP_SD_TIMEOUT - */ -uint8_t SD_WaitData(uint8_t data) { - uint16_t timeout = 0xFFFF; - uint8_t readvalue; - - /* Check if response is got or a timeout is happen */ - - do { - readvalue = SD_IO_WriteByte(SD_DUMMY_BYTE); - timeout--; - } while((readvalue != data) && timeout); - - if(timeout == 0) { - /* After time out */ - return BSP_SD_TIMEOUT; - } - - /* Right response got */ - return BSP_SD_OK; -} - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h b/firmware/targets/f7/fatfs/stm32_adafruit_sd.h deleted file mode 100644 index a133c5922..000000000 --- a/firmware/targets/f7/fatfs/stm32_adafruit_sd.h +++ /dev/null @@ -1,245 +0,0 @@ -/** - ****************************************************************************** - * @file stm32_adafruit_sd.h - * @author MCD Application Team - * @version V3.0.0 - * @date 23-December-2016 - * @brief This file contains the common defines and functions prototypes for - * the stm32_adafruit_sd.c driver. - ****************************************************************************** - * @attention - * - *

© COPYRIGHT(c) 2016 STMicroelectronics

- * - * Redistribution and use in source and binary forms, with or without modification, - * are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of STMicroelectronics nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __STM32_ADAFRUIT_SD_H -#define __STM32_ADAFRUIT_SD_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include -#include - -/** @addtogroup BSP - * @{ - */ -#define __IO volatile - -/** @addtogroup STM32_ADAFRUIT - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD - * @{ - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Types - * @{ - */ - -/** - * @brief SD status structure definition - */ -enum { BSP_SD_OK = 0x00, MSD_OK = 0x00, BSP_SD_ERROR = 0x01, BSP_SD_TIMEOUT }; - -typedef struct { - uint8_t Reserved1 : 2; /* Reserved */ - uint16_t DeviceSize : 12; /* Device Size */ - uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ - uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ - uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ - uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ - uint8_t DeviceSizeMul : 3; /* Device size multiplier */ -} struct_v1; - -typedef struct { - uint8_t Reserved1 : 6; /* Reserved */ - uint32_t DeviceSize : 22; /* Device Size */ - uint8_t Reserved2 : 1; /* Reserved */ -} struct_v2; - -/** - * @brief Card Specific Data: CSD Register - */ -typedef struct { - /* Header part */ - uint8_t CSDStruct : 2; /* CSD structure */ - uint8_t Reserved1 : 6; /* Reserved */ - uint8_t TAAC : 8; /* Data read access-time 1 */ - uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ - uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ - uint16_t CardComdClasses : 12; /* Card command classes */ - uint8_t RdBlockLen : 4; /* Max. read data block length */ - uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ - uint8_t WrBlockMisalign : 1; /* Write block misalignment */ - uint8_t RdBlockMisalign : 1; /* Read block misalignment */ - uint8_t DSRImpl : 1; /* DSR implemented */ - - /* v1 or v2 struct */ - union csd_version { - struct_v1 v1; - struct_v2 v2; - } version; - - uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ - uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ - uint8_t WrProtectGrSize : 7; /* Write protect group size */ - uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ - uint8_t Reserved2 : 2; /* Reserved */ - uint8_t WrSpeedFact : 3; /* Write speed factor */ - uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ - uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ - uint8_t Reserved3 : 5; /* Reserved */ - uint8_t FileFormatGrouop : 1; /* File format group */ - uint8_t CopyFlag : 1; /* Copy flag (OTP) */ - uint8_t PermWrProtect : 1; /* Permanent write protection */ - uint8_t TempWrProtect : 1; /* Temporary write protection */ - uint8_t FileFormat : 2; /* File Format */ - uint8_t Reserved4 : 2; /* Reserved */ - uint8_t crc : 7; /* Reserved */ - uint8_t Reserved5 : 1; /* always 1*/ - -} SD_CSD; - -/** - * @brief Card Identification Data: CID Register - */ -typedef struct { - uint8_t ManufacturerID; /* ManufacturerID */ - char OEM_AppliID[2]; /* OEM/Application ID */ - char ProdName[5]; /* Product Name */ - uint8_t ProdRev; /* Product Revision */ - uint32_t ProdSN; /* Product Serial Number */ - uint8_t Reserved1; /* Reserved1 */ - uint8_t ManufactYear; /* Manufacturing Year */ - uint8_t ManufactMonth; /* Manufacturing Month */ - uint8_t CID_CRC; /* CID CRC */ - uint8_t Reserved2; /* always 1 */ -} SD_CID; - -/** - * @brief SD Card information - */ -typedef struct { - SD_CSD Csd; - SD_CID Cid; - uint64_t CardCapacity; /*!< Card Capacity */ - uint32_t CardBlockSize; /*!< Card Block Size */ - uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ - uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ -} SD_CardInfo; - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SPI_SD_Exported_Constants - * @{ - */ - -/** - * @brief Block Size - */ -#define SD_BLOCK_SIZE 0x200 - -/** - * @brief SD detection on its memory slot - */ -#define SD_PRESENT ((uint8_t)0x01) -#define SD_NOT_PRESENT ((uint8_t)0x00) - -#define SD_DATATIMEOUT ((uint32_t)100000000) - -/** - * @brief SD Card information structure - */ -#define BSP_SD_CardInfo SD_CardInfo - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Macro - * @{ - */ - -/** - * @} - */ - -/** @defgroup STM32_ADAFRUIT_SD_Exported_Functions - * @{ - */ -uint8_t BSP_SD_MaxMountRetryCount(); -uint8_t BSP_SD_Init(bool reset_card); -uint8_t - BSP_SD_ReadBlocks(uint32_t* pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t - BSP_SD_WriteBlocks(uint32_t* pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); -uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); -uint8_t BSP_SD_GetCardState(void); -uint8_t BSP_SD_GetCardInfo(SD_CardInfo* pCardInfo); -uint8_t BSP_SD_GetCIDRegister(SD_CID* Cid); - -/* Link functions for SD Card peripheral*/ -void SD_SPI_Slow_Init(void); -void SD_SPI_Fast_Init(void); -void SD_IO_Init(void); -void SD_IO_CSState(uint8_t state); -void SD_IO_WriteReadData(const uint8_t* DataIn, uint8_t* DataOut, uint16_t DataLength); -uint8_t SD_IO_WriteByte(uint8_t Data); - -/* Link function for HAL delay */ -void HAL_Delay(__IO uint32_t Delay); - -#ifdef __cplusplus -} -#endif - -#endif /* __STM32_ADAFRUIT_SD_H */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/firmware/targets/f7/fatfs/user_diskio.c b/firmware/targets/f7/fatfs/user_diskio.c index b504fcd51..16ac78e4d 100644 --- a/firmware/targets/f7/fatfs/user_diskio.c +++ b/firmware/targets/f7/fatfs/user_diskio.c @@ -46,7 +46,7 @@ static volatile DSTATUS Stat = STA_NOINIT; static DSTATUS User_CheckStatus(BYTE lun) { UNUSED(lun); Stat = STA_NOINIT; - if(BSP_SD_GetCardState() == MSD_OK) { + if(sd_get_card_state() == SdSpiStatusOK) { Stat &= ~STA_NOINIT; } @@ -128,11 +128,18 @@ DRESULT USER_read(BYTE pdrv, BYTE* buff, DWORD sector, UINT count) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_ReadBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_read_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the read operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; @@ -160,11 +167,18 @@ DRESULT USER_write(BYTE pdrv, const BYTE* buff, DWORD sector, UINT count) { furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; - if(BSP_SD_WriteBlocks((uint32_t*)buff, (uint32_t)(sector), count, SD_DATATIMEOUT) == MSD_OK) { + if(sd_write_blocks((uint32_t*)buff, (uint32_t)(sector), count, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(SD_TIMEOUT_MS * 1000); + /* wait until the Write operation is finished */ - while(BSP_SD_GetCardState() != MSD_OK) { - } res = RES_OK; + while(sd_get_card_state() != SdSpiStatusOK) { + if(furi_hal_cortex_timer_is_expired(timer)) { + res = RES_ERROR; + break; + } + } } furi_hal_sd_spi_handle = NULL; @@ -187,7 +201,7 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* USER CODE BEGIN IOCTL */ UNUSED(pdrv); DRESULT res = RES_ERROR; - BSP_SD_CardInfo CardInfo; + SD_CardInfo CardInfo; if(Stat & STA_NOINIT) return RES_NOTRDY; @@ -202,21 +216,21 @@ DRESULT USER_ioctl(BYTE pdrv, BYTE cmd, void* buff) { /* Get number of sectors on the disk (DWORD) */ case GET_SECTOR_COUNT: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockNbr; res = RES_OK; break; /* Get R/W sector size (WORD) */ case GET_SECTOR_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(WORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; /* Get erase block size in unit of sector (DWORD) */ case GET_BLOCK_SIZE: - BSP_SD_GetCardInfo(&CardInfo); + sd_get_card_info(&CardInfo); *(DWORD*)buff = CardInfo.LogBlockSize; res = RES_OK; break; diff --git a/firmware/targets/f7/fatfs/user_diskio.h b/firmware/targets/f7/fatfs/user_diskio.h index 177723be1..12e0f27dc 100644 --- a/firmware/targets/f7/fatfs/user_diskio.h +++ b/firmware/targets/f7/fatfs/user_diskio.h @@ -30,7 +30,7 @@ extern "C" { /* USER CODE BEGIN 0 */ /* Includes ------------------------------------------------------------------*/ -#include "stm32_adafruit_sd.h" +#include "sd_spi_io.h" #include "fatfs/ff_gen_drv.h" /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ diff --git a/firmware/targets/f7/furi_hal/furi_hal.c b/firmware/targets/f7/furi_hal/furi_hal.c index 7f2f5759f..afe46c4ed 100644 --- a/firmware/targets/f7/furi_hal/furi_hal.c +++ b/firmware/targets/f7/furi_hal/furi_hal.c @@ -53,6 +53,7 @@ void furi_hal_init() { furi_hal_region_init(); furi_hal_spi_config_init(); + furi_hal_spi_dma_init(); furi_hal_ibutton_init(); FURI_LOG_I(TAG, "iButton OK"); diff --git a/firmware/targets/f7/furi_hal/furi_hal_infrared.c b/firmware/targets/f7/furi_hal/furi_hal_infrared.c index dea1112ab..c1d24f803 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_infrared.c +++ b/firmware/targets/f7/furi_hal/furi_hal_infrared.c @@ -28,6 +28,15 @@ const GpioPin gpio_infrared_tx_debug = {.port = GPIOA, .pin = GPIO_PIN_7}; #define INFRARED_TX_CCMR_LOW \ (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */ +/* DMA Channels definition */ +#define IR_DMA DMA2 +#define IR_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define IR_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define IR_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define IR_DMA_CH2_IRQ FuriHalInterruptIdDma2Ch2 +#define IR_DMA_CH1_DEF IR_DMA, IR_DMA_CH1_CHANNEL +#define IR_DMA_CH2_DEF IR_DMA, IR_DMA_CH2_CHANNEL + typedef struct { FuriHalInfraredRxCaptureCallback capture_callback; void* capture_context; @@ -213,15 +222,15 @@ void furi_hal_infrared_async_rx_set_timeout_isr_callback( } static void furi_hal_infrared_tx_dma_terminate(void) { - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_DisableIT_TC(IR_DMA_CH2_DEF); furi_assert(furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_TC(IR_DMA_CH1_DEF); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); LL_TIM_DisableCounter(TIM1); FuriStatus status = furi_semaphore_release(infrared_tim_tx.stop_semaphore); furi_check(status == FuriStatusOk); @@ -230,7 +239,7 @@ static void furi_hal_infrared_tx_dma_terminate(void) { static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { uint8_t buf_num = 0; - uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2); + uint32_t buffer_adr = LL_DMA_GetMemoryAddress(IR_DMA_CH2_DEF); if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[0].data) { buf_num = 0; } else if(buffer_adr == (uint32_t)infrared_tim_tx.buffer[1].data) { @@ -242,12 +251,13 @@ static uint8_t furi_hal_infrared_get_current_dma_tx_buffer(void) { } static void furi_hal_infrared_tx_dma_polarity_isr() { - if(LL_DMA_IsActiveFlag_TE1(DMA1)) { - LL_DMA_ClearFlag_TE1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_TE1(IR_DMA)) { + LL_DMA_ClearFlag_TE1(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH1_DEF)) { + LL_DMA_ClearFlag_TC1(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTx) || @@ -257,25 +267,29 @@ static void furi_hal_infrared_tx_dma_polarity_isr() { uint8_t next_buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); furi_hal_infrared_tx_dma_set_polarity(next_buf_num, 0); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_tx_dma_isr() { - if(LL_DMA_IsActiveFlag_TE2(DMA1)) { - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + if(LL_DMA_IsActiveFlag_TE2(IR_DMA)) { + LL_DMA_ClearFlag_TE2(IR_DMA); furi_crash(NULL); } - if(LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_HT2(DMA1); + if(LL_DMA_IsActiveFlag_HT2(IR_DMA) && LL_DMA_IsEnabledIT_HT(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_HT2(IR_DMA); uint8_t buf_num = furi_hal_infrared_get_current_dma_tx_buffer(); uint8_t next_buf_num = !buf_num; if(infrared_tim_tx.buffer[buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } else if( !infrared_tim_tx.buffer[buf_num].packet_end || (furi_hal_infrared_state == InfraredStateAsyncTx)) { furi_hal_infrared_tx_fill_buffer(next_buf_num, 0); if(infrared_tim_tx.buffer[next_buf_num].last_packet_end) { - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableIT_HT(IR_DMA_CH2_DEF); } } else if(furi_hal_infrared_state == InfraredStateAsyncTxStopReq) { /* fallthrough */ @@ -283,8 +297,8 @@ static void furi_hal_infrared_tx_dma_isr() { furi_crash(NULL); } } - if(LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) { - LL_DMA_ClearFlag_TC2(DMA1); + if(LL_DMA_IsActiveFlag_TC2(IR_DMA) && LL_DMA_IsEnabledIT_TC(IR_DMA_CH2_DEF)) { + LL_DMA_ClearFlag_TC2(IR_DMA); furi_check( (furi_hal_infrared_state == InfraredStateAsyncTxStopInProgress) || (furi_hal_infrared_state == InfraredStateAsyncTxStopReq) || @@ -310,6 +324,9 @@ static void furi_hal_infrared_tx_dma_isr() { infrared_tim_tx.signal_sent_callback(infrared_tim_tx.signal_sent_context); } } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_infrared_configure_tim_pwm_tx(uint32_t freq, float duty_cycle) { @@ -369,16 +386,19 @@ static void furi_hal_infrared_configure_tim_cmgr2_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); + LL_DMA_Init(IR_DMA_CH1_DEF, &dma_config); - LL_DMA_ClearFlag_TE1(DMA1); - LL_DMA_ClearFlag_TC1(DMA1); +#if IR_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + LL_DMA_ClearFlag_TE1(IR_DMA); + LL_DMA_ClearFlag_TC1(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableIT_TE(IR_DMA_CH1_DEF); + LL_DMA_EnableIT_TC(IR_DMA_CH1_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch1, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH1_IRQ, 4, furi_hal_infrared_tx_dma_polarity_isr, NULL); } static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { @@ -394,18 +414,21 @@ static void furi_hal_infrared_configure_tim_rcr_dma_tx(void) { dma_config.NbData = 0; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP; dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); + LL_DMA_Init(IR_DMA_CH2_DEF, &dma_config); - LL_DMA_ClearFlag_TC2(DMA1); - LL_DMA_ClearFlag_HT2(DMA1); - LL_DMA_ClearFlag_TE2(DMA1); +#if IR_DMA_CH2_CHANNEL == LL_DMA_CHANNEL_2 + LL_DMA_ClearFlag_TC2(IR_DMA); + LL_DMA_ClearFlag_HT2(IR_DMA); + LL_DMA_ClearFlag_TE2(IR_DMA); +#else +#error Update this code. Would you kindly? +#endif - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2); - LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableIT_TC(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_HT(IR_DMA_CH2_DEF); + LL_DMA_EnableIT_TE(IR_DMA_CH2_DEF); - furi_hal_interrupt_set_isr_ex( - FuriHalInterruptIdDma1Ch2, 5, furi_hal_infrared_tx_dma_isr, NULL); + furi_hal_interrupt_set_isr_ex(IR_DMA_CH2_IRQ, 5, furi_hal_infrared_tx_dma_isr, NULL); } static void furi_hal_infrared_tx_fill_buffer_last(uint8_t buf_num) { @@ -507,14 +530,14 @@ static void furi_hal_infrared_tx_dma_set_polarity(uint8_t buf_num, uint8_t polar furi_assert(buffer->polarity != NULL); FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH1_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(IR_DMA_CH1_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t)buffer->polarity); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift); + LL_DMA_SetMemoryAddress(IR_DMA_CH1_DEF, (uint32_t)buffer->polarity); + LL_DMA_SetDataLength(IR_DMA_CH1_DEF, buffer->size + polarity_shift); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); } FURI_CRITICAL_EXIT(); } @@ -527,14 +550,14 @@ static void furi_hal_infrared_tx_dma_set_buffer(uint8_t buf_num) { /* non-circular mode requires disabled channel before setup */ FURI_CRITICAL_ENTER(); - bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); + bool channel_enabled = LL_DMA_IsEnabledChannel(IR_DMA_CH2_DEF); if(channel_enabled) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(IR_DMA_CH2_DEF); } - LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size); + LL_DMA_SetMemoryAddress(IR_DMA_CH2_DEF, (uint32_t)buffer->data); + LL_DMA_SetDataLength(IR_DMA_CH2_DEF, buffer->size); if(channel_enabled) { - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); } @@ -545,8 +568,8 @@ static void furi_hal_infrared_async_tx_free_resources(void) { (furi_hal_infrared_state == InfraredStateAsyncTxStopped)); furi_hal_gpio_init(&gpio_infrared_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch2, NULL, NULL); + furi_hal_interrupt_set_isr(IR_DMA_CH1_IRQ, NULL, NULL); + furi_hal_interrupt_set_isr(IR_DMA_CH2_IRQ, NULL, NULL); LL_TIM_DeInit(TIM1); furi_semaphore_free(infrared_tim_tx.stop_semaphore); @@ -597,8 +620,8 @@ void furi_hal_infrared_async_tx_start(uint32_t freq, float duty_cycle) { furi_hal_infrared_state = InfraredStateAsyncTx; LL_TIM_ClearFlag_UPDATE(TIM1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_EnableChannel(IR_DMA_CH1_DEF); + LL_DMA_EnableChannel(IR_DMA_CH2_DEF); furi_delay_us(5); LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */ furi_delay_us(5); diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index 0ade85e0a..b0238f79f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -21,6 +21,14 @@ #define RFID_CAPTURE_IND_CH LL_TIM_CHANNEL_CH3 #define RFID_CAPTURE_DIR_CH LL_TIM_CHANNEL_CH4 +/* DMA Channels definition */ +#define RFID_DMA DMA2 +#define RFID_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define RFID_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define RFID_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define RFID_DMA_CH1_DEF RFID_DMA, RFID_DMA_CH1_CHANNEL +#define RFID_DMA_CH2_DEF RFID_DMA, RFID_DMA_CH2_CHANNEL + typedef struct { FuriHalRfidEmulateCallback callback; FuriHalRfidDMACallback dma_callback; @@ -302,15 +310,19 @@ void furi_hal_rfid_tim_read_capture_stop() { } static void furi_hal_rfid_dma_isr() { - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); +#if RFID_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(RFID_DMA)) { + LL_DMA_ClearFlag_HT1(RFID_DMA); furi_hal_rfid->dma_callback(true, furi_hal_rfid->context); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(RFID_DMA)) { + LL_DMA_ClearFlag_TC1(RFID_DMA); furi_hal_rfid->dma_callback(false, furi_hal_rfid->context); } +#else +#error Update this code. Would you kindly? +#endif } void furi_hal_rfid_tim_emulate_dma_start( @@ -347,8 +359,8 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(RFID_DMA_CH1_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH1_DEF); // configure DMA "mem -> CCR3" channel #if FURI_HAL_RFID_EMULATE_TIMER_CHANNEL == LL_TIM_CHANNEL_CH3 @@ -366,13 +378,13 @@ void furi_hal_rfid_tim_emulate_dma_start( dma_config.NbData = length; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(RFID_DMA_CH2_DEF, &dma_config); + LL_DMA_EnableChannel(RFID_DMA_CH2_DEF); // attach interrupt to one of DMA channels - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_rfid_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, furi_hal_rfid_dma_isr, NULL); + LL_DMA_EnableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(RFID_DMA_CH1_DEF); // start LL_TIM_EnableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); @@ -385,14 +397,14 @@ void furi_hal_rfid_tim_emulate_dma_stop() { LL_TIM_DisableCounter(FURI_HAL_RFID_EMULATE_TIMER); LL_TIM_DisableAllOutputs(FURI_HAL_RFID_EMULATE_TIMER); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); + furi_hal_interrupt_set_isr(RFID_DMA_CH1_IRQ, NULL, NULL); + LL_DMA_DisableIT_TC(RFID_DMA_CH1_DEF); + LL_DMA_DisableIT_HT(RFID_DMA_CH1_DEF); FURI_CRITICAL_ENTER(); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DeInit(RFID_DMA_CH1_DEF); + LL_DMA_DeInit(RFID_DMA_CH2_DEF); LL_TIM_DeInit(FURI_HAL_RFID_EMULATE_TIMER); FURI_CRITICAL_EXIT(); @@ -471,4 +483,4 @@ void COMP_IRQHandler() { (LL_COMP_ReadOutputLevel(COMP1) == LL_COMP_OUTPUT_LEVEL_LOW), furi_hal_rfid_comp_callback_context); } -} +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2f9d87080..8dba8327f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,14 +1,33 @@ +#include #include #include #include +#include -#include -#include - +#include #include #include #include +#define TAG "FuriHalSpi" + +#define SPI_DMA DMA2 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL +#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL + +// For simplicity, I assume that only one SPI DMA transaction can occur at a time. +static FuriSemaphore* spi_dma_lock = NULL; +static FuriSemaphore* spi_dma_completed = NULL; + +void furi_hal_spi_dma_init() { + spi_dma_lock = furi_semaphore_alloc(1, 1); + spi_dma_completed = furi_semaphore_alloc(1, 1); +} + void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); @@ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx( return ret; } + +static void spi_dma_isr() { +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC3(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC4(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif +} + +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + // If scheduler is not running, use blocking mode + if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); + } + + // Lock DMA + furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); + + const uint32_t dma_dummy_u32 = 0xFFFFFFFF; + + bool ret = true; + SPI_TypeDef* spi = handle->bus->spi; + uint32_t dma_rx_req; + uint32_t dma_tx_req; + + if(spi == SPI1) { + dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; + } else if(spi == SPI2) { + dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; + } else { + furi_crash(NULL); + } + + if(rx_buffer == NULL) { + // Only TX mode, do not use RX channel + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + LL_DMA_ClearFlag_TC4(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + } else { + // TRX or RX mode, use both channels + uint32_t tx_mem_increase_mode; + + if(tx_buffer == NULL) { + // RX mode, use dummy data instead of TX buffer + tx_buffer = (uint8_t*)&dma_dummy_u32; + tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; + } else { + tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; + } + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + LL_DMA_ClearFlag_TC3(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); + + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); + + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); + + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); + } + + furi_hal_spi_bus_end_txrx(handle, timeout_ms); + + furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); + + return ret; +} \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index 9bde15c33..a6d275308 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -18,6 +18,14 @@ static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +/* DMA Channels definition */ +#define SUBGHZ_DMA DMA2 +#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL +#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL + typedef struct { volatile SubGhzState state; volatile SubGhzRegulation regulation; @@ -525,8 +533,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { *buffer = 0; buffer++; samples--; - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); LL_TIM_EnableIT_UPDATE(TIM2); break; } else { @@ -567,17 +575,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { static void furi_hal_subghz_async_tx_dma_isr() { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); + +#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_subghz_async_tx_timer_isr() { @@ -586,7 +599,7 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; //forcibly pulls the pin to the ground so that there is no carrier @@ -634,11 +647,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); + LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); // Configure TIM2 LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -696,9 +709,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); + LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } return true; @@ -726,16 +739,16 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); // Deinitialize DMA - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); // Deinitialize GPIO furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // Stop debug if(furi_hal_subghz_stop_debug()) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/f7/src/update.c b/firmware/targets/f7/src/update.c index b223a5dc3..d8d26eb7c 100644 --- a/firmware/targets/f7/src/update.c +++ b/firmware/targets/f7/src/update.c @@ -23,8 +23,8 @@ static FATFS* pfs = NULL; } static bool flipper_update_mount_sd() { - for(int i = 0; i < BSP_SD_MaxMountRetryCount(); ++i) { - if(BSP_SD_Init((i % 2) == 0) != MSD_OK) { + for(int i = 0; i < sd_max_mount_retry_count(); ++i) { + if(sd_init((i % 2) == 0) != SdSpiStatusOK) { /* Next attempt will be without card reset, let it settle */ furi_delay_ms(1000); continue; diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index ab00ef0d7..79e809317 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ void furi_hal_spi_config_init(); +/** Initialize SPI DMA HAL */ +void furi_hal_spi_dma_init(); + /** Initialize SPI Bus * * @param handle pointer to FuriHalSpiBus instance @@ -103,6 +106,23 @@ bool furi_hal_spi_bus_trx( size_t size, uint32_t timeout); +/** SPI Transmit and Receive with DMA + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param tx_buffer pointer to tx buffer + * @param rx_buffer pointer to rx buffer + * @param size transaction size (buffer size) + * @param timeout_ms operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms); + #ifdef __cplusplus } #endif diff --git a/furi/core/event_flag.c b/furi/core/event_flag.c index 87de65f2d..9406a581f 100644 --- a/furi/core/event_flag.c +++ b/furi/core/event_flag.c @@ -32,7 +32,7 @@ uint32_t furi_event_flag_set(FuriEventFlag* instance, uint32_t flags) { if(FURI_IS_IRQ_MODE()) { yield = pdFALSE; if(xEventGroupSetBitsFromISR(hEventGroup, (EventBits_t)flags, &yield) == pdFAIL) { - rflags = (uint32_t)FuriStatusErrorResource; + rflags = (uint32_t)FuriFlagErrorResource; } else { rflags = flags; portYIELD_FROM_ISR(yield); diff --git a/furi/core/thread.c b/furi/core/thread.c index ef9560b4a..9a112d9a8 100644 --- a/furi/core/thread.c +++ b/furi/core/thread.c @@ -195,6 +195,15 @@ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority) { thread->priority = priority; } +void furi_thread_set_current_priority(FuriThreadPriority priority) { + UBaseType_t new_priority = priority ? priority : FuriThreadPriorityNormal; + vTaskPrioritySet(NULL, new_priority); +} + +FuriThreadPriority furi_thread_get_current_priority() { + return (FuriThreadPriority)uxTaskPriorityGet(NULL); +} + void furi_thread_set_state_callback(FuriThread* thread, FuriThreadStateCallback callback) { furi_assert(thread); furi_assert(thread->state == FuriThreadStateStopped); diff --git a/furi/core/thread.h b/furi/core/thread.h index 1542d5bf0..2675f7cee 100644 --- a/furi/core/thread.h +++ b/furi/core/thread.h @@ -122,6 +122,18 @@ void furi_thread_set_context(FuriThread* thread, void* context); */ void furi_thread_set_priority(FuriThread* thread, FuriThreadPriority priority); +/** Set current thread priority + * + * @param priority FuriThreadPriority value + */ +void furi_thread_set_current_priority(FuriThreadPriority priority); + +/** Get current thread priority + * + * @return FuriThreadPriority value + */ +FuriThreadPriority furi_thread_get_current_priority(); + /** Set FuriThread state change callback * * @param thread FuriThread instance From 7a3a1aaf0db35501b6ee5891ad039605285f2fea Mon Sep 17 00:00:00 2001 From: Georgii Surkov <37121527+gsurkov@users.noreply.github.com> Date: Wed, 8 Feb 2023 08:40:44 +0300 Subject: [PATCH 113/231] [FL-3057] Allow use of any suitable pin for 1-Wire devices (#2350) * Add 1-wire thermometer example app stub * Working 1-wire thermometer app * Refactor app to use threads * Clean up code, add comments * Add CRC checking * Increase update period * Fix error in fbt * Revert the old update period * Use settable pin in onewire_host * Use settable pin for onewire_slave * Clear EXTI flag after callback, make private methods static in onewire_slave * Do not hardcode GPIO pin number * Remove iButton hal from furi_hal_rfid * Remove most of furi_hal_ibutton * Add some of furi_hal_ibutton back * Slightly neater code * Fix formatting * Fix PVS-studio warnings * Update CODEOWNERS * Add furi_hal_gpio_get_ext_pin_number * Create README.md * FuriHal: move furi_hal_gpio_get_ext_pin_number to resources --------- Co-authored-by: Aleksandr Kutuzov --- .github/CODEOWNERS | 2 + .../examples/example_thermo/README.md | 44 +++ .../examples/example_thermo/application.fam | 10 + .../examples/example_thermo/example_thermo.c | 356 ++++++++++++++++++ .../example_thermo/example_thermo_10px.png | Bin 0 -> 7293 bytes applications/main/ibutton/ibutton_cli.c | 2 +- firmware/targets/f18/api_symbols.csv | 3 +- .../targets/f18/furi_hal/furi_hal_resources.c | 24 ++ .../targets/f18/furi_hal/furi_hal_resources.h | 7 + firmware/targets/f7/api_symbols.csv | 20 +- firmware/targets/f7/furi_hal/furi_hal_gpio.c | 33 +- .../targets/f7/furi_hal/furi_hal_ibutton.c | 43 +-- .../targets/f7/furi_hal/furi_hal_ibutton.h | 66 +--- .../targets/f7/furi_hal/furi_hal_resources.c | 23 ++ .../targets/f7/furi_hal/furi_hal_resources.h | 7 + firmware/targets/f7/furi_hal/furi_hal_rfid.c | 10 +- lib/one_wire/ibutton/ibutton_worker.c | 4 +- lib/one_wire/ibutton/ibutton_worker_modes.c | 13 +- lib/one_wire/one_wire_host.c | 48 ++- lib/one_wire/one_wire_host.h | 6 +- lib/one_wire/one_wire_slave.c | 51 +-- lib/one_wire/one_wire_slave.h | 4 +- 22 files changed, 592 insertions(+), 184 deletions(-) create mode 100644 applications/examples/example_thermo/README.md create mode 100644 applications/examples/example_thermo/application.fam create mode 100644 applications/examples/example_thermo/example_thermo.c create mode 100644 applications/examples/example_thermo/example_thermo_10px.png diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index aa4ada1a8..69f8289f9 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -42,6 +42,8 @@ /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm +/applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov + # Assets /assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov diff --git a/applications/examples/example_thermo/README.md b/applications/examples/example_thermo/README.md new file mode 100644 index 000000000..fa00264dc --- /dev/null +++ b/applications/examples/example_thermo/README.md @@ -0,0 +1,44 @@ +# 1-Wire Thermometer +This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer. +It also covers basic GUI, input handling, threads and localisation. + +## Electrical connections +Before launching the application, connect the sensor to Flipper's external GPIO according to the table below: +| DS18B20 | Flipper | +| :-----: | :-----: | +| VDD | 9 | +| GND | 18 | +| DQ | 17 | + +*NOTE 1*: GND is also available on pins 8 and 11. + +*NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9. + +## Launching the application +In order to launch this demo, follow the steps below: +1. Make sure your Flipper has an SD card installed. +2. Connect your Flipper to the computer via a USB cable. +3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice. + +## Changing the data pin +It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: + +```c +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) +``` +Do not forget about the external pull-up resistor as these pins do not have one built-in. + +With the changes been made, recompile and launch the application again. +The on-screen text should reflect it by asking to connect the thermometer to another pin. diff --git a/applications/examples/example_thermo/application.fam b/applications/examples/example_thermo/application.fam new file mode 100644 index 000000000..b4a05c7f9 --- /dev/null +++ b/applications/examples/example_thermo/application.fam @@ -0,0 +1,10 @@ +App( + appid="example_thermo", + name="Example: Thermometer", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_thermo_main", + requires=["gui"], + stack_size=1 * 1024, + fap_icon="example_thermo_10px.png", + fap_category="Examples", +) diff --git a/applications/examples/example_thermo/example_thermo.c b/applications/examples/example_thermo/example_thermo.c new file mode 100644 index 000000000..b3bc7cd99 --- /dev/null +++ b/applications/examples/example_thermo/example_thermo.c @@ -0,0 +1,356 @@ +/* + * This file contains an example application that reads and displays + * the temperature from a DS18B20 1-wire thermometer. + * + * It also covers basic GUI, input handling, threads and localisation. + * + * References: + * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf + */ + +#include +#include + +#include +#include + +#include + +#include +#include + +#define UPDATE_PERIOD_MS 1000UL +#define TEXT_STORE_SIZE 64U + +#define DS18B20_CMD_CONVERT 0x44U +#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU + +#define DS18B20_CFG_RESOLUTION_POS 5U +#define DS18B20_CFG_RESOLUTION_MASK 0x03U +#define DS18B20_DECIMAL_PART_MASK 0x0fU + +#define DS18B20_SIGN_MASK 0xf0U + +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) + +/* Flags which the reader thread responds to */ +typedef enum { + ReaderThreadFlagExit = 1, +} ReaderThreadFlag; + +typedef union { + struct { + uint8_t temp_lsb; /* Least significant byte of the temperature */ + uint8_t temp_msb; /* Most significant byte of the temperature */ + uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ + uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ + uint8_t config; /* Configuration register */ + uint8_t reserved[3]; /* Not used */ + uint8_t crc; /* CRC checksum for error detection */ + } fields; + uint8_t bytes[9]; +} DS18B20Scratchpad; + +/* Application context structure */ +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriThread* reader_thread; + FuriMessageQueue* event_queue; + OneWireHost* onewire; + float temp_celsius; + bool has_device; +} ExampleThermoContext; + +/*************** 1-Wire Communication and Processing *****************/ + +/* Commands the thermometer to begin measuring the temperature. */ +static void example_thermo_request_temperature(ExampleThermoContext* context) { + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) break; + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + /* After the ROM operation, a device-specific command is issued. + In this case, it's a request to start measuring the temperature. */ + onewire_host_write(onewire, DS18B20_CMD_CONVERT); + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Reads the measured temperature from the thermometer. */ +static void example_thermo_read_temperature(ExampleThermoContext* context) { + /* If there was no device detected, don't try to read the temperature */ + if(!context->has_device) { + return; + } + + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + + do { + DS18B20Scratchpad buf; + + /* Attempt reading the temperature 10 times before giving up */ + size_t attempts_left = 10; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) continue; + + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + + /* After the ROM operation, a device-specific command is issued. + This time, it will be the "Read Scratchpad" command which will + prepare the device's internal buffer memory for reading. */ + onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); + + /* The actual reading happens here. A total of 9 bytes is read. */ + onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); + + /* Calculate the checksum and compare it with one provided by the device. */ + const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); + + /* Checksums match, exit the loop */ + if(crc == buf.fields.crc) break; + + } while(--attempts_left); + + if(attempts_left == 0) break; + + /* Get the measurement resolution from the configuration register. (See [1] page 9) */ + const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & + DS18B20_CFG_RESOLUTION_MASK; + + /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ + const uint8_t decimal_mask = + (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & + DS18B20_DECIMAL_PART_MASK; + + /* Get the integer and decimal part of the temperature (See [1] page 6) */ + const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); + const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; + + /* Calculate the sign of the temperature (See [1] page 6) */ + const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; + + /* Combine the integer and decimal part together */ + const float temp_celsius_abs = integer_part + decimal_part / 16.f; + + /* Set the appropriate sign */ + context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ +static int32_t example_thermo_reader_thread_callback(void* ctx) { + ExampleThermoContext* context = ctx; + + for(;;) { + /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ + example_thermo_request_temperature(context); + + /* Wait for the measurement to finish. At the same time wait for an exit signal. */ + const uint32_t flags = + furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); + + /* If an exit signal was received, return from this thread. */ + if(flags != (unsigned)FuriFlagErrorTimeout) break; + + /* The measurement is now ready, read it from the termometer. */ + example_thermo_read_temperature(context); + } + + return 0; +} + +/*************** GUI, Input and Main Loop *****************/ + +/* Draw the GUI of the application. The screen is completely redrawn during each call. */ +static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { + ExampleThermoContext* context = ctx; + char text_store[TEXT_STORE_SIZE]; + const size_t middle_x = canvas_width(canvas) / 2U; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); + canvas_draw_line(canvas, 0, 16, 128, 16); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer"); + + snprintf( + text_store, + TEXT_STORE_SIZE, + "to GPIO pin %ld", + furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); + canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); + + canvas_set_font(canvas, FontKeyboard); + + if(context->has_device) { + float temp; + char temp_units; + + /* The applicaton is locale-aware. + Change Settings->System->Units to check it out. */ + switch(locale_get_measurement_unit()) { + case LocaleMeasurementUnitsMetric: + temp = context->temp_celsius; + temp_units = 'C'; + break; + case LocaleMeasurementUnitsImperial: + temp = locale_celsius_to_fahrenheit(context->temp_celsius); + temp_units = 'F'; + break; + default: + furi_crash("Illegal measurement units"); + } + /* If a reading is available, display it */ + snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); + } else { + /* Or show a message that no data is available */ + strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + } + + canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); +} + +/* This function is called from the GUI thread. All it does is put the event + into the application's queue so it can be processed later. */ +static void example_thermo_input_callback(InputEvent* event, void* ctx) { + ExampleThermoContext* context = ctx; + furi_message_queue_put(context->event_queue, event, FuriWaitForever); +} + +/* Starts the reader thread and handles the input */ +static void example_thermo_run(ExampleThermoContext* context) { + /* Configure the hardware in host mode */ + onewire_host_start(context->onewire); + + /* Start the reader thread. It will talk to the thermometer in the background. */ + furi_thread_start(context->reader_thread); + + /* An endless loop which handles the input*/ + for(bool is_running = true; is_running;) { + InputEvent event; + /* Wait for an input event. Input events come from the GUI thread via a callback. */ + const FuriStatus status = + furi_message_queue_get(context->event_queue, &event, FuriWaitForever); + + /* This application is only interested in short button presses. */ + if((status != FuriStatusOk) || (event.type != InputTypeShort)) { + continue; + } + + /* When the user presses the "Back" button, break the loop and exit the application. */ + if(event.key == InputKeyBack) { + is_running = false; + } + } + + /* Signal the reader thread to cease operation and exit */ + furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); + + /* Wait for the reader thread to finish */ + furi_thread_join(context->reader_thread); + + /* Reset the hardware */ + onewire_host_stop(context->onewire); +} + +/******************** Initialisation & startup *****************************/ + +/* Allocate the memory and initialise the variables */ +static ExampleThermoContext* example_thermo_context_alloc() { + ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); + + context->view_port = view_port_alloc(); + view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); + view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); + + context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + context->reader_thread = furi_thread_alloc(); + furi_thread_set_stack_size(context->reader_thread, 1024U); + furi_thread_set_context(context->reader_thread, context); + furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); + + context->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); + + context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); + + return context; +} + +/* Release the unused resources and deallocate memory */ +static void example_thermo_context_free(ExampleThermoContext* context) { + view_port_enabled_set(context->view_port, false); + gui_remove_view_port(context->gui, context->view_port); + + onewire_host_free(context->onewire); + furi_thread_free(context->reader_thread); + furi_message_queue_free(context->event_queue); + view_port_free(context->view_port); + + furi_record_close(RECORD_GUI); +} + +/* The application's entry point. Execution starts from here. */ +int32_t example_thermo_main(void* p) { + UNUSED(p); + + /* Allocate all of the necessary structures */ + ExampleThermoContext* context = example_thermo_context_alloc(); + + /* Start the applicaton's main loop. It won't return until the application was requested to exit. */ + example_thermo_run(context); + + /* Release all unneeded resources */ + example_thermo_context_free(context); + + return 0; +} diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3d527f306c2d325fc72380856f14860f51b3742c GIT binary patch literal 7293 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 literal 0 HcmV?d00001 diff --git a/applications/main/ibutton/ibutton_cli.c b/applications/main/ibutton/ibutton_cli.c index fab1ccf05..9ddb079dc 100644 --- a/applications/main/ibutton/ibutton_cli.c +++ b/applications/main/ibutton/ibutton_cli.c @@ -271,7 +271,7 @@ void onewire_cli_print_usage() { static void onewire_cli_search(Cli* cli) { UNUSED(cli); - OneWireHost* onewire = onewire_host_alloc(); + OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio); uint8_t address[8]; bool done = false; diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index ff6dbc239..025b605fc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.2,, +Version,+,13.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -910,6 +910,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.c b/firmware/targets/f18/furi_hal/furi_hal_resources.c index dafeefdd0..41cc80bfb 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.c @@ -199,3 +199,27 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + // TODO: describe second ROW + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f18/furi_hal/furi_hal_resources.h b/firmware/targets/f18/furi_hal/furi_hal_resources.h index ef2cdae7f..a24afbf94 100644 --- a/firmware/targets/f18/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f18/furi_hal/furi_hal_resources.h @@ -111,6 +111,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a1f34f106..35985aebb 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.2,, +Version,+,13.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1094,6 +1094,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1129,20 +1130,13 @@ Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uin Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" -Function,+,furi_hal_ibutton_add_interrupt,void,"GpioExtiCallback, void*" Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t Function,+,furi_hal_ibutton_emulate_start,void,"uint32_t, FuriHalIbuttonEmulateCallback, void*" Function,+,furi_hal_ibutton_emulate_stop,void, Function,-,furi_hal_ibutton_init,void, -Function,+,furi_hal_ibutton_pin_get_level,_Bool, -Function,+,furi_hal_ibutton_pin_high,void, -Function,+,furi_hal_ibutton_pin_low,void, -Function,+,furi_hal_ibutton_remove_interrupt,void, -Function,+,furi_hal_ibutton_start_drive,void, -Function,+,furi_hal_ibutton_start_drive_in_isr,void, -Function,+,furi_hal_ibutton_start_interrupt,void, -Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, -Function,+,furi_hal_ibutton_stop,void, +Function,+,furi_hal_ibutton_pin_configure,void, +Function,+,furi_hal_ibutton_pin_reset,void, +Function,+,furi_hal_ibutton_pin_write,void,const _Bool Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t @@ -2040,7 +2034,7 @@ Function,+,onewire_device_detach,void,OneWireDevice* Function,+,onewire_device_free,void,OneWireDevice* Function,+,onewire_device_get_id_p,uint8_t*,OneWireDevice* Function,+,onewire_device_send_id,void,OneWireDevice* -Function,+,onewire_host_alloc,OneWireHost*, +Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* Function,+,onewire_host_free,void,OneWireHost* Function,+,onewire_host_read,uint8_t,OneWireHost* Function,+,onewire_host_read_bit,_Bool,OneWireHost* @@ -2054,7 +2048,7 @@ Function,+,onewire_host_stop,void,OneWireHost* Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write,void,"OneWireHost*, uint8_t" Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool" -Function,+,onewire_slave_alloc,OneWireSlave*, +Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin* Function,+,onewire_slave_attach,void,"OneWireSlave*, OneWireDevice*" Function,+,onewire_slave_detach,void,OneWireSlave* Function,+,onewire_slave_free,void,OneWireSlave* diff --git a/firmware/targets/f7/furi_hal/furi_hal_gpio.c b/firmware/targets/f7/furi_hal/furi_hal_gpio.c index a148a3af2..e8318afcf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_gpio.c +++ b/firmware/targets/f7/furi_hal/furi_hal_gpio.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #define GET_SYSCFG_EXTI_PORT(gpio) \ @@ -224,85 +225,85 @@ static void furi_hal_gpio_int_call(uint16_t pin_num) { /* Interrupt handlers */ void EXTI0_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); furi_hal_gpio_int_call(0); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); } } void EXTI1_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); furi_hal_gpio_int_call(1); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); } } void EXTI2_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); furi_hal_gpio_int_call(2); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); } } void EXTI3_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); furi_hal_gpio_int_call(3); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); } } void EXTI4_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); furi_hal_gpio_int_call(4); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); } } void EXTI9_5_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); furi_hal_gpio_int_call(5); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); furi_hal_gpio_int_call(6); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); furi_hal_gpio_int_call(7); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); furi_hal_gpio_int_call(8); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); furi_hal_gpio_int_call(9); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); } } void EXTI15_10_IRQHandler(void) { if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); furi_hal_gpio_int_call(10); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); furi_hal_gpio_int_call(11); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); furi_hal_gpio_int_call(12); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); furi_hal_gpio_int_call(13); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); furi_hal_gpio_int_call(14); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); } if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) { - LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); furi_hal_gpio_int_call(15); + LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c index 0375893e7..c05cd69a8 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.c +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.c @@ -89,47 +89,16 @@ void furi_hal_ibutton_emulate_stop() { } } -void furi_hal_ibutton_start_drive() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_configure() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_start_drive_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_start_interrupt() { - furi_hal_ibutton_pin_high(); - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); -} - -void furi_hal_ibutton_start_interrupt_in_isr() { - furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); - LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); -} - -void furi_hal_ibutton_stop() { - furi_hal_ibutton_pin_high(); +void furi_hal_ibutton_pin_reset() { + furi_hal_gpio_write(&ibutton_gpio, true); furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) { - furi_hal_gpio_add_int_callback(&ibutton_gpio, cb, context); -} - -void furi_hal_ibutton_remove_interrupt() { - furi_hal_gpio_remove_int_callback(&ibutton_gpio); -} - -void furi_hal_ibutton_pin_low() { - furi_hal_gpio_write(&ibutton_gpio, false); -} - -void furi_hal_ibutton_pin_high() { - furi_hal_gpio_write(&ibutton_gpio, true); -} - -bool furi_hal_ibutton_pin_get_level() { - return furi_hal_gpio_read(&ibutton_gpio); +void furi_hal_ibutton_pin_write(const bool state) { + furi_hal_gpio_write(&ibutton_gpio, state); } diff --git a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h index fb57d628b..b723fe176 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_ibutton.h +++ b/firmware/targets/f7/furi_hal/furi_hal_ibutton.h @@ -7,7 +7,6 @@ #include #include -#include #ifdef __cplusplus extern "C" { @@ -18,70 +17,43 @@ typedef void (*FuriHalIbuttonEmulateCallback)(void* context); /** Initialize */ void furi_hal_ibutton_init(); +/** + * Start emulation timer + * @param period timer period + * @param callback timer callback + * @param context callback context + */ void furi_hal_ibutton_emulate_start( uint32_t period, FuriHalIbuttonEmulateCallback callback, void* context); +/** + * Update emulation timer period + * @param period new timer period + */ void furi_hal_ibutton_emulate_set_next(uint32_t period); +/** + * Stop emulation timer + */ void furi_hal_ibutton_emulate_stop(); /** - * Sets the pin to normal mode (open collector), and sets it to float + * Set the pin to normal mode (open collector), and sets it to float */ -void furi_hal_ibutton_start_drive(); - -/** - * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_drive_in_isr(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float - */ -void furi_hal_ibutton_start_interrupt(); - -/** - * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt. - * Used in EXTI interrupt context. - */ -void furi_hal_ibutton_start_interrupt_in_isr(); +void furi_hal_ibutton_pin_configure(); /** * Sets the pin to analog mode, and sets it to float */ -void furi_hal_ibutton_stop(); +void furi_hal_ibutton_pin_reset(); /** - * Attach interrupt callback to iButton pin - * @param cb callback - * @param context context + * iButton write pin + * @param state true / false */ -void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context); - -/** - * Remove interrupt callback from iButton pin - */ -void furi_hal_ibutton_remove_interrupt(); - -/** - * Sets the pin to low - */ -void furi_hal_ibutton_pin_low(); - -/** - * Sets the pin to high (float in iButton pin modes) - */ -void furi_hal_ibutton_pin_high(); - -/** - * Get pin level - * @return true if level is high - * @return false if level is low - */ -bool furi_hal_ibutton_pin_get_level(); +void furi_hal_ibutton_pin_write(const bool state); #ifdef __cplusplus } diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 03cc6e714..c0eb9ee67 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -191,3 +191,26 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 51ea7bcc1..04b99a84e 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -216,6 +216,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_rfid.c b/firmware/targets/f7/furi_hal/furi_hal_rfid.c index b0238f79f..145e49df2 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_rfid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_rfid.c @@ -77,7 +77,7 @@ void furi_hal_rfid_init() { void furi_hal_rfid_pins_reset() { // ibutton bus disable - furi_hal_ibutton_stop(); + furi_hal_ibutton_pin_reset(); // pulldown rfid antenna furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); @@ -94,8 +94,8 @@ void furi_hal_rfid_pins_reset() { void furi_hal_rfid_pins_emulate() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // pull pin to timer out furi_hal_gpio_init_ex( @@ -115,8 +115,8 @@ void furi_hal_rfid_pins_emulate() { void furi_hal_rfid_pins_read() { // ibutton low - furi_hal_ibutton_start_drive(); - furi_hal_ibutton_pin_low(); + furi_hal_ibutton_pin_configure(); + furi_hal_ibutton_pin_write(false); // dont pull rfid antenna furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); diff --git a/lib/one_wire/ibutton/ibutton_worker.c b/lib/one_wire/ibutton/ibutton_worker.c index 9d5ea3897..1fe39b5e5 100644 --- a/lib/one_wire/ibutton/ibutton_worker.c +++ b/lib/one_wire/ibutton/ibutton_worker.c @@ -25,8 +25,8 @@ iButtonWorker* ibutton_worker_alloc() { iButtonWorker* worker = malloc(sizeof(iButtonWorker)); worker->key_p = NULL; worker->key_data = malloc(ibutton_key_get_max_size()); - worker->host = onewire_host_alloc(); - worker->slave = onewire_slave_alloc(); + worker->host = onewire_host_alloc(&ibutton_gpio); + worker->slave = onewire_slave_alloc(&ibutton_gpio); worker->writer = ibutton_writer_alloc(worker->host); worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); diff --git a/lib/one_wire/ibutton/ibutton_worker_modes.c b/lib/one_wire/ibutton/ibutton_worker_modes.c index b284940e7..da6b10766 100644 --- a/lib/one_wire/ibutton/ibutton_worker_modes.c +++ b/lib/one_wire/ibutton/ibutton_worker_modes.c @@ -234,16 +234,13 @@ void ibutton_worker_emulate_timer_cb(void* context) { furi_assert(context); iButtonWorker* worker = context; - LevelDuration level = + const LevelDuration level_duration = protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); - furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level)); + const bool level = level_duration_get_level(level_duration); - if(level_duration_get_level(level)) { - furi_hal_ibutton_pin_high(); - } else { - furi_hal_ibutton_pin_low(); - } + furi_hal_ibutton_emulate_set_next(level); + furi_hal_ibutton_pin_write(level); } void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { @@ -266,7 +263,7 @@ void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); - furi_hal_ibutton_start_drive(); + furi_hal_ibutton_pin_configure(); furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); } diff --git a/lib/one_wire/one_wire_host.c b/lib/one_wire/one_wire_host.c index 654b9e45d..f3d3d3c4d 100644 --- a/lib/one_wire/one_wire_host.c +++ b/lib/one_wire/one_wire_host.c @@ -1,18 +1,19 @@ #include -#include + #include "one_wire_host.h" #include "one_wire_host_timing.h" struct OneWireHost { - // global search state - unsigned char saved_rom[8]; + const GpioPin* gpio_pin; + unsigned char saved_rom[8]; /** < global search state */ uint8_t last_discrepancy; uint8_t last_family_discrepancy; bool last_device_flag; }; -OneWireHost* onewire_host_alloc() { +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) { OneWireHost* host = malloc(sizeof(OneWireHost)); + host->gpio_pin = gpio_pin; onewire_host_reset_search(host); return host; } @@ -23,49 +24,47 @@ void onewire_host_free(OneWireHost* host) { } bool onewire_host_reset(OneWireHost* host) { - UNUSED(host); uint8_t r; uint8_t retries = 125; // wait until the gpio is high - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); do { if(--retries == 0) return 0; furi_delay_us(2); - } while(!furi_hal_ibutton_pin_get_level()); + } while(!furi_hal_gpio_read(host->gpio_pin)); // pre delay furi_delay_us(OWH_RESET_DELAY_PRE); // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_RESET_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_RESET_RELEASE); // read and post delay - r = !furi_hal_ibutton_pin_get_level(); + r = !furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_RESET_DELAY_POST); return r; } bool onewire_host_read_bit(OneWireHost* host) { - UNUSED(host); bool result; // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_READ_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_READ_RELEASE); // read and post delay - result = furi_hal_ibutton_pin_get_level(); + result = furi_hal_gpio_read(host->gpio_pin); furi_delay_us(OWH_READ_DELAY_POST); return result; @@ -90,22 +89,21 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) } void onewire_host_write_bit(OneWireHost* host, bool value) { - UNUSED(host); if(value) { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_1_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_1_RELEASE); } else { // drive low - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(host->gpio_pin, false); furi_delay_us(OWH_WRITE_0_DRIVE); // release - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(host->gpio_pin, true); furi_delay_us(OWH_WRITE_0_RELEASE); } } @@ -123,13 +121,13 @@ void onewire_host_skip(OneWireHost* host) { } void onewire_host_start(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_start_drive(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); } void onewire_host_stop(OneWireHost* host) { - UNUSED(host); - furi_hal_ibutton_stop(); + furi_hal_gpio_write(host->gpio_pin, true); + furi_hal_gpio_init(host->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } void onewire_host_reset_search(OneWireHost* host) { @@ -150,7 +148,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { host->last_device_flag = false; } -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) { +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { uint8_t id_bit_number; uint8_t last_zero, rom_byte_number, search_result; uint8_t id_bit, cmp_id_bit; @@ -259,7 +257,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSear host->last_family_discrepancy = 0; search_result = false; } else { - for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i]; + for(int i = 0; i < 8; i++) new_addr[i] = host->saved_rom[i]; } return search_result; diff --git a/lib/one_wire/one_wire_host.h b/lib/one_wire/one_wire_host.h index 5b55ee8dd..9c01abc11 100644 --- a/lib/one_wire/one_wire_host.h +++ b/lib/one_wire/one_wire_host.h @@ -22,10 +22,10 @@ typedef struct OneWireHost OneWireHost; /** * Allocate onewire host bus - * @param gpio + * @param pin * @return OneWireHost* */ -OneWireHost* onewire_host_alloc(); +OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); /** * Deallocate onewire host bus @@ -114,7 +114,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); * @param mode * @return uint8_t */ -uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); +uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); #ifdef __cplusplus } diff --git a/lib/one_wire/one_wire_slave.c b/lib/one_wire/one_wire_slave.c index ad9c34b19..4b54c4f99 100644 --- a/lib/one_wire/one_wire_slave.c +++ b/lib/one_wire/one_wire_slave.c @@ -27,6 +27,7 @@ typedef enum { } OneWireSlaveError; struct OneWireSlave { + const GpioPin* gpio_pin; OneWireSlaveError error; OneWireDevice* device; OneWireSlaveResultCallback result_cb; @@ -35,15 +36,15 @@ struct OneWireSlave { /*********************** PRIVATE ***********************/ -uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { - UNUSED(bus); +static uint32_t + onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { uint32_t start = DWT->CYCCNT; uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); uint32_t time_captured; do { //-V1044 time_captured = DWT->CYCCNT; - if(furi_hal_ibutton_pin_get_level() != pin_value) { + if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { uint32_t remaining_time = time_ticks - (time_captured - start); remaining_time /= furi_hal_cortex_instructions_per_microsecond(); return remaining_time; @@ -53,14 +54,14 @@ uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, cons return 0; } -bool onewire_slave_show_presence(OneWireSlave* bus) { +static bool onewire_slave_show_presence(OneWireSlave* bus) { // wait while master delay presence check onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); // show presence - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(bus->gpio_pin, false); furi_delay_us(OWS_PRESENCE_MIN); - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); // somebody also can show presence const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; @@ -74,7 +75,7 @@ bool onewire_slave_show_presence(OneWireSlave* bus) { return true; } -bool onewire_slave_receive_bit(OneWireSlave* bus) { +static bool onewire_slave_receive_bit(OneWireSlave* bus) { // wait while bus is low uint32_t time = OWS_SLOT_MAX; time = onewire_slave_wait_while_gpio_is(bus, time, false); @@ -98,7 +99,7 @@ bool onewire_slave_receive_bit(OneWireSlave* bus) { return (time > 0); } -bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { +static bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { const bool write_zero = !value; // wait while bus is low @@ -119,7 +120,7 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { // choose write time if(write_zero) { - furi_hal_ibutton_pin_low(); + furi_hal_gpio_write(bus->gpio_pin, false); time = OWS_WRITE_ZERO; } else { time = OWS_READ_MAX; @@ -127,12 +128,12 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { // hold line for ZERO or ONE time furi_delay_us(time); - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); return true; } -void onewire_slave_cmd_search_rom(OneWireSlave* bus) { +static void onewire_slave_cmd_search_rom(OneWireSlave* bus) { const uint8_t key_bytes = 8; uint8_t* key = onewire_device_get_id_p(bus->device); @@ -151,7 +152,7 @@ void onewire_slave_cmd_search_rom(OneWireSlave* bus) { } } -bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { +static bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { uint8_t cmd; onewire_slave_receive(bus, &cmd, 1); @@ -178,14 +179,14 @@ bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { } } -bool onewire_slave_bus_start(OneWireSlave* bus) { +static bool onewire_slave_bus_start(OneWireSlave* bus) { bool result = true; if(bus->device == NULL) { result = false; } else { FURI_CRITICAL_ENTER(); - furi_hal_ibutton_start_drive_in_isr(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); bus->error = NO_ERROR; if(onewire_slave_show_presence(bus)) { @@ -197,7 +198,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { result = false; } - furi_hal_ibutton_start_interrupt_in_isr(); + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); FURI_CRITICAL_EXIT(); } @@ -207,7 +208,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { static void exti_cb(void* context) { OneWireSlave* bus = context; - volatile bool input_state = furi_hal_ibutton_pin_get_level(); + volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); static uint32_t pulse_start = 0; if(input_state) { @@ -234,8 +235,9 @@ static void exti_cb(void* context) { /*********************** PUBLIC ***********************/ -OneWireSlave* onewire_slave_alloc() { +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { OneWireSlave* bus = malloc(sizeof(OneWireSlave)); + bus->gpio_pin = gpio_pin; bus->error = NO_ERROR; bus->device = NULL; bus->result_cb = NULL; @@ -249,14 +251,15 @@ void onewire_slave_free(OneWireSlave* bus) { } void onewire_slave_start(OneWireSlave* bus) { - furi_hal_ibutton_add_interrupt(exti_cb, bus); - furi_hal_ibutton_start_interrupt(); + furi_hal_gpio_add_int_callback(bus->gpio_pin, exti_cb, bus); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); } void onewire_slave_stop(OneWireSlave* bus) { - UNUSED(bus); - furi_hal_ibutton_stop(); - furi_hal_ibutton_remove_interrupt(); + furi_hal_gpio_write(bus->gpio_pin, true); + furi_hal_gpio_init(bus->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_remove_int_callback(bus->gpio_pin); } void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { @@ -282,7 +285,7 @@ void onewire_slave_set_result_callback( bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { uint8_t bytes_sent = 0; - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); // bytes loop for(; bytes_sent < data_length; ++bytes_sent) { @@ -304,7 +307,7 @@ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { uint8_t bytes_received = 0; - furi_hal_ibutton_pin_high(); + furi_hal_gpio_write(bus->gpio_pin, true); for(; bytes_received < data_length; ++bytes_received) { uint8_t value = 0; diff --git a/lib/one_wire/one_wire_slave.h b/lib/one_wire/one_wire_slave.h index 82e9f5523..2e5db3a1c 100644 --- a/lib/one_wire/one_wire_slave.h +++ b/lib/one_wire/one_wire_slave.h @@ -19,10 +19,10 @@ typedef void (*OneWireSlaveResultCallback)(void* context); /** * Allocate onewire slave - * @param pin + * @param gpio_pin * @return OneWireSlave* */ -OneWireSlave* onewire_slave_alloc(); +OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); /** * Free onewire slave From 111c7557b3c3c5a371f3fddf1a63592b7ad04b6c Mon Sep 17 00:00:00 2001 From: Liam Hays Date: Wed, 8 Feb 2023 01:08:50 -0700 Subject: [PATCH 114/231] Fix minor UI inconsistencies and bugs (#2361) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Changed blue LED to cyan in NFC Magic and Picopass apps. * Fix capitalization of ATQA and UID in NFC Add Manually wizard. * Fix reselection of "Saved" menu item in NFC and RFID apps. * Fix double back press after deleting a file in the SubGhz browser. * Make NFC app behave like other apps: return to the file browser after deleting a file. * Rename NfcSceneSetAtqua to NfcSceneSetAtqa. * Save selected menu items in NFC Magic and Picopass apps in a way that always works. * Restore previous selection in Universal Remotes menu. * Other way to do universal remote menu saving, and NFC Extra Actions saves last selection. Co-authored-by: あく --- .../main/infrared/scenes/infrared_scene_universal.c | 4 +++- .../main/lfrfid/scenes/lfrfid_scene_start.c | 9 ++++++++- applications/main/nfc/scenes/nfc_scene_config.h | 2 +- .../main/nfc/scenes/nfc_scene_delete_success.c | 2 +- .../main/nfc/scenes/nfc_scene_extra_actions.c | 2 ++ applications/main/nfc/scenes/nfc_scene_set_atqa.c | 2 +- applications/main/nfc/scenes/nfc_scene_set_sak.c | 2 +- applications/main/nfc/scenes/nfc_scene_set_uid.c | 2 +- applications/main/nfc/scenes/nfc_scene_start.c | 13 ++++++++++++- .../subghz/scenes/subghz_scene_delete_success.c | 5 ++++- applications/plugins/nfc_magic/nfc_magic.c | 6 +++--- .../nfc_magic/scenes/nfc_magic_scene_start.c | 10 +++++++++- applications/plugins/picopass/picopass.c | 6 +++--- .../plugins/picopass/scenes/picopass_scene_start.c | 7 ++++++- 14 files changed, 55 insertions(+), 17 deletions(-) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 914360d78..5043c9bd7 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -33,7 +33,8 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalAC, infrared_scene_universal_submenu_callback, context); - submenu_set_selected_item(submenu, 0); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(infrared->scene_manager, InfraredSceneUniversal)); view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewSubmenu); } @@ -54,6 +55,7 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; } + scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } return consumed; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_start.c b/applications/main/lfrfid/scenes/lfrfid_scene_start.c index 8e1c92dbb..2d83ba53b 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_start.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_start.c @@ -47,21 +47,28 @@ bool lfrfid_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexRead); scene_manager_next_scene(app->scene_manager, LfRfidSceneRead); DOLPHIN_DEED(DolphinDeedRfidRead); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Like in the other apps, explicitly save the scene state + // in each branch in case the user cancels loading a file. + scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, SubmenuIndexSaved); furi_string_set(app->file_path, LFRFID_APP_FOLDER); scene_manager_next_scene(app->scene_manager, LfRfidSceneSelectKey); consumed = true; } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexAddManually); scene_manager_next_scene(app->scene_manager, LfRfidSceneSaveType); consumed = true; } else if(event.event == SubmenuIndexExtraActions) { + scene_manager_set_scene_state( + app->scene_manager, LfRfidSceneStart, SubmenuIndexExtraActions); scene_manager_next_scene(app->scene_manager, LfRfidSceneExtraActions); consumed = true; } - scene_manager_set_scene_state(app->scene_manager, LfRfidSceneStart, event.event); } return consumed; diff --git a/applications/main/nfc/scenes/nfc_scene_config.h b/applications/main/nfc/scenes/nfc_scene_config.h index ce51d000d..f81fe178e 100644 --- a/applications/main/nfc/scenes/nfc_scene_config.h +++ b/applications/main/nfc/scenes/nfc_scene_config.h @@ -4,7 +4,7 @@ ADD_SCENE(nfc, saved_menu, SavedMenu) ADD_SCENE(nfc, extra_actions, ExtraActions) ADD_SCENE(nfc, set_type, SetType) ADD_SCENE(nfc, set_sak, SetSak) -ADD_SCENE(nfc, set_atqa, SetAtqua) +ADD_SCENE(nfc, set_atqa, SetAtqa) ADD_SCENE(nfc, set_uid, SetUid) ADD_SCENE(nfc, generate_info, GenerateInfo) ADD_SCENE(nfc, read_card_success, ReadCardSuccess) diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index 1664a9e5b..795363527 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -30,7 +30,7 @@ bool nfc_scene_delete_success_on_event(void* context, SceneManagerEvent event) { nfc->scene_manager, NfcSceneMfClassicKeys); } else { consumed = scene_manager_search_and_switch_to_previous_scene( - nfc->scene_manager, NfcSceneStart); + nfc->scene_manager, NfcSceneFileSelect); } } } diff --git a/applications/main/nfc/scenes/nfc_scene_extra_actions.c b/applications/main/nfc/scenes/nfc_scene_extra_actions.c index 717e8efc4..66aaf5a26 100644 --- a/applications/main/nfc/scenes/nfc_scene_extra_actions.c +++ b/applications/main/nfc/scenes/nfc_scene_extra_actions.c @@ -34,6 +34,8 @@ void nfc_scene_extra_actions_on_enter(void* context) { SubmenuIndexMfUltralightUnlock, nfc_scene_extra_actions_submenu_callback, nfc); + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(nfc->scene_manager, NfcSceneExtraActions)); view_dispatcher_switch_to_view(nfc->view_dispatcher, NfcViewMenu); } diff --git a/applications/main/nfc/scenes/nfc_scene_set_atqa.c b/applications/main/nfc/scenes/nfc_scene_set_atqa.c index f2100aa19..d079b3804 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_atqa.c +++ b/applications/main/nfc/scenes/nfc_scene_set_atqa.c @@ -11,7 +11,7 @@ void nfc_scene_set_atqa_on_enter(void* context) { // Setup view ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter atqa in hex"); + byte_input_set_header_text(byte_input, "Enter ATQA in hex"); byte_input_set_result_callback( byte_input, nfc_scene_set_atqa_byte_input_callback, diff --git a/applications/main/nfc/scenes/nfc_scene_set_sak.c b/applications/main/nfc/scenes/nfc_scene_set_sak.c index 3c88f3504..60a1e1494 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_sak.c +++ b/applications/main/nfc/scenes/nfc_scene_set_sak.c @@ -28,7 +28,7 @@ bool nfc_scene_set_sak_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == NfcCustomEventByteInputDone) { - scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqua); + scene_manager_next_scene(nfc->scene_manager, NfcSceneSetAtqa); consumed = true; } } diff --git a/applications/main/nfc/scenes/nfc_scene_set_uid.c b/applications/main/nfc/scenes/nfc_scene_set_uid.c index 5f0f52f6e..54606b68e 100644 --- a/applications/main/nfc/scenes/nfc_scene_set_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_set_uid.c @@ -11,7 +11,7 @@ void nfc_scene_set_uid_on_enter(void* context) { // Setup view ByteInput* byte_input = nfc->byte_input; - byte_input_set_header_text(byte_input, "Enter uid in hex"); + byte_input_set_header_text(byte_input, "Enter UID in hex"); nfc->dev_edit_data = nfc->dev->dev_data.nfc_data; byte_input_set_result_callback( byte_input, diff --git a/applications/main/nfc/scenes/nfc_scene_start.c b/applications/main/nfc/scenes/nfc_scene_start.c index 2a116fe09..a01f871ab 100644 --- a/applications/main/nfc/scenes/nfc_scene_start.c +++ b/applications/main/nfc/scenes/nfc_scene_start.c @@ -48,11 +48,14 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexRead); nfc->dev->dev_data.read_mode = NfcReadModeAuto; scene_manager_next_scene(nfc->scene_manager, NfcSceneRead); DOLPHIN_DEED(DolphinDeedNfcRead); consumed = true; } else if(event.event == SubmenuIndexDetectReader) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexDetectReader); bool sd_exist = storage_sd_status(nfc->dev->storage) == FSE_OK; if(sd_exist) { nfc_device_data_clear(&nfc->dev->dev_data); @@ -63,19 +66,27 @@ bool nfc_scene_start_on_event(void* context, SceneManagerEvent event) { } consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Save the scene state explicitly in each branch, so that + // if the user cancels loading a file, the Saved menu item + // is properly reselected. + scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, SubmenuIndexSaved); scene_manager_next_scene(nfc->scene_manager, NfcSceneFileSelect); consumed = true; } else if(event.event == SubmenuIndexExtraAction) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexExtraAction); scene_manager_next_scene(nfc->scene_manager, NfcSceneExtraActions); consumed = true; } else if(event.event == SubmenuIndexAddManually) { + scene_manager_set_scene_state( + nfc->scene_manager, NfcSceneStart, SubmenuIndexAddManually); 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; } - scene_manager_set_scene_state(nfc->scene_manager, NfcSceneStart, event.event); } return consumed; } diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4f98b6a39..8a2546243 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -31,7 +31,10 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); + // Commented so that the user doesn't have to press + // back twice to get to the main SubGhz menu after + // deleting a file. + //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/plugins/nfc_magic/nfc_magic.c b/applications/plugins/nfc_magic/nfc_magic.c index e4e0ffde0..1805f35ed 100644 --- a/applications/plugins/nfc_magic/nfc_magic.c +++ b/applications/plugins/nfc_magic/nfc_magic.c @@ -136,9 +136,9 @@ void nfc_magic_free(NfcMagic* nfc_magic) { free(nfc_magic); } -static const NotificationSequence nfc_magic_sequence_blink_start_blue = { +static const NotificationSequence nfc_magic_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -149,7 +149,7 @@ static const NotificationSequence nfc_magic_sequence_blink_stop = { }; void nfc_magic_blink_start(NfcMagic* nfc_magic) { - notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_blue); + notification_message(nfc_magic->notifications, &nfc_magic_sequence_blink_start_cyan); } void nfc_magic_blink_stop(NfcMagic* nfc_magic) { diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c index f2984443f..a70eb8acc 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_start.c @@ -40,16 +40,24 @@ bool nfc_magic_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexCheck) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexCheck); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneCheck); consumed = true; } else if(event.event == SubmenuIndexWriteGen1A) { + // Explicitly save state in each branch so that the + // correct option is reselected if the user cancels + // loading a file. + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWriteGen1A); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneFileSelect); consumed = true; } else if(event.event == SubmenuIndexWipe) { + scene_manager_set_scene_state( + nfc_magic->scene_manager, NfcMagicSceneStart, SubmenuIndexWipe); scene_manager_next_scene(nfc_magic->scene_manager, NfcMagicSceneWipe); consumed = true; } - scene_manager_set_scene_state(nfc_magic->scene_manager, NfcMagicSceneStart, event.event); } return consumed; diff --git a/applications/plugins/picopass/picopass.c b/applications/plugins/picopass/picopass.c index e9f48b676..217f963d3 100644 --- a/applications/plugins/picopass/picopass.c +++ b/applications/plugins/picopass/picopass.c @@ -137,9 +137,9 @@ void picopass_text_store_clear(Picopass* picopass) { memset(picopass->text_store, 0, sizeof(picopass->text_store)); } -static const NotificationSequence picopass_sequence_blink_start_blue = { +static const NotificationSequence picopass_sequence_blink_start_cyan = { &message_blink_start_10, - &message_blink_set_color_blue, + &message_blink_set_color_cyan, &message_do_not_reset, NULL, }; @@ -150,7 +150,7 @@ static const NotificationSequence picopass_sequence_blink_stop = { }; void picopass_blink_start(Picopass* picopass) { - notification_message(picopass->notifications, &picopass_sequence_blink_start_blue); + notification_message(picopass->notifications, &picopass_sequence_blink_start_cyan); } void picopass_blink_stop(Picopass* picopass) { diff --git a/applications/plugins/picopass/scenes/picopass_scene_start.c b/applications/plugins/picopass/scenes/picopass_scene_start.c index 6d1aeedcd..d33a1d264 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_start.c +++ b/applications/plugins/picopass/scenes/picopass_scene_start.c @@ -32,13 +32,18 @@ bool picopass_scene_start_on_event(void* context, SceneManagerEvent event) { if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubmenuIndexRead) { + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexRead); scene_manager_next_scene(picopass->scene_manager, PicopassSceneReadCard); consumed = true; } else if(event.event == SubmenuIndexSaved) { + // Explicitly save state so that the correct item is + // reselected if the user cancels loading a file. + scene_manager_set_scene_state( + picopass->scene_manager, PicopassSceneStart, SubmenuIndexSaved); scene_manager_next_scene(picopass->scene_manager, PicopassSceneFileSelect); consumed = true; } - scene_manager_set_scene_state(picopass->scene_manager, PicopassSceneStart, event.event); } return consumed; From 23ecc186c24062002aeac5e73235b3a6c0021f77 Mon Sep 17 00:00:00 2001 From: LTVA1 <87536432+LTVA1@users.noreply.github.com> Date: Wed, 8 Feb 2023 12:26:17 +0300 Subject: [PATCH 115/231] Custom font set function (#2261) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * custom font set function * update API symbols * add example of custom font usage * delete u8g2 dependency in example custom font * rename to canvas_set_custom_u8g2_font * now change the name in ALL places Co-authored-by: あく Co-authored-by: hedger --- .../debug/example_custom_font/application.fam | 9 ++ .../example_custom_font/example_custom_font.c | 98 +++++++++++++++++++ applications/services/gui/canvas.c | 6 ++ applications/services/gui/canvas.h | 7 ++ firmware/targets/f18/api_symbols.csv | 5 +- firmware/targets/f7/api_symbols.csv | 1 + 6 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 applications/debug/example_custom_font/application.fam create mode 100644 applications/debug/example_custom_font/example_custom_font.c diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam new file mode 100644 index 000000000..02285b8a0 --- /dev/null +++ b/applications/debug/example_custom_font/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_custom_font", + name="Example: custom font", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_custom_font_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c new file mode 100644 index 000000000..15eeb5f02 --- /dev/null +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +//This arrays contains the font itself. You can use any u8g2 font you want + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310fb, 1); + u8g2_SetFont(&canvas->fb, font); +} + void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 0b0c7e658..b2a065ca7 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -146,6 +146,13 @@ void canvas_invert_color(Canvas* canvas); */ void canvas_set_font(Canvas* canvas, Font font); +/** Set custom drawing font + * + * @param canvas Canvas instance + * @param font Pointer to u8g2 const uint8_t* font array + */ +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); + /** Draw string at position of baseline defined by x, y. * * @param canvas Canvas instance diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index 025b605fc..d29d696fc 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.0,, +Version,+,13.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -546,6 +546,7 @@ Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" @@ -910,7 +911,6 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* -Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1017,6 +1017,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rtc_datetime_to_timestamp,uint32_t,FuriHalRtcDateTime* diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 35985aebb..6456555d8 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -632,6 +632,7 @@ Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" Function,+,canvas_set_font,void,"Canvas*, Font" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" Function,+,canvas_width,uint8_t,Canvas* From 00076deece707d61aed1f9cc01151b430b4f3a13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Such=C3=A1nek?= Date: Wed, 8 Feb 2023 10:35:38 +0100 Subject: [PATCH 116/231] SCons: do not include backup files in build (#2221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SCons: do not include backup files in build * fbt: now GlobRecursive by default excludes backup files * fbt: added backup file exclusion to plain libs Signed-off-by: Michal Suchanek Co-authored-by: hedger Co-authored-by: hedger Co-authored-by: あく --- assets/SConscript | 4 +++- firmware.scons | 3 ++- lib/misc.scons | 8 +++++++- scripts/fbt/util.py | 4 ++++ scripts/fbt_tools/fbt_assets.py | 4 +--- scripts/fbt_tools/sconsrecursiveglob.py | 8 ++++++-- 6 files changed, 23 insertions(+), 8 deletions(-) diff --git a/assets/SConscript b/assets/SConscript index ef5d83c79..21437aa30 100644 --- a/assets/SConscript +++ b/assets/SConscript @@ -78,7 +78,9 @@ if assetsenv["IS_BASE_FIRMWARE"]: resources = assetsenv.Command( "#/assets/resources/Manifest", assetsenv.GlobRecursive( - "*", assetsenv.Dir("resources").srcnode(), exclude="Manifest" + "*", + assetsenv.Dir("resources").srcnode(), + exclude=["Manifest"], ), action=Action( '${PYTHON3} "${ASSETS_COMPILER}" manifest "${TARGET.dir.posix}" --timestamp=${GIT_UNIX_TIMESTAMP}', diff --git a/firmware.scons b/firmware.scons index 92f2d1a91..a094765af 100644 --- a/firmware.scons +++ b/firmware.scons @@ -2,6 +2,7 @@ Import("ENV", "fw_build_meta") from SCons.Errors import UserError from SCons.Node import FS + import itertools from fbt_extra.util import ( @@ -171,7 +172,7 @@ sources = [apps_c] # Gather sources only from app folders in current configuration sources.extend( itertools.chain.from_iterable( - fwenv.GlobRecursive(source_type, appdir.relpath, exclude="lib") + fwenv.GlobRecursive(source_type, appdir.relpath, exclude=["lib"]) for appdir, source_type in fwenv["APPBUILD"].get_builtin_app_folders() ) ) diff --git a/lib/misc.scons b/lib/misc.scons index cd2377ceb..49b6b61d9 100644 --- a/lib/misc.scons +++ b/lib/misc.scons @@ -1,5 +1,7 @@ Import("env") +from fbt.util import GLOB_FILE_EXCLUSION + env.Append( CPPPATH=[ "#/lib/digital_signal", @@ -39,7 +41,11 @@ libs_plain = [ ] for lib in libs_plain: - sources += Glob(lib + "/*.c*", source=True) + sources += Glob( + lib + "/*.c*", + exclude=GLOB_FILE_EXCLUSION, + source=True, + ) lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) libenv.Install("${LIB_DIST_DIR}", lib) diff --git a/scripts/fbt/util.py b/scripts/fbt/util.py index ee7562058..7bdaea031 100644 --- a/scripts/fbt/util.py +++ b/scripts/fbt/util.py @@ -8,6 +8,10 @@ import os WINPATHSEP_RE = re.compile(r"\\([^\"'\\]|$)") +# Used by default when globbing for files with GlobRecursive +# Excludes all files ending with ~, usually created by editors as backup files +GLOB_FILE_EXCLUSION = ["*~"] + def tempfile_arg_esc_func(arg): arg = quote_spaces(arg) diff --git a/scripts/fbt_tools/fbt_assets.py b/scripts/fbt_tools/fbt_assets.py index e17487358..d2a58f3fb 100644 --- a/scripts/fbt_tools/fbt_assets.py +++ b/scripts/fbt_tools/fbt_assets.py @@ -27,9 +27,7 @@ def proto_emitter(target, source, env): def dolphin_emitter(target, source, env): res_root_dir = source[0].Dir(env["DOLPHIN_RES_TYPE"]) source = [res_root_dir] - source.extend( - env.GlobRecursive("*.*", res_root_dir.srcnode()), - ) + source.extend(env.GlobRecursive("*.*", res_root_dir.srcnode())) target_base_dir = target[0] env.Replace(_DOLPHIN_OUT_DIR=target[0]) diff --git a/scripts/fbt_tools/sconsrecursiveglob.py b/scripts/fbt_tools/sconsrecursiveglob.py index 32ff55ea1..fbcee965b 100644 --- a/scripts/fbt_tools/sconsrecursiveglob.py +++ b/scripts/fbt_tools/sconsrecursiveglob.py @@ -1,7 +1,11 @@ import SCons +from SCons.Script import Flatten +from fbt.util import GLOB_FILE_EXCLUSION -def GlobRecursive(env, pattern, node=".", exclude=None): +def GlobRecursive(env, pattern, node=".", exclude=[]): + exclude = list(set(Flatten(exclude) + GLOB_FILE_EXCLUSION)) + # print(f"Starting glob for {pattern} from {node} (exclude: {exclude})") results = [] if isinstance(node, str): node = env.Dir(node) @@ -13,7 +17,7 @@ def GlobRecursive(env, pattern, node=".", exclude=None): source=True, exclude=exclude, ) - # print(f"Glob for {pattern} from {node}: {results}") + # print(f"Glob result for {pattern} from {node}: {results}") return results From 20f98050f2030e3a8f1509421cc43729617919b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 19:38:09 +0900 Subject: [PATCH 117/231] Github: disable f18 build (#2375) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 080e7c2c2..a6c9219c2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,7 +10,7 @@ on: pull_request: env: - TARGETS: f7 f18 + TARGETS: f7 DEFAULT_TARGET: f7 FBT_TOOLCHAIN_PATH: /runner/_work From cee9b640b30502f65ab1081134a891a1e6841343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=82=E3=81=8F?= Date: Wed, 8 Feb 2023 20:45:33 +0900 Subject: [PATCH 118/231] Update Missing SD Card icon from PR 2373 (#2376) --- assets/icons/SDCard/SDQuestion_35x43.png | Bin 1950 -> 2042 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/SDCard/SDQuestion_35x43.png b/assets/icons/SDCard/SDQuestion_35x43.png index 9b9c9a58e3257f926677533f8cc99ffb19dd74f5..257ab1d852460ad83dffa5989e765902fcb09578 100644 GIT binary patch delta 523 zcmbQo|BHWugar#T0}w3HVF!|o#X;^)4C~IxyaaMs(j9#r85lP9bN@+X1@hSfd_r9R z|NjqUh90_HH&Ic7@%uz8ezSl|hu0(V4SA9t0%7SFZhTNGdi(6U zqtX3c^NQF~FPUjxUAjEGb!OR09>>O*+iqu6ZhUtYncXoe)wdUX*U5dow13$b cea!*}8+O&+FAqd3fX-m>boFyt=akR{04GeM8vp%dtcP1$Xn~K~5UtcSa z%;J*#qDngjo1&C7tKif^yUBA|b~EXNDDla)tXmZHfpYpVNgxwl-{k*nGF%YtPKoJu zlNYnuaYJ>0`G45tb)bBg)Z*l#%z~24{5+VUK`w3}W`z~d*xb~TL_3Aaf$YNd+7>6) z0X=Zo)5S3)qV?@GZ@$9{JgkPN{;vPCYk}`2GmG;QF`|a6IRxAKa|IY)wstHrWSDT* z#4q^7^nZ)qe+@L{O>(>JubebcTr>xA;mo&QS_`}msD%H&0j`ym{bIX}= zOW&%!e!SiJ%|z?^88d$Te0N6PAoTmLgw@r0!CkAW-_AH!8nnmtgxFKVBQwS7oqSd? aS1_D$pUltxg^3;L9|liXKbLh*2~7Y$MyGH9 From 99253a0e28f45476d631ac8db8c8717d13e0bac8 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 17:20:42 +0400 Subject: [PATCH 119/231] [FL-3093, FL-3087] SubGhz: Fix Raw write, add short duration filter setting (#2300) * SubGhz: Fix recording RAW files, sometimes could not start at a high level * SubGhz: subghz_worker, add short duration filter setting * SubGhz: capture raw timings in cli. Furi: clear pending interrupts on ISR set/reset * SubGhz: fix start duration in furi_hal_subghz_start_async_rx * [FL-3093] SubGhz: hopping issue in some regions * [FL-3087] SubGhz: fix delete-ok issue * SubGhz: remove copypasta from rx_raw cli command Co-authored-by: Aleksandr Kutuzov --- .../scenes/subghz_scene_delete_success.c | 5 -- .../scenes/subghz_scene_receiver_info.c | 15 ++++ applications/main/subghz/subghz_cli.c | 83 ++++++++++++++++++- firmware/targets/f7/api_symbols.csv | 3 +- .../targets/f7/furi_hal/furi_hal_interrupt.c | 17 ++++ .../targets/f7/furi_hal/furi_hal_subghz.c | 11 ++- lib/subghz/protocols/raw.c | 1 + lib/subghz/subghz_worker.c | 36 ++++---- lib/subghz/subghz_worker.h | 8 ++ 9 files changed, 150 insertions(+), 29 deletions(-) diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 8a2546243..4d9f33e37 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -28,13 +28,8 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event if(event.event == SubGhzCustomEventSceneDeleteSuccess) { if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneReadRAW)) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - // Commented so that the user doesn't have to press - // back twice to get to the main SubGhz menu after - // deleting a file. - //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index c0f901578..4733b0e1d 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -129,6 +129,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + } + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, + furi_string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); + } + if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { + subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + } + subghz->state_notifications = SubGhzNotificationStateRx; } else { subghz->state_notifications = SubGhzNotificationStateTx; } diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 536cb535e..9271443a8 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -309,6 +309,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { free(instance); } +void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + // Configure radio + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelDuration level_duration; + size_t counter = 0; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == 0) { + continue; + } + if(ret != sizeof(LevelDuration)) { + puts("stream corrupt"); + break; + } + if(level_duration_is_reset(level_duration)) { + puts(". "); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + printf("%c%lu ", level ? '+' : '-', duration); + } + furi_thread_stdout_flush(); + counter++; + if(counter > 255) { + puts("\r\n"); + counter = 0; + } + } + + // Shutdown radio + furi_hal_subghz_stop_async_rx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + + // Cleanup + furi_stream_buffer_free(instance->stream); + free(instance); +} void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -431,7 +506,8 @@ static void subghz_cli_command_print_usage() { printf("\tchat \t - Chat with other Flippers\r\n"); printf( "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Reception key\r\n"); + printf("\trx \t - Receive\r\n"); + printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -733,6 +809,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "rx_raw") == 0) { + subghz_cli_command_rx_raw(cli, args, context); + break; + } + if(furi_string_cmp_str(cmd, "decode_raw") == 0) { subghz_cli_command_decode_raw(cli, args, context); break; diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 6456555d8..65ab375ef 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.0,, +Version,+,13.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -2675,6 +2675,7 @@ Function,+,subghz_worker_free,void,SubGhzWorker* Function,+,subghz_worker_is_running,_Bool,SubGhzWorker* Function,+,subghz_worker_rx_callback,void,"_Bool, uint32_t, void*" Function,+,subghz_worker_set_context,void,"SubGhzWorker*, void*" +Function,+,subghz_worker_set_filter,void,"SubGhzWorker*, uint16_t" Function,+,subghz_worker_set_overrun_callback,void,"SubGhzWorker*, SubGhzWorkerOverrunCallback" Function,+,subghz_worker_set_pair_callback,void,"SubGhzWorker*, SubGhzWorkerPairCallback" Function,+,subghz_worker_start,void,SubGhzWorker* diff --git a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c index 1b1132d0c..b5639d230 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_interrupt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_interrupt.c @@ -74,6 +74,21 @@ __attribute__((always_inline)) static inline void NVIC_EnableIRQ(furi_hal_interrupt_irqn[index]); } +__attribute__((always_inline)) static inline void + furi_hal_interrupt_clear_pending(FuriHalInterruptId index) { + NVIC_ClearPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_get_pending(FuriHalInterruptId index) { + NVIC_GetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + +__attribute__((always_inline)) static inline void + furi_hal_interrupt_set_pending(FuriHalInterruptId index) { + NVIC_SetPendingIRQ(furi_hal_interrupt_irqn[index]); +} + __attribute__((always_inline)) static inline void furi_hal_interrupt_disable(FuriHalInterruptId index) { NVIC_DisableIRQ(furi_hal_interrupt_irqn[index]); @@ -123,6 +138,7 @@ void furi_hal_interrupt_set_isr_ex( // Pre ISR clear furi_assert(furi_hal_interrupt_isr[index].isr != NULL); furi_hal_interrupt_disable(index); + furi_hal_interrupt_clear_pending(index); } furi_hal_interrupt_isr[index].isr = isr; @@ -131,6 +147,7 @@ void furi_hal_interrupt_set_isr_ex( if(isr) { // Post ISR set + furi_hal_interrupt_clear_pending(index); furi_hal_interrupt_enable(index, priority); } else { // Post ISR clear diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index a6d275308..596464613 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -438,7 +438,7 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; // Clock division for capture filter LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced @@ -455,13 +455,15 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); // Timer: channel 2 direct LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); + LL_TIM_IC_SetFilter( + TIM2, + LL_TIM_CHANNEL_CH2, + LL_TIM_IC_FILTER_FDIV32_N8); // Capture filter: 1/(64000000/64/4/32*8) = 16us // ISR setup furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); @@ -481,6 +483,9 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* // Switch to RX furi_hal_subghz_rx(); + + //Clear the variable after the end of the session + furi_hal_subghz_capture_delta_duration = 0; } void furi_hal_subghz_stop_async_rx() { diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index b639c93b9..ac3492e78 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -159,6 +159,7 @@ bool subghz_protocol_raw_save_to_file_init( instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); instance->file_is_open = RAWFileIsOpenWrite; instance->sample_write = 0; + instance->last_level = false; instance->pause = false; init = true; } while(0); diff --git a/lib/subghz/subghz_worker.c b/lib/subghz/subghz_worker.c index 35e399885..50b5aba51 100644 --- a/lib/subghz/subghz_worker.c +++ b/lib/subghz/subghz_worker.c @@ -12,7 +12,6 @@ struct SubGhzWorker { volatile bool overrun; LevelDuration filter_level_duration; - bool filter_running; uint16_t filter_duration; SubGhzWorkerOverrunCallback overrun_callback; @@ -59,24 +58,19 @@ static int32_t subghz_worker_thread_callback(void* context) { bool level = level_duration_get_level(level_duration); uint32_t duration = level_duration_get_duration(level_duration); - if(instance->filter_running) { - if((duration < instance->filter_duration) || - (instance->filter_level_duration.level == level)) { - instance->filter_level_duration.duration += duration; + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; - } else if(instance->filter_level_duration.level != level) { - if(instance->pair_callback) - instance->pair_callback( - instance->context, - instance->filter_level_duration.level, - instance->filter_level_duration.duration); - - instance->filter_level_duration.duration = duration; - instance->filter_level_duration.level = level; - } - } else { + } else if(instance->filter_level_duration.level != level) { if(instance->pair_callback) - instance->pair_callback(instance->context, level, duration); + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; } } } @@ -94,8 +88,7 @@ SubGhzWorker* subghz_worker_alloc() { instance->stream = furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); - //setting filter - instance->filter_running = true; + //setting default filter in us instance->filter_duration = 30; return instance; @@ -149,3 +142,8 @@ bool subghz_worker_is_running(SubGhzWorker* instance) { furi_assert(instance); return instance->running; } + +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { + furi_assert(instance); + instance->filter_duration = timeout; +} \ No newline at end of file diff --git a/lib/subghz/subghz_worker.h b/lib/subghz/subghz_worker.h index f85b1fdc7..657278f50 100644 --- a/lib/subghz/subghz_worker.h +++ b/lib/subghz/subghz_worker.h @@ -67,6 +67,14 @@ void subghz_worker_stop(SubGhzWorker* instance); */ bool subghz_worker_is_running(SubGhzWorker* instance); +/** + * Short duration filter setting. + * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled + * @param instance Pointer to a SubGhzWorker instance + * @param timeout time in us + */ +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); + #ifdef __cplusplus } #endif From b1f581239bbc83af1720e7180e6bf4a7c319288e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:01:00 +0300 Subject: [PATCH 120/231] BadUSB: Keyboard Layouts (#2256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * BadUSB: Keyboard Layouts * Apply requested changes pt1 * Add layout file check when we loading config Co-authored-by: Nikolay Minaylov Co-authored-by: あく --- applications/main/bad_usb/bad_usb_app.c | 72 +++++++++++++- applications/main/bad_usb/bad_usb_app_i.h | 11 ++- applications/main/bad_usb/bad_usb_script.c | 51 ++++++++-- applications/main/bad_usb/bad_usb_script.h | 2 + .../main/bad_usb/bad_usb_settings_filename.h | 3 + .../bad_usb/scenes/bad_usb_scene_config.c | 53 ++++++++++ .../bad_usb/scenes/bad_usb_scene_config.h | 2 + .../scenes/bad_usb_scene_config_layout.c | 50 ++++++++++ .../scenes/bad_usb_scene_file_select.c | 14 ++- .../main/bad_usb/scenes/bad_usb_scene_work.c | 27 ++++-- .../main/bad_usb/views/bad_usb_view.c | 91 ++++++++++++------ .../main/bad_usb/views/bad_usb_view.h | 6 +- assets/icons/Archive/keyboard_10px.png | Bin 0 -> 147 bytes .../resources/badusb/assets/layouts/ba-BA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/cz_CS.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/da-DA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-DE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/dvorak.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-UK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-US.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/es-ES.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-BE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-FR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hr-HR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hu-HU.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/it-IT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nb-NO.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nl-NL.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-BR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-PT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/si-SI.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sk-SK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sv-SE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/tr-TR.kl | Bin 0 -> 256 bytes 36 files changed, 323 insertions(+), 59 deletions(-) create mode 100644 applications/main/bad_usb/bad_usb_settings_filename.h create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config.c create mode 100644 applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c create mode 100644 assets/icons/Archive/keyboard_10px.png create mode 100644 assets/resources/badusb/assets/layouts/ba-BA.kl create mode 100644 assets/resources/badusb/assets/layouts/cz_CS.kl create mode 100644 assets/resources/badusb/assets/layouts/da-DA.kl create mode 100644 assets/resources/badusb/assets/layouts/de-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/de-DE.kl create mode 100644 assets/resources/badusb/assets/layouts/dvorak.kl create mode 100644 assets/resources/badusb/assets/layouts/en-UK.kl create mode 100644 assets/resources/badusb/assets/layouts/en-US.kl create mode 100644 assets/resources/badusb/assets/layouts/es-ES.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-BE.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-FR.kl create mode 100644 assets/resources/badusb/assets/layouts/hr-HR.kl create mode 100644 assets/resources/badusb/assets/layouts/hu-HU.kl create mode 100644 assets/resources/badusb/assets/layouts/it-IT.kl create mode 100644 assets/resources/badusb/assets/layouts/nb-NO.kl create mode 100644 assets/resources/badusb/assets/layouts/nl-NL.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-BR.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-PT.kl create mode 100644 assets/resources/badusb/assets/layouts/si-SI.kl create mode 100755 assets/resources/badusb/assets/layouts/sk-SK.kl create mode 100644 assets/resources/badusb/assets/layouts/sv-SE.kl create mode 100644 assets/resources/badusb/assets/layouts/tr-TR.kl diff --git a/applications/main/bad_usb/bad_usb_app.c b/applications/main/bad_usb/bad_usb_app.c index 6fd29cd70..5f2aa4789 100644 --- a/applications/main/bad_usb/bad_usb_app.c +++ b/applications/main/bad_usb/bad_usb_app.c @@ -1,9 +1,12 @@ #include "bad_usb_app_i.h" +#include "bad_usb_settings_filename.h" #include #include #include #include +#define BAD_USB_SETTINGS_PATH BAD_USB_APP_BASE_FOLDER "/" BAD_USB_SETTINGS_FILE_NAME + static bool bad_usb_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); BadUsbApp* app = context; @@ -22,15 +25,62 @@ static void bad_usb_app_tick_event_callback(void* context) { scene_manager_handle_tick_event(app->scene_manager); } +static void bad_usb_load_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_READ, FSOM_OPEN_EXISTING)) { + char chr; + while((storage_file_read(settings_file, &chr, 1) == 1) && + !storage_file_eof(settings_file) && !isspace(chr)) { + furi_string_push_back(app->keyboard_layout, chr); + } + } else { + furi_string_reset(app->keyboard_layout); + } + storage_file_close(settings_file); + storage_file_free(settings_file); + + if(!furi_string_empty(app->keyboard_layout)) { + Storage* fs_api = furi_record_open(RECORD_STORAGE); + FileInfo layout_file_info; + FS_Error file_check_err = storage_common_stat( + fs_api, furi_string_get_cstr(app->keyboard_layout), &layout_file_info); + furi_record_close(RECORD_STORAGE); + if(file_check_err != FSE_OK) { + furi_string_reset(app->keyboard_layout); + return; + } + if(layout_file_info.size != 256) { + furi_string_reset(app->keyboard_layout); + } + } +} + +static void bad_usb_save_settings(BadUsbApp* app) { + File* settings_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(storage_file_open(settings_file, BAD_USB_SETTINGS_PATH, FSAM_WRITE, FSOM_OPEN_ALWAYS)) { + storage_file_write( + settings_file, + furi_string_get_cstr(app->keyboard_layout), + furi_string_size(app->keyboard_layout)); + storage_file_write(settings_file, "\n", 1); + } + storage_file_close(settings_file); + storage_file_free(settings_file); +} + BadUsbApp* bad_usb_app_alloc(char* arg) { BadUsbApp* app = malloc(sizeof(BadUsbApp)); - app->file_path = furi_string_alloc(); + app->bad_usb_script = NULL; + app->file_path = furi_string_alloc(); + app->keyboard_layout = furi_string_alloc(); if(arg && strlen(arg)) { furi_string_set(app->file_path, arg); } + bad_usb_load_settings(app); + app->gui = furi_record_open(RECORD_GUI); app->notifications = furi_record_open(RECORD_NOTIFICATION); app->dialogs = furi_record_open(RECORD_DIALOGS); @@ -53,6 +103,10 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewError, widget_get_view(app->widget)); + app->submenu = submenu_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, BadUsbAppViewConfig, submenu_get_view(app->submenu)); + app->bad_usb_view = bad_usb_alloc(); view_dispatcher_add_view( app->view_dispatcher, BadUsbAppViewWork, bad_usb_get_view(app->bad_usb_view)); @@ -64,9 +118,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { scene_manager_next_scene(app->scene_manager, BadUsbSceneError); } else { if(!furi_string_empty(app->file_path)) { + app->bad_usb_script = bad_usb_script_open(app->file_path); + bad_usb_script_set_keyboard_layout(app->bad_usb_script, app->keyboard_layout); scene_manager_next_scene(app->scene_manager, BadUsbSceneWork); } else { - furi_string_set(app->file_path, BAD_USB_APP_PATH_FOLDER); + furi_string_set(app->file_path, BAD_USB_APP_BASE_FOLDER); scene_manager_next_scene(app->scene_manager, BadUsbSceneFileSelect); } } @@ -77,6 +133,11 @@ BadUsbApp* bad_usb_app_alloc(char* arg) { void bad_usb_app_free(BadUsbApp* app) { furi_assert(app); + if(app->bad_usb_script) { + bad_usb_script_close(app->bad_usb_script); + app->bad_usb_script = NULL; + } + // Views view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewWork); bad_usb_free(app->bad_usb_view); @@ -85,6 +146,10 @@ void bad_usb_app_free(BadUsbApp* app) { view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewError); widget_free(app->widget); + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, BadUsbAppViewConfig); + submenu_free(app->submenu); + // View dispatcher view_dispatcher_free(app->view_dispatcher); scene_manager_free(app->scene_manager); @@ -94,7 +159,10 @@ void bad_usb_app_free(BadUsbApp* app) { furi_record_close(RECORD_NOTIFICATION); furi_record_close(RECORD_DIALOGS); + bad_usb_save_settings(app); + furi_string_free(app->file_path); + furi_string_free(app->keyboard_layout); free(app); } diff --git a/applications/main/bad_usb/bad_usb_app_i.h b/applications/main/bad_usb/bad_usb_app_i.h index f333c36d8..1bd1964c8 100644 --- a/applications/main/bad_usb/bad_usb_app_i.h +++ b/applications/main/bad_usb/bad_usb_app_i.h @@ -15,8 +15,10 @@ #include #include "views/bad_usb_view.h" -#define BAD_USB_APP_PATH_FOLDER ANY_PATH("badusb") -#define BAD_USB_APP_EXTENSION ".txt" +#define BAD_USB_APP_BASE_FOLDER ANY_PATH("badusb") +#define BAD_USB_APP_PATH_LAYOUT_FOLDER BAD_USB_APP_BASE_FOLDER "/assets/layouts" +#define BAD_USB_APP_SCRIPT_EXTENSION ".txt" +#define BAD_USB_APP_LAYOUT_EXTENSION ".kl" typedef enum { BadUsbAppErrorNoFiles, @@ -30,9 +32,11 @@ struct BadUsbApp { NotificationApp* notifications; DialogsApp* dialogs; Widget* widget; + Submenu* submenu; BadUsbAppError error; FuriString* file_path; + FuriString* keyboard_layout; BadUsb* bad_usb_view; BadUsbScript* bad_usb_script; }; @@ -40,4 +44,5 @@ struct BadUsbApp { typedef enum { BadUsbAppViewError, BadUsbAppViewWork, -} BadUsbAppView; + BadUsbAppViewConfig, +} BadUsbAppView; \ No newline at end of file diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index e2281133f..1416acfee 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -16,6 +16,9 @@ #define SCRIPT_STATE_END (-2) #define SCRIPT_STATE_NEXT_LINE (-3) +#define BADUSB_ASCII_TO_KEY(script, x) \ + (((uint8_t)x < 128) ? (script->layout[(uint8_t)x]) : HID_KEYBOARD_NONE) + typedef enum { WorkerEvtToggle = (1 << 0), WorkerEvtEnd = (1 << 1), @@ -28,6 +31,7 @@ struct BadUsbScript { BadUsbState st; FuriString* file_path; uint32_t defdelay; + uint16_t layout[128]; FuriThread* thread; uint8_t file_buf[FILE_BUFFER_LEN + 1]; uint8_t buf_start; @@ -205,10 +209,10 @@ static bool ducky_altstring(const char* param) { return state; } -static bool ducky_string(const char* param) { +static bool ducky_string(BadUsbScript* bad_usb, const char* param) { uint32_t i = 0; while(param[i] != '\0') { - uint16_t keycode = HID_ASCII_TO_KEY(param[i]); + uint16_t keycode = BADUSB_ASCII_TO_KEY(bad_usb, param[i]); if(keycode != HID_KEYBOARD_NONE) { furi_hal_hid_kb_press(keycode); furi_hal_hid_kb_release(keycode); @@ -218,7 +222,7 @@ static bool ducky_string(const char* param) { return true; } -static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { +static uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { for(size_t i = 0; i < (sizeof(ducky_keys) / sizeof(ducky_keys[0])); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); if((strncmp(param, ducky_keys[i].name, key_cmd_len) == 0) && @@ -227,7 +231,7 @@ static uint16_t ducky_get_keycode(const char* param, bool accept_chars) { } } if((accept_chars) && (strlen(param) > 0)) { - return (HID_ASCII_TO_KEY(param[0]) & 0xFF); + return (BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF); } return 0; } @@ -276,7 +280,7 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_string, strlen(ducky_cmd_string)) == 0) { // STRING line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - state = ducky_string(line_tmp); + state = ducky_string(bad_usb, line_tmp); if(!state && error != NULL) { snprintf(error, error_len, "Invalid string %s", line_tmp); } @@ -312,14 +316,14 @@ static int32_t } else if(strncmp(line_tmp, ducky_cmd_sysrq, strlen(ducky_cmd_sysrq)) == 0) { // SYSRQ line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - uint16_t key = ducky_get_keycode(line_tmp, true); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, true); furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_hid_kb_press(key); furi_hal_hid_kb_release_all(); return (0); } else { // Special keys + modifiers - uint16_t key = ducky_get_keycode(line_tmp, false); + uint16_t key = ducky_get_keycode(bad_usb, line_tmp, false); if(key == HID_KEYBOARD_NONE) { if(error != NULL) { snprintf(error, error_len, "No keycode defined for %s", line_tmp); @@ -329,7 +333,7 @@ static int32_t if((key & 0xFF00) != 0) { // It's a modifier key line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; - key |= ducky_get_keycode(line_tmp, true); + key |= ducky_get_keycode(bad_usb, line_tmp, true); } furi_hal_hid_kb_press(key); furi_hal_hid_kb_release(key); @@ -650,12 +654,19 @@ static int32_t bad_usb_worker(void* context) { return 0; } +static void bad_usb_script_set_default_keyboard_layout(BadUsbScript* bad_usb) { + furi_assert(bad_usb); + memset(bad_usb->layout, HID_KEYBOARD_NONE, sizeof(bad_usb->layout)); + memcpy(bad_usb->layout, hid_asciimap, MIN(sizeof(hid_asciimap), sizeof(bad_usb->layout))); +} + BadUsbScript* bad_usb_script_open(FuriString* file_path) { furi_assert(file_path); BadUsbScript* bad_usb = malloc(sizeof(BadUsbScript)); bad_usb->file_path = furi_string_alloc(); furi_string_set(bad_usb->file_path, file_path); + bad_usb_script_set_default_keyboard_layout(bad_usb); bad_usb->st.state = BadUsbStateInit; bad_usb->st.error[0] = '\0'; @@ -674,6 +685,30 @@ void bad_usb_script_close(BadUsbScript* bad_usb) { free(bad_usb); } +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path) { + furi_assert(bad_usb); + + if((bad_usb->st.state == BadUsbStateRunning) || (bad_usb->st.state == BadUsbStateDelay)) { + // do not update keyboard layout while a script is running + return; + } + + File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); + if(!furi_string_empty(layout_path)) { + if(storage_file_open( + layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint16_t layout[128]; + if(storage_file_read(layout_file, layout, sizeof(layout)) == sizeof(layout)) { + memcpy(bad_usb->layout, layout, sizeof(layout)); + } + } + storage_file_close(layout_file); + } else { + bad_usb_script_set_default_keyboard_layout(bad_usb); + } + storage_file_free(layout_file); +} + void bad_usb_script_toggle(BadUsbScript* bad_usb) { furi_assert(bad_usb); furi_thread_flags_set(furi_thread_get_id(bad_usb->thread), WorkerEvtToggle); diff --git a/applications/main/bad_usb/bad_usb_script.h b/applications/main/bad_usb/bad_usb_script.h index 188142db8..1e4d98fe7 100644 --- a/applications/main/bad_usb/bad_usb_script.h +++ b/applications/main/bad_usb/bad_usb_script.h @@ -33,6 +33,8 @@ BadUsbScript* bad_usb_script_open(FuriString* file_path); void bad_usb_script_close(BadUsbScript* bad_usb); +void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layout_path); + void bad_usb_script_start(BadUsbScript* bad_usb); void bad_usb_script_stop(BadUsbScript* bad_usb); diff --git a/applications/main/bad_usb/bad_usb_settings_filename.h b/applications/main/bad_usb/bad_usb_settings_filename.h new file mode 100644 index 000000000..12ba8f31c --- /dev/null +++ b/applications/main/bad_usb/bad_usb_settings_filename.h @@ -0,0 +1,3 @@ +#pragma once + +#define BAD_USB_SETTINGS_FILE_NAME ".badusb.settings" diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.c b/applications/main/bad_usb/scenes/bad_usb_scene_config.c new file mode 100644 index 000000000..2a9f2f76c --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.c @@ -0,0 +1,53 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" + +enum SubmenuIndex { + SubmenuIndexKeyboardLayout, +}; + +void bad_usb_scene_config_submenu_callback(void* context, uint32_t index) { + BadUsbApp* bad_usb = context; + view_dispatcher_send_custom_event(bad_usb->view_dispatcher, index); +} + +void bad_usb_scene_config_on_enter(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_add_item( + submenu, + "Keyboard layout", + SubmenuIndexKeyboardLayout, + bad_usb_scene_config_submenu_callback, + bad_usb); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(bad_usb->scene_manager, BadUsbSceneConfig)); + + view_dispatcher_switch_to_view(bad_usb->view_dispatcher, BadUsbAppViewConfig); +} + +bool bad_usb_scene_config_on_event(void* context, SceneManagerEvent event) { + BadUsbApp* bad_usb = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + scene_manager_set_scene_state(bad_usb->scene_manager, BadUsbSceneConfig, event.event); + consumed = true; + if(event.event == SubmenuIndexKeyboardLayout) { + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneConfigLayout); + } else { + furi_crash("Unknown key type"); + } + } + + return consumed; +} + +void bad_usb_scene_config_on_exit(void* context) { + BadUsbApp* bad_usb = context; + Submenu* submenu = bad_usb->submenu; + + submenu_reset(submenu); +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config.h b/applications/main/bad_usb/scenes/bad_usb_scene_config.h index 0ab8f54f8..423aedc51 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_config.h +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config.h @@ -1,3 +1,5 @@ ADD_SCENE(bad_usb, file_select, FileSelect) ADD_SCENE(bad_usb, work, Work) ADD_SCENE(bad_usb, error, Error) +ADD_SCENE(bad_usb, config, Config) +ADD_SCENE(bad_usb, config_layout, ConfigLayout) diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c new file mode 100644 index 000000000..7708ed1d8 --- /dev/null +++ b/applications/main/bad_usb/scenes/bad_usb_scene_config_layout.c @@ -0,0 +1,50 @@ +#include "../bad_usb_app_i.h" +#include "furi_hal_power.h" +#include "furi_hal_usb.h" +#include + +static bool bad_usb_layout_select(BadUsbApp* bad_usb) { + furi_assert(bad_usb); + + FuriString* predefined_path; + predefined_path = furi_string_alloc(); + if(!furi_string_empty(bad_usb->keyboard_layout)) { + furi_string_set(predefined_path, bad_usb->keyboard_layout); + } else { + furi_string_set(predefined_path, BAD_USB_APP_PATH_LAYOUT_FOLDER); + } + + DialogsFileBrowserOptions browser_options; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = BAD_USB_APP_PATH_LAYOUT_FOLDER; + browser_options.skip_assets = false; + + // Input events and views are managed by file_browser + bool res = dialog_file_browser_show( + bad_usb->dialogs, bad_usb->keyboard_layout, predefined_path, &browser_options); + + furi_string_free(predefined_path); + return res; +} + +void bad_usb_scene_config_layout_on_enter(void* context) { + BadUsbApp* bad_usb = context; + + if(bad_usb_layout_select(bad_usb)) { + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + } + scene_manager_previous_scene(bad_usb->scene_manager); +} + +bool bad_usb_scene_config_layout_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + // BadUsbApp* bad_usb = context; + return false; +} + +void bad_usb_scene_config_layout_on_exit(void* context) { + UNUSED(context); + // BadUsbApp* bad_usb = context; +} diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c index f1f34f5bc..b04669252 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_file_select.c @@ -7,8 +7,10 @@ static bool bad_usb_file_select(BadUsbApp* bad_usb) { furi_assert(bad_usb); DialogsFileBrowserOptions browser_options; - dialog_file_browser_set_basic_options(&browser_options, BAD_USB_APP_EXTENSION, &I_badusb_10px); - browser_options.base_path = BAD_USB_APP_PATH_FOLDER; + dialog_file_browser_set_basic_options( + &browser_options, BAD_USB_APP_SCRIPT_EXTENSION, &I_badusb_10px); + browser_options.base_path = BAD_USB_APP_BASE_FOLDER; + browser_options.skip_assets = true; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( @@ -21,12 +23,18 @@ void bad_usb_scene_file_select_on_enter(void* context) { BadUsbApp* bad_usb = context; furi_hal_usb_disable(); + if(bad_usb->bad_usb_script) { + bad_usb_script_close(bad_usb->bad_usb_script); + bad_usb->bad_usb_script = NULL; + } if(bad_usb_file_select(bad_usb)) { + bad_usb->bad_usb_script = bad_usb_script_open(bad_usb->file_path); + bad_usb_script_set_keyboard_layout(bad_usb->bad_usb_script, bad_usb->keyboard_layout); + scene_manager_next_scene(bad_usb->scene_manager, BadUsbSceneWork); } else { furi_hal_usb_enable(); - //scene_manager_previous_scene(bad_usb->scene_manager); view_dispatcher_stop(bad_usb->view_dispatcher); } } diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_work.c b/applications/main/bad_usb/scenes/bad_usb_scene_work.c index 1e3534822..187b83bd9 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_work.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_work.c @@ -4,10 +4,10 @@ #include #include "toolbox/path.h" -void bad_usb_scene_work_ok_callback(InputType type, void* context) { +void bad_usb_scene_work_button_callback(InputKey key, void* context) { furi_assert(context); BadUsbApp* app = context; - view_dispatcher_send_custom_event(app->view_dispatcher, type); + view_dispatcher_send_custom_event(app->view_dispatcher, key); } bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { @@ -15,8 +15,13 @@ bool bad_usb_scene_work_on_event(void* context, SceneManagerEvent event) { bool consumed = false; if(event.type == SceneManagerEventTypeCustom) { - bad_usb_script_toggle(app->bad_usb_script); - consumed = true; + if(event.event == InputKeyLeft) { + scene_manager_next_scene(app->scene_manager, BadUsbSceneConfig); + consumed = true; + } else if(event.event == InputKeyOk) { + bad_usb_script_toggle(app->bad_usb_script); + consumed = true; + } } else if(event.type == SceneManagerEventTypeTick) { bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); } @@ -28,20 +33,22 @@ void bad_usb_scene_work_on_enter(void* context) { FuriString* file_name; file_name = furi_string_alloc(); - path_extract_filename(app->file_path, file_name, true); bad_usb_set_file_name(app->bad_usb_view, furi_string_get_cstr(file_name)); - app->bad_usb_script = bad_usb_script_open(app->file_path); - furi_string_free(file_name); + FuriString* layout; + layout = furi_string_alloc(); + path_extract_filename(app->keyboard_layout, layout, true); + bad_usb_set_layout(app->bad_usb_view, furi_string_get_cstr(layout)); + furi_string_free(layout); + bad_usb_set_state(app->bad_usb_view, bad_usb_script_get_state(app->bad_usb_script)); - bad_usb_set_ok_callback(app->bad_usb_view, bad_usb_scene_work_ok_callback, app); + bad_usb_set_button_callback(app->bad_usb_view, bad_usb_scene_work_button_callback, app); view_dispatcher_switch_to_view(app->view_dispatcher, BadUsbAppViewWork); } void bad_usb_scene_work_on_exit(void* context) { - BadUsbApp* app = context; - bad_usb_script_close(app->bad_usb_script); + UNUSED(context); } diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index b3eb9bb56..bb9dc3b7e 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -1,5 +1,6 @@ #include "bad_usb_view.h" #include "../bad_usb_script.h" +#include #include #include @@ -7,12 +8,13 @@ struct BadUsb { View* view; - BadUsbOkCallback callback; + BadUsbButtonCallback callback; void* context; }; typedef struct { char file_name[MAX_NAME_LEN]; + char layout[MAX_NAME_LEN]; BadUsbState state; uint8_t anim_frame; } BadUsbModel; @@ -25,9 +27,23 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + + if(strlen(model->layout) == 0) { + furi_string_set(disp_str, "(default)"); + } else { + furi_string_reset(disp_str); + furi_string_push_back(disp_str, '('); + for(size_t i = 0; i < strlen(model->layout); i++) + furi_string_push_back(disp_str, model->layout[i]); + furi_string_push_back(disp_str, ')'); + } + elements_string_fit_width(canvas, disp_str, 128 - 2); + canvas_draw_str( + canvas, 2, 8 + canvas_current_font_height(canvas), furi_string_get_cstr(disp_str)); + furi_string_reset(disp_str); - canvas_draw_icon(canvas, 22, 20, &I_UsbTree_48x22); + canvas_draw_icon(canvas, 22, 24, &I_UsbTree_48x22); if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { @@ -38,23 +54,28 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_button_center(canvas, "Cancel"); } + if((model->state.state == BadUsbStateNotConnected) || + (model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone)) { + elements_button_left(canvas, "Config"); + } + if(model->state.state == BadUsbStateNotConnected) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Connect"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "to USB"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Connect"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "to USB"); } else if(model->state.state == BadUsbStateWillRun) { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "Will run"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "on connect"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "on connect"); } else if(model->state.state == BadUsbStateFileError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); - canvas_draw_str_aligned(canvas, 127, 27, AlignRight, AlignBottom, "File"); - canvas_draw_str_aligned(canvas, 127, 39, AlignRight, AlignBottom, "ERROR"); + canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "File"); + canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "ERROR"); } else if(model->state.state == BadUsbStateScriptError) { - canvas_draw_icon(canvas, 4, 22, &I_Error_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Error_18x18); canvas_set_font(canvas, FontPrimary); canvas_draw_str_aligned(canvas, 127, 33, AlignRight, AlignBottom, "ERROR:"); canvas_set_font(canvas, FontSecondary); @@ -64,49 +85,49 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { furi_string_reset(disp_str); canvas_draw_str_aligned(canvas, 127, 56, AlignRight, AlignBottom, model->state.error); } else if(model->state.state == BadUsbStateIdle) { - canvas_draw_icon(canvas, 4, 22, &I_Smile_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Smile_18x18); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "0"); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "0"); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateRunning) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDone) { - canvas_draw_icon(canvas, 4, 19, &I_EviSmile1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviSmile1_18x21); canvas_set_font(canvas, FontBigNumbers); - canvas_draw_str_aligned(canvas, 114, 36, AlignRight, AlignBottom, "100"); + canvas_draw_str_aligned(canvas, 114, 40, AlignRight, AlignBottom, "100"); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); } else if(model->state.state == BadUsbStateDelay) { if(model->anim_frame == 0) { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting1_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting1_18x21); } else { - canvas_draw_icon(canvas, 4, 19, &I_EviWaiting2_18x21); + canvas_draw_icon(canvas, 4, 23, &I_EviWaiting2_18x21); } canvas_set_font(canvas, FontBigNumbers); furi_string_printf( disp_str, "%u", ((model->state.line_cur - 1) * 100) / model->state.line_nb); canvas_draw_str_aligned( - canvas, 114, 36, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 114, 40, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); - canvas_draw_icon(canvas, 117, 22, &I_Percent_10x14); + canvas_draw_icon(canvas, 117, 26, &I_Percent_10x14); canvas_set_font(canvas, FontSecondary); furi_string_printf(disp_str, "delay %lus", model->state.delay_remain); canvas_draw_str_aligned( - canvas, 127, 46, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); + canvas, 127, 50, AlignRight, AlignBottom, furi_string_get_cstr(disp_str)); furi_string_reset(disp_str); } else { - canvas_draw_icon(canvas, 4, 22, &I_Clock_18x18); + canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); } furi_string_free(disp_str); @@ -118,10 +139,10 @@ static bool bad_usb_input_callback(InputEvent* event, void* context) { bool consumed = false; if(event->type == InputTypeShort) { - if(event->key == InputKeyOk) { + if((event->key == InputKeyLeft) || (event->key == InputKeyOk)) { consumed = true; furi_assert(bad_usb->callback); - bad_usb->callback(InputTypeShort, bad_usb->context); + bad_usb->callback(event->key, bad_usb->context); } } @@ -151,7 +172,7 @@ View* bad_usb_get_view(BadUsb* bad_usb) { return bad_usb->view; } -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context) { +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context) { furi_assert(bad_usb); furi_assert(callback); with_view_model( @@ -174,6 +195,14 @@ void bad_usb_set_file_name(BadUsb* bad_usb, const char* name) { true); } +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout) { + furi_assert(layout); + with_view_model( + bad_usb->view, + BadUsbModel * model, + { strlcpy(model->layout, layout, MAX_NAME_LEN); }, + true); +} void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st) { furi_assert(st); with_view_model( diff --git a/applications/main/bad_usb/views/bad_usb_view.h b/applications/main/bad_usb/views/bad_usb_view.h index 80a47e2ca..8447fb055 100644 --- a/applications/main/bad_usb/views/bad_usb_view.h +++ b/applications/main/bad_usb/views/bad_usb_view.h @@ -4,7 +4,7 @@ #include "../bad_usb_script.h" typedef struct BadUsb BadUsb; -typedef void (*BadUsbOkCallback)(InputType type, void* context); +typedef void (*BadUsbButtonCallback)(InputKey key, void* context); BadUsb* bad_usb_alloc(); @@ -12,8 +12,10 @@ void bad_usb_free(BadUsb* bad_usb); View* bad_usb_get_view(BadUsb* bad_usb); -void bad_usb_set_ok_callback(BadUsb* bad_usb, BadUsbOkCallback callback, void* context); +void bad_usb_set_button_callback(BadUsb* bad_usb, BadUsbButtonCallback callback, void* context); void bad_usb_set_file_name(BadUsb* bad_usb, const char* name); +void bad_usb_set_layout(BadUsb* bad_usb, const char* layout); + void bad_usb_set_state(BadUsb* bad_usb, BadUsbState* st); diff --git a/assets/icons/Archive/keyboard_10px.png b/assets/icons/Archive/keyboard_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..74a10e6db2e784486a6781c0db8346373b9c7409 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2xGmzZ=C-xtZVhivIasB`QKad%E=yDy9;wgN=z( mnGc5;aHhO3XW%q4U|?wcz_Hx?k)a$=ErX}4pUXO@geCyYPARJZ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/ba-BA.kl b/assets/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHkcVK4j(2-*&PMujecVX$$ zm1{Rv)^6RqGmA3vIK<6U^hikQGaw^p$cQl$rpzdqv!GYm`cD?ePm*T4GZVB*m| zTln^pGi$gvZr&YZh&>B_Ym yx9;4VM-@dp{CKxUod!)>wCT{LN1p*hMvR#-WyYKZOIEDeuw}=dEbdXpBj69fFAeVi literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-CH.kl b/assets/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 0000000000000000000000000000000000000000..1704bc9dbaae9fcc90213cb33b0132bca1b71866 GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5i<8!$oa;st31l>ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-DE.kl b/assets/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 0000000000000000000000000000000000000000..67b05c042f1649ab6aa17c4d369b096c88ec2785 GIT binary patch literal 256 zcmaLLHx9y30KiboqW2Pd4WuBN!ay1SVRHX7Ftgz88Gbu+;q<%y>dMxg4`L4uK0?Afwu&6#5(8&;hd+qL7+fvJ5TwhjIGHgM$E s3wH`~pT4NjqeYV%GsZ+Ts4^j@ONSu?Qu;h-6Y}9rz=$sw?-ss^@Y|jPyWjO!M`q5wk-2p3gN<8r z&%QYE=EIg5a~5PQS+OQ(!`(YC&%FKkuw>baRci*;Z5Z0LW!sLCv58$%d-feTbmZ8H tQ!f-;S$XnFi82+c)Cj24Af!o)HXR~j61t@H=rdr*h%pnUTr9kL6TT^X3*7(! literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/en-US.kl b/assets/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 0000000000000000000000000000000000000000..8089d8257881765fe67691a206b6bf6e9cb2c97d GIT binary patch literal 256 zcmaLL#}dH+007aQ9YGK+QKLn)QG!wb|I3_nvA4HS?#PKldHvOyg-=&zuHE=#=iaS_ zZ!UcJvS-1P6&Y)CHf-7P@WI=Hk6)Ko46ItSZfL`%EhF1@?Ao($Y~sM7BgamhnmTjt sl{+_bFTSV{P^CtlkOoa!M6~J9rAMEbgaJcFjF~VcWyYMVcW=Ig9}}MoYybcN literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/es-ES.kl b/assets/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 0000000000000000000000000000000000000000..15e9d7997c3f5178982fe212b5341f09469486bc GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5lA#$@e+Y@}VF{DLgOx;B0)}2=l z9xW`m^W?$a6A39BGPdm4bKuCsyDv_@yqZSIvK6b=tlO|@%eEc6_Ut=w=*Y1Xr_P+a zaOujmdHhEaxleOSl&MgqMx6#tTD0lVrAMCuLq?35FlEM^1xr?}`HLiOkp;W~w>u5X literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-BE.kl b/assets/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 0000000000000000000000000000000000000000..ea9e553e894a470639ee48648a386898ed5cfa67 GIT binary patch literal 256 zcmaLLNm9Z96adk#F6Mcj2}uy4P%#!3Wcd%1`=7xz8+-Q}Uc2(a<#qk5PfnzyQSfU* zM$Q+7r6X=Ue0nzVU}|RW(Kk=N`{Y4IN#)2x%&gn6Y0I`9yY}omaOmB!6Q|akyKw2s zwR^Yj+?YodMSOGfCuY=X(4ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-FR.kl b/assets/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 0000000000000000000000000000000000000000..f9193297e58722fd4f1547cb9ef62474beb2487b GIT binary patch literal 256 zcmaLLH*Nv}6adlDh@5i{8(2U#EVi(~}3$%7XqF9)7tV%3^;8#Zm(wqw_xeeVt(Ikw`|nR6E| z-MV(;$}Ebw#*KnQOsG<$PJ<>b+H~mBqtBfoBgRyiGGoqyC0o{PSdm5^W$Xj~0L-fm A_W%F@ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/hr-HR.kl b/assets/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk)x=fVv{{L5{w#jy9;k{43_;_Fc>YIyakMR>% z$I*KC;Silq2439!rR7Y=g)29D?mT$;j68aqcv{8C#IX~nre@BZyKw2s+`_dROSkUa zd$6+hXyeH=%E+RN!9GSLjF~VcC1b{%1xr@s6s*}$vSr7f0~Iw#8crlpM-gqnA2aL@ AGynhq literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/it-IT.kl b/assets/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 0000000000000000000000000000000000000000..059e428808a07b26d0da37d7f37d5d8d4614365b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>eeY5fH(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvm0r4@+k!5$Wx$5i83J(6{^&z)1XO96ECB#K@^L=Pq0t xyK-$Ft`y%!)Nxcub+H~j=&?BVJfFUEsOqeobPMlv%@{{ldJ?{*0 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/pt-BR.kl b/assets/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 0000000000000000000000000000000000000000..d36421cfc45747687dba3cabe05ff84a4550050a GIT binary patch literal 256 zcmaKnM-ssR004J(1W9z!q7!8hWkx%CjQ_uZtBbwu-WGn@v1j|6{@Q_wH)qaWn0aIE z#+66!wV)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk17S!45fq0~DYekR@*l?gpTRX7d-ob{d*zLn+x4&B`SxrR z=Qx?hB0l}{*Orbe7e0Kp_0GKq?|ty-qfcJ^p^Fo{=&Yh=Vrt)knYlwpj-5DlX5rk0 zrAt??-B?-MxN~b3W#qAstHmjD0& literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/sv-SE.kl b/assets/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 0000000000000000000000000000000000000000..5c55bb9ef1df3785fb143eb463a5375be10ce96f GIT binary patch literal 256 zcmaLLH*x|26u?l8hMaSTC2U}9uLl~S{D;~5pMjbNbx-iw55HVL*H`^JB+m2m(_8mBMwCT{LN1p*hMvR#-WyYKZOIEDeuw}=dG^!}#7VrkLXASNE literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/tr-TR.kl b/assets/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 0000000000000000000000000000000000000000..6377b770365ed544824108765ce2587f315bc1da GIT binary patch literal 256 zcmaLLM{dFZ6adlDh~A6oU~F)Sk{HDcB$od$x&IlmW|P^yf%iW7;^Te&t8Y@DWRb_s zyH`sq8**D7yeOY}m46&w(Q+&Ri(sH|qEa_ygc8 B4s!qi literal 0 HcmV?d00001 From 8288a08eb3ab1c49451c1abcf1bc38bbfdcc8c71 Mon Sep 17 00:00:00 2001 From: Brandon Weeks Date: Wed, 8 Feb 2023 07:26:45 -0800 Subject: [PATCH 121/231] SubGhz: add protocol "Linear Delta-3" (#2239) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add protocol "Linear Delta-3" * SubGhz: fix Leniar Delta 3 * BadUSB: mask pvs studio warning for valid code Co-authored-by: SkorP Co-authored-by: あく Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com> --- .../debug/unit_tests/subghz/subghz_test.c | 18 +- applications/main/bad_usb/bad_usb_script.c | 2 +- assets/unit_tests/subghz/linear_delta3.sub | 7 + .../unit_tests/subghz/linear_delta3_raw.sub | 8 + assets/unit_tests/subghz/test_random_raw.sub | 2 + lib/subghz/protocols/linear_delta3.c | 359 ++++++++++++++++++ lib/subghz/protocols/linear_delta3.h | 111 ++++++ lib/subghz/protocols/protocol_items.c | 49 ++- lib/subghz/protocols/protocol_items.h | 1 + 9 files changed, 543 insertions(+), 14 deletions(-) create mode 100644 assets/unit_tests/subghz/linear_delta3.sub create mode 100644 assets/unit_tests/subghz/linear_delta3_raw.sub create mode 100644 lib/subghz/protocols/linear_delta3.c create mode 100644 lib/subghz/protocols/linear_delta3.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 1dee1d59e..83fadeda9 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/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 273 +#define TEST_RANDOM_COUNT_PARSE 295 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -489,6 +489,14 @@ MU_TEST(subghz_decoder_linear_test) { "Test decoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_decoder_linear_delta3_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/linear_delta3_raw.sub"), + SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME), + "Test decoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_decoder_megacode_test) { mu_assert( subghz_decoder_test( @@ -647,6 +655,12 @@ MU_TEST(subghz_encoder_linear_test) { "Test encoder " SUBGHZ_PROTOCOL_LINEAR_NAME " error\r\n"); } +MU_TEST(subghz_encoder_linear_delta3_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/linear_delta3.sub")), + "Test encoder " SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME " error\r\n"); +} + MU_TEST(subghz_encoder_megacode_test) { mu_assert( subghz_encoder_test(EXT_PATH("unit_tests/subghz/megacode.sub")), @@ -772,6 +786,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_somfy_telis_test); MU_RUN_TEST(subghz_decoder_star_line_test); MU_RUN_TEST(subghz_decoder_linear_test); + MU_RUN_TEST(subghz_decoder_linear_delta3_test); MU_RUN_TEST(subghz_decoder_megacode_test); MU_RUN_TEST(subghz_decoder_secplus_v1_test); MU_RUN_TEST(subghz_decoder_secplus_v2_test); @@ -796,6 +811,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_nice_flo_test); MU_RUN_TEST(subghz_encoder_keelog_test); MU_RUN_TEST(subghz_encoder_linear_test); + MU_RUN_TEST(subghz_encoder_linear_delta3_test); MU_RUN_TEST(subghz_encoder_megacode_test); MU_RUN_TEST(subghz_encoder_holtek_test); MU_RUN_TEST(subghz_encoder_secplus_v1_test); diff --git a/applications/main/bad_usb/bad_usb_script.c b/applications/main/bad_usb/bad_usb_script.c index 1416acfee..0fadbcc07 100644 --- a/applications/main/bad_usb/bad_usb_script.c +++ b/applications/main/bad_usb/bad_usb_script.c @@ -694,7 +694,7 @@ void bad_usb_script_set_keyboard_layout(BadUsbScript* bad_usb, FuriString* layou } File* layout_file = storage_file_alloc(furi_record_open(RECORD_STORAGE)); - if(!furi_string_empty(layout_path)) { + if(!furi_string_empty(layout_path)) { //-V1051 if(storage_file_open( layout_file, furi_string_get_cstr(layout_path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint16_t layout[128]; diff --git a/assets/unit_tests/subghz/linear_delta3.sub b/assets/unit_tests/subghz/linear_delta3.sub new file mode 100644 index 000000000..f00507428 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: LinearDelta3 +Bit: 8 +Key: 00 00 00 00 00 00 00 D0 diff --git a/assets/unit_tests/subghz/linear_delta3_raw.sub b/assets/unit_tests/subghz/linear_delta3_raw.sub new file mode 100644 index 000000000..1973622a5 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index be635f04d..7571d688d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -173,3 +173,5 @@ RAW_Data: 107 -1501 77 -1518 53 -704 113 -390 107 -650 73 -932 51 -3641 169 -704 RAW_Data: 79 -4798 53 -918 83 -4847 51 -755 103 -732 81 -388 55 -1026 77 -1506 101 -242 107 -469 51 -2026 79 -686 77 -348 51 -104 131 -860 129 -148 73 -446 75 -440 97 -306 99 -600 51 -626 105 -1350 95 -674 83 -230 119 -1714 135 -396 155 -1111 109 -652 111 -482 51 -506 55 -1715 103 -968 207 -1156 81 -164 57 -404 99 -508 205 -126 75 -1417 51 -186 77 -588 53 -54 103 -2854 73 -1010 53 -800 51 -2494 53 -106 105 -52 51 -104 79 -1116 51 -654 103 -220 77 -162 71 -5385 137 -2232 79 -1159 79 -250 57 -108 79 -164 107 -1660 79 -3927 129 -992 73 -1913 51 -1430 51 -1498 55 -514 103 -586 81 -386 53 -2402 175 -1994 85 -3431 53 -3209 99 -372 79 -78 53 -1338 75 -682 97 -680 51 -206 101 -1708 101 -452 131 -1397 161 -2272 53 -456 77 -1413 193 -270 109 -466 53 -2432 77 -222 189 -474 107 -774 171 -192 79 -1327 75 -2141 51 -908 135 -3866 75 -804 129 -468 101 -1040 79 -1470 55 -869 77 -1448 105 -160 55 -1916 240 -588 79 -1587 53 -922 79 -2292 181 -1448 51 -552 77 -2189 75 -2545 77 -384 300 -2478 101 -1092 73 -558 79 -132 105 -884 103 -1177 109 -880 79 -2431 109 -1006 105 -468 53 -1378 235 -684 75 -285 73 -604 129 -528 77 -1582 51 -1240 105 -2750 75 -252 51 -1024 95 -1891 51 -864 107 -326 83 -887 159 -1058 163 -322 105 -722 83 -388 81 -936 155 -880 55 -220 83 -2123 135 -2100 73 -1926 103 -1633 149 -526 51 -324 51 -1538 103 -164 137 -964 81 -152 111 -781 225 -655 53 -2888 105 -151 131 -454 53 -4109 77 -1052 53 -178 163 -910 51 -733 207 -2070 53 -474 79 -54 53 -818 51 -1228 53 -2262 79 -788 79 -480 73 -2747 83 -316 183 -1880 105 -862 53 -662 53 -2287 153 -1630 51 -817 243 -806 55 -510 51 -1389 75 -986 135 -498 109 -532 131 -5521 99 -2948 209 -764 75 -1168 75 -886 83 -2065 53 -710 51 -596 77 -374 73 -628 99 -732 51 -202 73 -632 53 -222 55 -511 79 -4884 53 -1826 81 -1266 107 -356 55 -110 113 -280 83 -756 169 -252 81 -1854 51 -1556 157 -258 75 -748 53 -1438 291 -244 71 -1092 77 -1220 229 -1055 181 -1182 71 -1284 77 -864 79 -138 53 -160 53 -952 81 -80 127 -1272 51 -590 103 -502 77 -634 101 -74 51 -224 101 -912 77 -562 51 -164 83 -396 105 -4643 111 -3293 133 -1395 107 -3047 137 -2353 53 -298 83 -54 81 -80 53 -162 83 -392 105 -606 107 -787 53 -928 51 -2800 161 -1146 51 -182 103 -536 103 -994 81 -2044 83 -732 133 -1881 133 -2160 75 -178 RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 125 -128 51 -5075 77 -1992 83 -1272 176 -2100 53 -2044 53 -1234 79 -1704 157 -519 99 -2374 101 -100 103 -202 51 -360 77 -1962 103 -2153 77 -1820 191 -164 167 -1320 77 -1718 127 -1374 81 -1047 53 -54 79 -632 53 -656 51 -128 81 -216 51 -755 79 -2692 103 -1478 125 -452 51 -896 157 -3679 135 -632 105 -134 55 -112 77 -588 79 -188 55 -1118 79 -1152 51 -1950 109 -1858 103 -1104 81 -580 131 -226 255 -2932 77 -1536 51 -1044 159 -2135 67667 -252 333 -278 333 -276 333 -74 533 -280 307 -276 345 -6930 331 -276 329 -278 329 -278 327 -278 349 -270 325 -270 317 -290 313 -308 283 -306 309 -100 509 -306 283 -328 281 -6972 307 -302 281 -326 281 -326 281 -328 279 -328 281 -326 279 -328 279 -326 281 -328 279 -124 481 -308 281 -326 279 -6998 281 -326 279 -328 279 -328 277 -330 277 -328 279 -328 279 -328 277 -304 303 -302 303 -100 503 -306 295 -322 293 -6968 287 -342 259 -336 283 -332 257 -356 255 -328 283 -328 257 -352 257 -352 257 -352 255 -150 455 -334 281 -326 281 -6996 265 -342 253 -354 253 -354 253 -354 253 -354 267 -326 269 -350 263 -342 257 -336 259 -152 459 -360 257 -354 231 -7038 267 -338 255 -352 253 -354 253 -354 239 -354 269 -352 239 -372 233 -366 233 -358 257 -154 457 -360 231 -378 231 -7050 231 -380 231 -378 231 -380 231 -378 231 -354 255 -352 257 -352 255 -354 231 -378 229 -176 453 -358 229 -378 231 -7076 231 -378 231 -380 231 -380 229 -354 255 -354 257 -352 257 -352 257 -352 257 -354 255 -150 455 -358 265 -364 229 -4941 101 -1058 153 -670 157 -532 124 -1396 133 -82 165 -162 153 -258 207 -156 131 -1582 85 -714 53 -774 103 -396 274 -110 131 -1965 55 -402 159 -1026 79 -590 77 -3531 57 -500 51 -4770 109 -722 77 -186 53 -298 79 -502 165 -808 77 -438 53 -382 101 -1914 75 -504 77 -1969 135 -5517 99 -576 51 -608 243 -684 53 -2058 315 -1384 79 -1079 77 -232 79 -212 155 -1500 137 -258 75 -975 204 -752 83 -2542 51 -484 103 -78 77 -210 53 -922 157 -1900 107 -2173 83 -384 101 -80 128 -814 183 -978 127 -772 105 -2073 51 -708 53 -300 83 -739 237 -884 131 -3412 157 -1752 81 -164 83 -3373 53 -1406 105 -3809 79 -432 51 -724 77 -548 53 -1955 79 -807 81 -2096 103 -490 105 -1196 109 -108 79 -394 71 -1159 129 -126 143 -340 107 -556 81 -2390 135 -106 133 -690 133 -4347 189 -290 51 -110 53 -78 103 -1101 51 -1362 RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* 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; + } + + //Send key data + 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_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_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); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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 = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_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_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 2022e9c47..a1f1bc149 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,19 +1,44 @@ #include "protocol_items.h" const SubGhzProtocol* subghz_protocol_registry_items[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &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_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_smc5326, + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &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_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 998fb56b3..185e47394 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -21,6 +21,7 @@ #include "gate_tx.h" #include "raw.h" #include "linear.h" +#include "linear_delta3.h" #include "secplus_v2.h" #include "secplus_v1.h" #include "megacode.h" From 0afc4a8982265d432a473b26cc94973f105973ca Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:37:24 +0400 Subject: [PATCH 122/231] [FL-3092] SubGhz: add DOOYA protocol (#2178) * SubGhz: add DOOYA protocol * SubGhz: add unit_test DOOYA protocol * SubGhz: fix protocol Dooya Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 17 +- assets/unit_tests/subghz/dooya.sub | 7 + assets/unit_tests/subghz/dooya_raw.sub | 8 + assets/unit_tests/subghz/test_random_raw.sub | 3 + lib/subghz/protocols/dooya.c | 447 ++++++++++++++++++ lib/subghz/protocols/dooya.h | 107 +++++ lib/subghz/protocols/protocol_items.c | 3 +- lib/subghz/protocols/protocol_items.h | 1 + 8 files changed, 591 insertions(+), 2 deletions(-) create mode 100644 assets/unit_tests/subghz/dooya.sub create mode 100644 assets/unit_tests/subghz/dooya_raw.sub create mode 100644 lib/subghz/protocols/dooya.c create mode 100644 lib/subghz/protocols/dooya.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 83fadeda9..0dba19d90 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/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 295 +#define TEST_RANDOM_COUNT_PARSE 300 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -612,6 +612,13 @@ MU_TEST(subghz_decoder_holtek_ht12x_test) { "Test decoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_decoder_dooya_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/dooya_raw.sub"), SUBGHZ_PROTOCOL_DOOYA_NAME), + "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -757,6 +764,12 @@ MU_TEST(subghz_encoder_holtek_ht12x_test) { "Test encoder " SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME " error\r\n"); } +MU_TEST(subghz_encoder_dooya_test) { + mu_assert( + subghz_encoder_test(EXT_PATH("unit_tests/subghz/dooya.sub")), + "Test encoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); +} + MU_TEST(subghz_random_test) { mu_assert(subghz_decode_random_test(TEST_RANDOM_DIR_NAME), "Random test error\r\n"); } @@ -803,6 +816,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_ansonic_test); MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); @@ -828,6 +842,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_encoder_ansonic_test); MU_RUN_TEST(subghz_encoder_smc5326_test); MU_RUN_TEST(subghz_encoder_holtek_ht12x_test); + MU_RUN_TEST(subghz_encoder_dooya_test); MU_RUN_TEST(subghz_random_test); subghz_test_deinit(); diff --git a/assets/unit_tests/subghz/dooya.sub b/assets/unit_tests/subghz/dooya.sub new file mode 100644 index 000000000..0767a1a73 --- /dev/null +++ b/assets/unit_tests/subghz/dooya.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Dooya +Bit: 40 +Key: 00 00 00 E1 DC 03 05 11 diff --git a/assets/unit_tests/subghz/dooya_raw.sub b/assets/unit_tests/subghz/dooya_raw.sub new file mode 100644 index 000000000..6c3ca1627 --- /dev/null +++ b/assets/unit_tests/subghz/dooya_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 7571d688d..aee15a16d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -175,3 +175,6 @@ RAW_Data: 75 -1694 101 -122 73 -864 51 -250 129 -406 77 -630 77 -610 101 -781 12 RAW_Data: 83 -320 81 -4648 101 -3726 173 -1418 85 -348 53 -2994 79 -1390 51 -1656 107 -764 53 -134 79 -1619 131 -932 55 -2810 107 -3218 79 -765 107 -654 103 -1498 77 -228 51 -134 247 -1526 51 -3903 103 -1495 179 -282 77 -392 53 -1756 105 -368 111 -486 51 -298 53 -216 113 -358 51 -266 187 -1059 81 -780 105 -238 51 -482 53 -791 109 -2169 77 -5304 53 -398 79 -650 51 -54 51 -1789 73 -198 101 -1580 101 -746 97 -4518 53 -744 51 -1064 101 -928 111 -392 185 -869 103 -320 133 -704 81 -244 53 -1628 75 -634 79 -666 183 -1276 83 -218 107 -1163 55 -1276 127 -1144 73 -1400 81 -266 77 -568 129 -806 121 -1420 103 -848 77 -982 103 -2132 81 -1610 101 -1218 55 -2208 75 -2735 53 -921 53 -724 51 -472 83 -3164 185 -400 77 -812 81 -306 215 -2167 53 -130 53 -272 81 -400 79 -1272 81 -418 51 -1381 73 -340 101 -2169 81 -2330 137 -2698 99 -2340 99 -126 51 -1714 55 -488 81 -3500 51 -404 77 -1422 77 -856 215 -80 51 -2308 53 -134 77 -2036 75 -5175 129 -946 239 -638 53 -244 55 -564 105 -826 71 -1632 77 -106 129 -246 135 -366 79 -724 79 -1535 57 -1085 113 -1320 79 -3111 127 -1578 75 -324 75 -102 173 -364 79 -1374 53 -1508 107 -622 51 -526 109 -584 187 -2648 51 -106 79 -380 103 -604 51 -1244 73 -5766 107 -1934 177 -702 51 -1277 53 -1643 79 -1446 81 -4098 75 -574 103 -432 189 -1436 107 -454 79 -132 105 -136 81 -112 113 -942 239 -1238 79 -952 157 -340 51 -314 191 -456 53 -3368 101 -150 99 -464 51 -718 73 -770 101 -150 73 -2132 75 -557 77 -680 81 -3512 151 -760 75 -332 75 -1212 131 -1468 79 -1955 101 -541 75 -344 79 -2146 53 -2299 97 -720 79 -2518 79 -3807 51 -1272 75 -352 77 -52 75 -586 53 -1142 79 -82 81 -2400 157 -324 81 -268 103 -1154 81 -1175 79 -1191 51 -1074 53 -2566 137 -854 75 -1497 51 -4533 51 -2290 51 -344 77 -348 55 -1182 77 -897 135 -874 51 -1064 51 -208 55 -140 55 -1334 133 -1238 157 -1669 113 -2128 75 -848 85 -510 83590 -126 333 -280 331 -252 331 -6946 331 -276 331 -276 329 -278 329 -276 331 -276 331 -276 331 -276 347 -238 351 -254 353 -268 323 -270 345 -6924 335 -282 307 -304 307 -304 281 -304 307 -302 307 -302 305 -302 307 -302 305 -302 281 -124 507 -282 305 -302 305 -6984 279 -328 277 -328 279 -328 277 -330 277 -304 303 -302 305 -302 305 -302 303 -304 303 -100 507 -314 295 -298 293 -6986 283 -334 281 -306 283 -328 283 -328 281 -328 283 -328 255 -352 RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/lib/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* 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_dooya_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); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* 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_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_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); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/lib/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index a1f1bc149..99de18c93 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -39,8 +39,9 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, }; const SubGhzProtocolRegistry subghz_protocol_registry = { .items = subghz_protocol_registry_items, - .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 185e47394..4d1551bb3 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -39,5 +39,6 @@ #include "ansonic.h" #include "smc5326.h" #include "holtek_ht12x.h" +#include "dooya.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From 31259d530499b2ff6e07fd24a5ec3b8583a41ac0 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 20:59:49 +0400 Subject: [PATCH 123/231] [FL-3091] SubGhz: add protocol Alutech at-4n (#2352) * GubGhz: add protocol Alutech at-4n * SubGhz: fix syntax * SubGhz: fix subghz_protocol_decoder_alutech_at_4n_get_hash_data * SubGhz: add unit test alutech at-4n * SubGhz: add name key Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 14 +- applications/main/subghz/subghz.c | 2 + applications/main/subghz/subghz_cli.c | 4 + assets/resources/subghz/assets/alutech_at_4n | 6 + .../unit_tests/subghz/alutech_at_4n_raw.sub | 10 + assets/unit_tests/subghz/test_random_raw.sub | 5 + firmware/targets/f7/api_symbols.csv | 2 + lib/subghz/environment.c | 16 + lib/subghz/environment.h | 17 + lib/subghz/protocols/alutech_at_4n.c | 455 ++++++++++++++++++ lib/subghz/protocols/alutech_at_4n.h | 74 +++ lib/subghz/protocols/came_atomo.c | 2 +- lib/subghz/protocols/keeloq.c | 13 +- lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + 15 files changed, 617 insertions(+), 5 deletions(-) create mode 100644 assets/resources/subghz/assets/alutech_at_4n create mode 100644 assets/unit_tests/subghz/alutech_at_4n_raw.sub create mode 100644 lib/subghz/protocols/alutech_at_4n.c create mode 100644 lib/subghz/protocols/alutech_at_4n.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 0dba19d90..705d6f2f6 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -12,8 +12,9 @@ #define KEYSTORE_DIR_NAME EXT_PATH("subghz/assets/keeloq_mfcodes") #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 ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 300 +#define TEST_RANDOM_COUNT_PARSE 304 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -43,6 +44,8 @@ static void subghz_test_init(void) { environment_handler, CAME_ATOMO_DIR_NAME); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment_handler, NICE_FLOR_S_DIR_NAME); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment_handler, ALUTECH_AT_4N_DIR_NAME); subghz_environment_set_protocol_registry( environment_handler, (void*)&subghz_protocol_registry); @@ -619,6 +622,14 @@ MU_TEST(subghz_decoder_dooya_test) { "Test decoder " SUBGHZ_PROTOCOL_DOOYA_NAME " error\r\n"); } +MU_TEST(subghz_decoder_alutech_at_4n_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/alutech_at_4n_raw.sub"), + SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME), + "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -817,6 +828,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_smc5326_test); MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_decoder_dooya_test); + MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index b7564ab57..200be6262 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -187,6 +187,8 @@ SubGhz* subghz_alloc() { subghz->txrx->environment = subghz_environment_alloc(); subghz_environment_set_came_atomo_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry( diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index 9271443a8..ac19d65b4 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -257,6 +257,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -452,6 +454,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); diff --git a/assets/resources/subghz/assets/alutech_at_4n b/assets/resources/subghz/assets/alutech_at_4n new file mode 100644 index 000000000..5d7beacec --- /dev/null +++ b/assets/resources/subghz/assets/alutech_at_4n @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz Keystore RAW File +Version: 0 +Encryption: 1 +IV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31 +Encrypt_data: RAW +E811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60 \ No newline at end of file diff --git a/assets/unit_tests/subghz/alutech_at_4n_raw.sub b/assets/unit_tests/subghz/alutech_at_4n_raw.sub new file mode 100644 index 000000000..ae5db9715 --- /dev/null +++ b/assets/unit_tests/subghz/alutech_at_4n_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index aee15a16d..44e1613c7 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -178,3 +178,8 @@ RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 65ab375ef..846041c3e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2569,12 +2569,14 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* +Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 0a4b7b606..b39b259d4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -6,6 +6,7 @@ struct SubGhzEnvironment { const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; }; SubGhzEnvironment* subghz_environment_alloc() { @@ -57,6 +58,21 @@ const char* return instance->came_atomo_rainbow_table_file_name; } +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + void subghz_environment_set_nice_flor_s_rainbow_table_file_name( SubGhzEnvironment* instance, const char* filename) { diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index e994f7c97..7bd38ba2f 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -52,6 +52,23 @@ void subghz_environment_set_came_atomo_rainbow_table_file_name( */ const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + /** * Set filename to work with Nice Flor-S. * @param instance Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/came_atomo.c b/lib/subghz/protocols/came_atomo.c index 3f6045bea..2e2718485 100644 --- a/lib/subghz/protocols/came_atomo.c +++ b/lib/subghz/protocols/came_atomo.c @@ -189,7 +189,7 @@ void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file - * @param number_atomo_magic_xor Сell number in the array + * @param number_atomo_magic_xor number in the array * @return atomo_magic_xor */ static uint64_t subghz_protocol_came_atomo_get_magic_xor_in_file( diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index 6a9c3468e..4a3602fbe 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -390,11 +390,14 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur subghz_protocol_keeloq_const.te_delta)) { // Found end TX instance->decoder.parser_step = KeeloqDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -411,6 +414,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( @@ -421,6 +426,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else { diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 99de18c93..3904c1811 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -40,6 +40,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 4d1551bb3..cd9b7c6ed 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -40,5 +40,6 @@ #include "smc5326.h" #include "holtek_ht12x.h" #include "dooya.h" +#include "alutech_at_4n.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From bf4d00a7d1cb94a2b17d26647359ab93207880c1 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:20:28 +0400 Subject: [PATCH 124/231] [FL-3100] SubGhz: add protocol Nice One (#2358) * SubGhz: add protocol Nice One * SubGhz: fix annotation * SubGhz: add unit test Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 10 +- assets/unit_tests/subghz/nice_one_raw.sub | 12 ++ assets/unit_tests/subghz/test_random_raw.sub | 7 + lib/subghz/protocols/nice_flor_s.c | 167 +++++++++++++++--- 4 files changed, 174 insertions(+), 22 deletions(-) create mode 100644 assets/unit_tests/subghz/nice_one_raw.sub diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 705d6f2f6..97629efea 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -14,7 +14,7 @@ #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 304 +#define TEST_RANDOM_COUNT_PARSE 317 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -630,6 +630,13 @@ MU_TEST(subghz_decoder_alutech_at_4n_test) { "Test decoder " SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME " error\r\n"); } +MU_TEST(subghz_decoder_nice_one_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/nice_one_raw.sub"), SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME), + "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -829,6 +836,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_holtek_ht12x_test); MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); + MU_RUN_TEST(subghz_decoder_nice_one_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/assets/unit_tests/subghz/nice_one_raw.sub b/assets/unit_tests/subghz/nice_one_raw.sub new file mode 100644 index 000000000..169b3f088 --- /dev/null +++ b/assets/unit_tests/subghz/nice_one_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 44e1613c7..12b08d48d 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -183,3 +183,10 @@ RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 80 RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 411ceeacf..dd5521a64 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -14,6 +14,9 @@ #define TAG "SubGhzProtocoNiceFlorS" +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { .te_short = 500, .te_long = 1000, @@ -28,6 +31,7 @@ struct SubGhzProtocolDecoderNiceFlorS { SubGhzBlockGeneric generic; const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; }; struct SubGhzProtocolEncoderNiceFlorS { @@ -77,6 +81,64 @@ const SubGhzProtocol subghz_protocol_nice_flor_s = { .encoder = &subghz_protocol_nice_flor_s_encoder, }; +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -237,10 +299,14 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -274,6 +340,11 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ } else { instance->decoder.parser_step = NiceFlorSDecoderStepReset; } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } break; } } @@ -287,6 +358,7 @@ static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name) { /* + * Protocol Nice Flor-S * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; * P1 (4-bit) - batch repetition number, calculated by the formula: @@ -307,6 +379,24 @@ static void subghz_protocol_nice_flor_s_remote_controller( * data => 0x1c5783607f7b3 key serial cnt * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * */ if(!file_name) { instance->cnt = 0; @@ -333,7 +423,15 @@ bool subghz_protocol_decoder_nice_flor_s_serialize( SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; } bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { @@ -344,11 +442,25 @@ bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperForma if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if(instance->generic.data_count_bit != - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + ret = true; } while(false); return ret; @@ -360,20 +472,33 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } } From 39841bd5a9ad1d6b1f4d222ab0670be96aadc732 Mon Sep 17 00:00:00 2001 From: itsweekly <92674764+itsweekly@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:28:34 +0100 Subject: [PATCH 125/231] Universal Projector Remote (#2343) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Georgii Surkov Co-authored-by: あく --- applications/main/infrared/infrared_cli.c | 2 +- .../infrared/scenes/infrared_scene_config.h | 1 + .../scenes/infrared_scene_universal.c | 12 +- .../infrared_scene_universal_projector.c | 86 ++ assets/resources/infrared/assets/projector.ir | 829 ++++++++++++++++++ documentation/UniversalRemotes.md | 7 + 6 files changed, 935 insertions(+), 2 deletions(-) create mode 100644 applications/main/infrared/scenes/infrared_scene_universal_projector.c create mode 100644 assets/resources/infrared/assets/projector.ir diff --git a/applications/main/infrared/infrared_cli.c b/applications/main/infrared/infrared_cli.c index 5f5e2d4bb..3fa99cb02 100644 --- a/applications/main/infrared/infrared_cli.c +++ b/applications/main/infrared/infrared_cli.c @@ -86,7 +86,7 @@ static void infrared_cli_print_usage(void) { printf("\tir universal \r\n"); printf("\tir universal list \r\n"); // TODO: Do not hardcode universal remote names - printf("\tAvailable universal remotes: tv audio ac\r\n"); + printf("\tAvailable universal remotes: tv audio ac projector\r\n"); } static void infrared_cli_start_ir_rx(Cli* cli, FuriString* args) { diff --git a/applications/main/infrared/scenes/infrared_scene_config.h b/applications/main/infrared/scenes/infrared_scene_config.h index 111fd2d31..27eabe225 100644 --- a/applications/main/infrared/scenes/infrared_scene_config.h +++ b/applications/main/infrared/scenes/infrared_scene_config.h @@ -17,6 +17,7 @@ ADD_SCENE(infrared, universal, Universal) ADD_SCENE(infrared, universal_tv, UniversalTV) ADD_SCENE(infrared, universal_ac, UniversalAC) ADD_SCENE(infrared, universal_audio, UniversalAudio) +ADD_SCENE(infrared, universal_projector, UniversalProjector) ADD_SCENE(infrared, debug, Debug) ADD_SCENE(infrared, error_databases, ErrorDatabases) ADD_SCENE(infrared, rpc, Rpc) diff --git a/applications/main/infrared/scenes/infrared_scene_universal.c b/applications/main/infrared/scenes/infrared_scene_universal.c index 5043c9bd7..4ef7c5c26 100644 --- a/applications/main/infrared/scenes/infrared_scene_universal.c +++ b/applications/main/infrared/scenes/infrared_scene_universal.c @@ -4,6 +4,7 @@ typedef enum { SubmenuIndexUniversalTV, SubmenuIndexUniversalAC, SubmenuIndexUniversalAudio, + SubmenuIndexUniversalProjector, } SubmenuIndex; static void infrared_scene_universal_submenu_callback(void* context, uint32_t index) { @@ -27,6 +28,12 @@ void infrared_scene_universal_on_enter(void* context) { SubmenuIndexUniversalAudio, infrared_scene_universal_submenu_callback, context); + submenu_add_item( + submenu, + "Projectors", + SubmenuIndexUniversalProjector, + infrared_scene_universal_submenu_callback, + context); submenu_add_item( submenu, "Air Conditioners", @@ -54,6 +61,9 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { } else if(event.event == SubmenuIndexUniversalAudio) { scene_manager_next_scene(scene_manager, InfraredSceneUniversalAudio); consumed = true; + } else if(event.event == SubmenuIndexUniversalProjector) { + scene_manager_next_scene(scene_manager, InfraredSceneUniversalProjector); + consumed = true; } scene_manager_set_scene_state(scene_manager, InfraredSceneUniversal, event.event); } @@ -64,4 +74,4 @@ bool infrared_scene_universal_on_event(void* context, SceneManagerEvent event) { void infrared_scene_universal_on_exit(void* context) { Infrared* infrared = context; submenu_reset(infrared->submenu); -} +} \ No newline at end of file diff --git a/applications/main/infrared/scenes/infrared_scene_universal_projector.c b/applications/main/infrared/scenes/infrared_scene_universal_projector.c new file mode 100644 index 000000000..c1df91c34 --- /dev/null +++ b/applications/main/infrared/scenes/infrared_scene_universal_projector.c @@ -0,0 +1,86 @@ +#include "../infrared_i.h" + +#include "common/infrared_scene_universal_common.h" + +void infrared_scene_universal_projector_on_enter(void* context) { + infrared_scene_universal_common_on_enter(context); + + Infrared* infrared = context; + ButtonPanel* button_panel = infrared->button_panel; + InfraredBruteForce* brute_force = infrared->brute_force; + + infrared_brute_force_set_db_filename(brute_force, EXT_PATH("infrared/assets/projector.ir")); + + button_panel_reserve(button_panel, 2, 2); + uint32_t i = 0; + button_panel_add_item( + button_panel, + i, + 0, + 0, + 3, + 19, + &I_Power_25x27, + &I_Power_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Power"); + button_panel_add_item( + button_panel, + i, + 1, + 0, + 36, + 19, + &I_Mute_25x27, + &I_Mute_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Mute"); + button_panel_add_item( + button_panel, + i, + 0, + 1, + 3, + 66, + &I_Vol_up_25x27, + &I_Vol_up_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_up"); + button_panel_add_item( + button_panel, + i, + 1, + 1, + 36, + 66, + &I_Vol_down_25x27, + &I_Vol_down_hvr_25x27, + infrared_scene_universal_common_item_callback, + context); + infrared_brute_force_add_record(brute_force, i++, "Vol_dn"); + + button_panel_add_label(button_panel, 2, 11, FontPrimary, "Proj. remote"); + button_panel_add_label(button_panel, 17, 62, FontSecondary, "Volume"); + + view_set_orientation(view_stack_get_view(infrared->view_stack), ViewOrientationVertical); + view_dispatcher_switch_to_view(infrared->view_dispatcher, InfraredViewStack); + + infrared_show_loading_popup(infrared, true); + bool success = infrared_brute_force_calculate_messages(brute_force); + infrared_show_loading_popup(infrared, false); + + if(!success) { + scene_manager_next_scene(infrared->scene_manager, InfraredSceneErrorDatabases); + } +} + +bool infrared_scene_universal_projector_on_event(void* context, SceneManagerEvent event) { + return infrared_scene_universal_common_on_event(context, event); +} + +void infrared_scene_universal_projector_on_exit(void* context) { + infrared_scene_universal_common_on_exit(context); +} diff --git a/assets/resources/infrared/assets/projector.ir b/assets/resources/infrared/assets/projector.ir new file mode 100644 index 000000000..e9861de21 --- /dev/null +++ b/assets/resources/infrared/assets/projector.ir @@ -0,0 +1,829 @@ +Filetype: IR library file +Version: 1 +# +# Model: Smart +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8A 00 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +# Model: Hitatchi +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 +# +name: Vol_up +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 49 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 44 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 83 7C 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 82 7D 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 13 00 00 +command: 87 78 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 +# +name: Mute +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 29 D6 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 08 F7 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 04 FB 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 93 6C 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 1D 00 00 00 +# +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 +# AV-Mute +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 4C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 529 7218 126 6585 219 703 180 5362 427 18618 177 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620 +# ON +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 46 B9 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0E F1 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0D F2 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 08 16 00 00 +command: C8 37 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 28 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 29 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1D E2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 99 66 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 98 67 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 52 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 51 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 56 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 5A 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 82 7D 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 83 7C 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 91 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 31 00 00 00 +command: D0 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 89 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 30 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 31 00 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 32 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595 +# +name: Mute +type: raw +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_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 14 00 00 00 +# OFF +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 1D 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 15 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: Vol_up +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: Vol_dn +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 18 E9 00 00 +command: 49 B6 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 14 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 40 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1F E0 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 81 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8F 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: A8 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 88 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 9C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 50 AF 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635 +# +name: Power +type: parsed +protocol: NECext +address: FF FF 00 00 +command: E8 17 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: FF FF 00 00 +command: BD 42 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: FF FF 00 00 +command: F2 0D 00 00 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +# +name: Vol_dn +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 71 01 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 81 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 \ No newline at end of file diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md index 264829e16..325f640d7 100644 --- a/documentation/UniversalRemotes.md +++ b/documentation/UniversalRemotes.md @@ -25,6 +25,13 @@ Make sure that every signal does what it's supposed to. If everything checks out, append these signals **to the end** of the [audio player universal remote file](/assets/resources/infrared/assets/audio.ir). +## Projectors + +Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. +To save time, please make sure every recording has been named accordingly. +In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. + + ## Air conditioners Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. From db1a8f8014ebff5243ac624d0c7c62614e3fb428 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Wed, 8 Feb 2023 21:47:39 +0400 Subject: [PATCH 126/231] [FL-3099] SubGhz: add protocol KingGates Stylo4k (#2368) * [FL-3099] SubGhz: add protocol KingGates Stylo4k * SubGhz: add unit test file * f7: api: reverted symbols Co-authored-by: hedger Co-authored-by: Aleksandr Kutuzov --- .../debug/unit_tests/subghz/subghz_test.c | 11 +- assets/resources/subghz/assets/keeloq_mfcodes | 105 +++--- .../subghz/kinggates_stylo4k_raw.sub | 11 + assets/unit_tests/subghz/test_random_raw.sub | 6 + lib/subghz/protocols/kinggates_stylo_4k.c | 336 ++++++++++++++++++ lib/subghz/protocols/kinggates_stylo_4k.h | 74 ++++ lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + 8 files changed, 492 insertions(+), 53 deletions(-) create mode 100644 assets/unit_tests/subghz/kinggates_stylo4k_raw.sub create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.c create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.h diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c index 97629efea..c7e9c96f1 100644 --- a/applications/debug/unit_tests/subghz/subghz_test.c +++ b/applications/debug/unit_tests/subghz/subghz_test.c @@ -14,7 +14,7 @@ #define NICE_FLOR_S_DIR_NAME EXT_PATH("subghz/assets/nice_flor_s") #define ALUTECH_AT_4N_DIR_NAME EXT_PATH("subghz/assets/alutech_at_4n") #define TEST_RANDOM_DIR_NAME EXT_PATH("unit_tests/subghz/test_random_raw.sub") -#define TEST_RANDOM_COUNT_PARSE 317 +#define TEST_RANDOM_COUNT_PARSE 329 #define TEST_TIMEOUT 10000 static SubGhzEnvironment* environment_handler; @@ -637,6 +637,14 @@ MU_TEST(subghz_decoder_nice_one_test) { "Test decoder " SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME " error\r\n"); } +MU_TEST(subghz_decoder_kinggates_stylo4k_test) { + mu_assert( + subghz_decoder_test( + EXT_PATH("unit_tests/subghz/kinggates_stylo4k_raw.sub"), + SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME), + "Test decoder " SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME " error\r\n"); +} + //test encoders MU_TEST(subghz_encoder_princeton_test) { mu_assert( @@ -837,6 +845,7 @@ MU_TEST_SUITE(subghz) { MU_RUN_TEST(subghz_decoder_dooya_test); MU_RUN_TEST(subghz_decoder_alutech_at_4n_test); MU_RUN_TEST(subghz_decoder_nice_one_test); + MU_RUN_TEST(subghz_decoder_kinggates_stylo4k_test); MU_RUN_TEST(subghz_encoder_princeton_test); MU_RUN_TEST(subghz_encoder_came_test); diff --git a/assets/resources/subghz/assets/keeloq_mfcodes b/assets/resources/subghz/assets/keeloq_mfcodes index 1b27bfb01..3eedc564f 100644 --- a/assets/resources/subghz/assets/keeloq_mfcodes +++ b/assets/resources/subghz/assets/keeloq_mfcodes @@ -1,55 +1,56 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: AA FF DE 54 A1 BB F1 21 83 46 FE 2A 1E B7 3D 33 -95B8CD65BBAC95EACE67CA94F679B82877A921396D461ECB479722F8A369454A -61065C41297B9FF8F8168814F49A03D1FE7B4CB79DFFCBBF0402AAA6A2211E84 -A1557AC139188FF105D1081A4B688C5CA440FB5DA7F40901B541120AD08A544F -AF0A6056D7F0D97DAD6C16C4E63204E4B3B1C5A20AC82B983B516F4F718EE29F -6861BFAE46A1AADB1DB2D6DFAA7E39D21D5B3E46A41BD50F4F2828879EB328EF0A406F2B9C79A031AB361257E6D69756 -0DDB3DAC53678541981CC46C22CED245CBA314C9BBE1BA9383B8505B75AC5E40 -99AB5D9404934F2D257ED04D9F8CCEE06D00F38157B121AFD63101E4E5C08268 -5114A6C42B342C7D933A76F9052FF963C2047E85EA524497C21B4C35C38EF6E7 -88CA2A1907D94B972FF93DBB9B88CB576F3E1BB0FE8F85A5B2CCA7D44B00374D -349C4153FE7CA8AE044E9F75F77D9694304474CE3F127CF968662B5F78A7F421 -62AA02E20CA7E691EFC0B55CA41C9BDF889FB23868289284241CD31AA1A0E499AE2A770B6B5AB3170CDCCDB8A246D36C -97901B5EB76228ADF8E5073F1BAB1502878DEFF1C4EBF12A43D105556CB7E80F947A8BD7831666BD838C57CDF64A6F3F -B05959D210B500943A93BDFAF783D9DB215FC84503B152EAFBCFB5B6237E3888 -B393DE4489BCAFD5DB80592A12E329E18913E185D2042580048029A8C4C3A257 -B4B30492A5F0C3C763E2F43C02D1451A5B9CFB468CFE62BE85B1F56FF49DAB9A -CE5D57C0EE3D717FC717EB725970A9F25D211546EE7AC5C237950CEA323D85D4 -4E9028944813FD40A17AF6DF5A97E76179B48EE79265BBD38B07E3A270587A813DADB51B3367479AC5644F754B5613F8 -3B3C3000B9D1361711ECE3DB77C90A059576F738CB167679DA36DD3D128B27A1 -997023148148DE7B9CBA47D3FD48DEF73AA1715FF4BC1E7A1DBA6D52A0DCB2C0 -C8428D18E69FB92486434FCE470F1FF37D40507F27D824679C132A70D516530367277F02DDB5C464D03450FF6B425A24 -3701200DF5DA7235971FD95844056E74C7D61A8EB12A8772E04F52037C63D50B6229A7F905F3E6F84C565FCC7632870C -BB392A464CDC0D5D923AA9EF8ECC3C6F020D0AD82165462DF0DE7C5025AAAAAC -999C82209B30638506E5D708471676D2CBB4A432E5AF86ABD61179111EDAE636 -FDE2A452A6B47261338117EC20FC57731DA492562ECD21BBC61F098A5442CF20 -D923BABB5C4DFB48E3F763898B2796C7830D3EE9A91DF904AC2223A0F4736507 -0987DDAC695DD5E4607048DF1D4EF96599E17ED52F41785E676AA048AB7213FE -26CB3E6CFA10338A8DDD99BFF6957C53DEF435CB0FF977B71B5164ADFE11292A -097908FD07A0A093CA80E6FF59524707C1A11169D0CB6F8E4967D8DAA725FE7A -8C629E70A5CC6FCB039DFA1A6AC58CB7B7E92C85BDA66266AB49E6B1285FC7A6 -39A2052350CD446EDC1B9AD0C2DD51C78B2E5F3A76AAD0EC200F74B40ACD4AC5 -A1685CF8C4A5401F2CA0C8172CB5B4B5726C61CE68A72AE834B0A472CEB2F3DE -1F5ED5793DB381D1B501BA8A4DF3E74FB11FC1A922DDC8AE62E5BA8934C37EA8 -D80EF661BF36E2F6C179E253CE5BC3732684ACBC7C65E526A628442A2EBF8FAA -7785BF721F21E19A8CFBFBBB56BD76B96A4E8EF9F8A2344009B14AB385909598F834A5533B648DA7D62BD6D4314A43A5 -C8F6F943DE615B5827569B283577344C0455B3279C73634FC4E0E9A8088DF633 -FB4F4C786FC51BBDA679A212B4A05EF120AC62F7EBFFE8263BD50A4D9BC9C6E0 -16EEC35CF69BA86DB3BE999CDF9B39F5736F3727B2AA2C5AB9141A48F176D831 -AD1AD6DE813E7710DA3AF546D4F9EA085831E6B3FA17B64F1B8765F48134EA54 -345D743BC35B4A8614632ADD11E809C0D1E6C78F9469256B9A738DA0B648B2B8 -7C876CECAC839EBB4609C3996966C3EC454F51C8ABCC51097E405370C4B6F086 -0F857C031FD3047607647148C534F969567F207FF1691D8D06DCDF4C2514695D -EC0630EDC82241C1952F49B6B1B0C1A954A7DDD6BDB1326ACC54AD449D1BF985 -286EF9F7FD0D09F2604CCE867C52144CD0C4773A3D8183066C61B8BF9860AE7C -EA55424097A08722A66966E3177E09DE91AC65175E5C68CB47B6153E6585DF85 -D54FCDF9EA4BD1FE4F316DB6D5CE4A2675F2D0144772865EDC781FBA7DFD23E4 -7A2F5C5CA9F97FE9527BAA760E64B930C407A27DE036476737E6BDD9422F4056A5F1F414F12F0982109FD7C30E8CC1CB -06BAD9B4EEEEB1BCF8C97672D271534FE84D772282EE9642698788D3842D7641 -101C1B2DBD963E23777294C22E553D145D5B40838F91355CA86D571A0CEFF68F -1B148C2B502B3E0A5BD40858E019C513DD4CCAF2A114CBB29C59BFB018079285 -8DF4D07EC20FF873EA989ACEF4AF96E9787FE6E0F71965858B4186C3AF302A31 -2317DC8C098CD60F3467B3644A19CCE887339708820CD37F6F5277D6648F837512F70CE90E23D7339CDDE002BD8D83DB +IV: 41 84 34 43 84 1D 43 04 45 44 34 38 41 24 3E 74 +8C5AEF725F0620DB3952B40BB8E76A815BCEE1D1B92F7E8E8E63D1894F1C7FD0 +1DFF1D6A322D6B3D8AD7C594A02462AADE723D417B9233585526982F08187DAA +0A9184F15D4A5589DDDA6422063BACD58580661CFE60EE600D87F73F0CB5013E +6E56802DAA049C3DFDEDC90432A0E694A172C369EBECD136F4C911B979AA098D +A659716B51053604059F7FC3651D6A153F5EAB1852F95B20C44C41A7889A0DE91A078B63E3C311280C4315F0A3C8BA1F +A315170EDC51627157725D9A96490DB75EBF8232957FBA313C03B2BA2884EA85 +DEAB3C2C2E2DC76FE45AEBAC7EBFB478CECCD970A63B8DE2024FBFDCCBD1B26E +7BBFC36CBA77468B4624C6B685610877D53985C985DAD8EFE47527EB7C7260CD +879EE18B314ED4F3F548B41176099176FB97F4F1A062481C935B2DDFBCE2FE4D +493372D7D47A96A66305DFDC8A915EB651620881AE1D603B7E9605E004C04CA9 +F80AAA4C447F8E8C0B039DDAECF9126119C32FF164118780BE268E326A8CBF8010DE2EBF94033CEAC39815D6A8958CF4 +41C1393A039E665F6A53A5E5D1C105008BD14D9755751545A667860C39B2E39AA47306E76E2BA7DDDAA2A286FDB58D23 +34853A4CDE42CB80045E641AB4800C86C1CF6907EAAFA399156CCC727A008584 +D0783A34BD6A36D31BFF5F70FA1116CAE48EF02716D80481AE406DABB3C3400E +0BB3582605434CF2A5D74A27773B88DA331B6033C444837E9F7A155897258B03 +E4E71F3EB290B9436FFF0FDADA468BE37D964E89BE8D9971A14776F821822769 +744AA59D129C892120B5DAB81C8A3D573B0AD80EF0708C1B9ECF13DA60CECA07DC0591A08611DB4D3A8B7C70994D5DEF +716F9F8D5D2C697BC4183EFCC97E52D08ECA07B613A0F389C801F65803DFF4A4 +560262DA8489D2C18C8D97E314DC663403AFE4DE9DCB6D453087D2BFBD36532D +9E31F7152C50B6940EE3E3894C98F75702C7897F702B48E5C9B54B6E25083641AD2E521267505066C7E5BAB7F6CF1433 +6630EDA18A6E58BD395792CCC656DD10CD9C5DD2B1949FE677122FA39A53C724E79C0D0752A3A39A03407BBA2282185E +00D15A06F5DD82A4B926B78809CC4D129AAFA9A92B0A81568F255D15697FE0FD +29FF9A4F5346ABEE8FEDE034988F87FCD29EA747735898F1E7207EF74FAB71A8 +C0E8EB6AE6F77EE38DF2AB1B7742E34ED5236F3D8E964845E66762A4675AA21F +00FC4C459DC4CE92B62D0AC2546F9FBBE0893F84D2AF0A20ED462A5EAE63DE3B +E92EF482A40CEEFC8339BBB713BBC452A266A09B2645EDEB12716544B2DB9B09 +D7D9C5C757831BCE2FF1DB25A080D77769FB36A1F3F48F4361418A0A45609280 +C19246F52AE1EE5CE968CED52F642D9CD78B020029632FE83C49C657D23ED075 +FEE3C05432FB3860D5D28562323F5D1B053B8F3ADCD416BD0C4645F6F4D43DCF +D780A4AADD0205E0BACDCC9AF46ED259E0946C5DA888C341BFE96E09A87CCCFA +CE3C13CFA08E532B637FDB707E29548D57EE92EAEF6516C3D67E9D36FCD59CF9 +5E88CE71258CB0D91631FEB41C9A2F47AE0FF4810A9A1EDF3F308BBDE6944D5E +1531F4107FC64810BA5DB5E46C7B9AD61531AF5430E137B7688109FBC06B6221 +68050A39C0B302E0B713FAAC5F829C79AB30E18B1D982A94005DBAC7CCFB95379A619C0B9F7409C44D19FF2C5E8E4546 +3F73E8BA22C602280496EF8E88E2CAA9EC442E3B3083B684942DBF9CB5121241 +FA1FCD7C9182FAE8FFF4E88433AE68F66076B3BDFF8AD0BF5CEA43870082E9BE +DFF7DD2678C03401656B093BF7AC7E033F15FD0F30188E48A62045740B423699 +371BCFF653E7811D99C048A1A39921AAA563E06AC86CB3D2F392C2C955A1ABD0 +F4F1766DEAEDE934478208B9EB3050326D9FFCC001C73EEE93407D8B12CD49E4 +A241C9FC62DDF67D645936245FAFFE2A42C86151F484B7BCE5410E8F36FC87901D3AC4E40334E08FFFC2AD676E490D94 +3566A94A9C0479E0C4387D9137375ADF2C921504364F3903F198D6757CDFD21B +7274E1B5A6445FDC29C355D550E981C17F349BC4A14251B3B51BC96FC334FBCA +04EEA5EDD9B3BC3E0638E53A5561DC8BF761D615A64D435BD31A94AF2650159E +B84818CC1695FE8B731CD653D0679D1AAA0578C0B06AD1E3510785B2DE20841C +4121343D6B79E38C06DD038D770D76D10336AFF47ED0D0DCDDD6B0FEA4DAE67C +75E49C839CCD7019D9CE90AC364F488468B2AB01E387A8BEF8815915925166A6 +CFAA9F4717568C1EC7B96E0D71D260B828A70484E1D9CA7C99A50D10704F8BBBAE62EE98C9FBDFF06F357F1C1E2F2677 +41E4D250B92BC57442B91DE2015C41226531CF9A8D77B83AFC8E4F3183DB11DE +45EA8BD854D7F044FB249C16F08A0C24FF117D54BC20A4CC667B3DAD09EAC4F9 +F455CA0BB8B496C301406DE4FB52C9B0F64645776803BC2935A2F38675318BE2 +22FF72A5D2E1A2EBFB6C55FFD0A3CEA0474CCBD13462D63229C9708276E87D3F +8470F9A300170F226C0216C07AA829591CBD4CE34AA918EAE49363BDE86CC77EEEBEEA84A097488D35B92F773F5DBB4C diff --git a/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub new file mode 100644 index 000000000..49b190002 --- /dev/null +++ b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/assets/unit_tests/subghz/test_random_raw.sub b/assets/unit_tests/subghz/test_random_raw.sub index 12b08d48d..949a44458 100644 --- a/assets/unit_tests/subghz/test_random_raw.sub +++ b/assets/unit_tests/subghz/test_random_raw.sub @@ -190,3 +190,9 @@ RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 4 RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..2c8de0d2d --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,336 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->data; + instance->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 = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + uint64_t data, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->data, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->data, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..c9f1cf380 --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 3904c1811..050904eec 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -41,6 +41,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_linear_delta3, &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index cd9b7c6ed..522931d22 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -41,5 +41,6 @@ #include "holtek_ht12x.h" #include "dooya.h" #include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; From 8f2f2d810ab4112605adee7f605613ff3c151f10 Mon Sep 17 00:00:00 2001 From: Eric Betts Date: Wed, 8 Feb 2023 10:06:42 -0800 Subject: [PATCH 127/231] Move CSN space to revent overflow (#2232) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: あく --- .../plugins/picopass/scenes/picopass_scene_read_card_success.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index bb170ac45..0d1cc78c3 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -33,7 +33,7 @@ void picopass_scene_read_card_success_on_enter(void* context) { uint8_t csn[PICOPASS_BLOCK_LEN]; memcpy(csn, &AA1->data[PICOPASS_CSN_BLOCK_INDEX], PICOPASS_BLOCK_LEN); for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { - furi_string_cat_printf(csn_str, " %02X", csn[i]); + furi_string_cat_printf(csn_str, "%02X ", csn[i]); } // Neither of these are valid. Indicates the block was all 0x00 or all 0xff From a00508763652a424049b337b26386adc2ad4248d Mon Sep 17 00:00:00 2001 From: hedger Date: Wed, 8 Feb 2023 22:16:05 +0400 Subject: [PATCH 128/231] fbt: building fap_dist for compact gh build; accessor: fixed for latest ibutton changes (#2377) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fbt: building fap_dist as default target; accessor: fixed for latest ibutton changes * fbt: not building fap_dist as default target; github: doing fap_dist for compact builds Co-authored-by: あく --- .github/workflows/build.yml | 2 +- applications/debug/accessor/accessor_app.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a6c9219c2..be9817684 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -188,5 +188,5 @@ jobs: set -e for TARGET in ${TARGETS}; do TARGET="$(echo "${TARGET}" | sed 's/f//')"; \ - ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 updater_package + ./fbt TARGET_HW=$TARGET DEBUG=0 COMPACT=1 fap_dist updater_package done diff --git a/applications/debug/accessor/accessor_app.cpp b/applications/debug/accessor/accessor_app.cpp index 9d3708ebe..337437d0e 100644 --- a/applications/debug/accessor/accessor_app.cpp +++ b/applications/debug/accessor/accessor_app.cpp @@ -34,7 +34,7 @@ void AccessorApp::run(void) { AccessorApp::AccessorApp() : text_store{0} { notification = static_cast(furi_record_open(RECORD_NOTIFICATION)); - onewire_host = onewire_host_alloc(); + onewire_host = onewire_host_alloc(&ibutton_gpio); furi_hal_power_enable_otg(); } From 892adcc6956cf929457e42c6a2a5a167ff78b0af Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:46:33 -0500 Subject: [PATCH 129/231] added pager modulation, removed duplicate modulations, and cleaned up duplicate setting file --- applications/main/subghz/subghz.c | 27 +++++++---- applications/main/unirfremix/unirfremix_app.c | 2 +- applications/plugins/protoview/app.c | 2 +- assets/resources/subghz/assets/setting_user | 45 ------------------- .../resources/subghz/assets/setting_user.txt | 21 --------- 5 files changed, 21 insertions(+), 76 deletions(-) delete mode 100644 assets/resources/subghz/assets/setting_user diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index c39c35679..d95133da7 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -183,7 +183,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { //init setting subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user.txt")); // Custom Presets load without using config file @@ -208,26 +208,37 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset2); - // # HND - FM presets + // Pagers FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); flipper_format_write_string_cstr( - temp_fm_preset3, + temp_fm_preset2, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3); flipper_format_free(temp_fm_preset3); + // # HND - FM presets FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_1", temp_fm_preset4); - flipper_format_free(temp_fm_preset4); + flipper_format_free(temp_fm_preset3); + + FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset5, + (const char*)"Custom_preset_data", + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + flipper_format_rewind(temp_fm_preset5); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_2", temp_fm_preset5); + + flipper_format_free(temp_fm_preset5); // custom presets loading - end diff --git a/applications/main/unirfremix/unirfremix_app.c b/applications/main/unirfremix/unirfremix_app.c index 2c0b68ae7..2b12a12b2 100644 --- a/applications/main/unirfremix/unirfremix_app.c +++ b/applications/main/unirfremix/unirfremix_app.c @@ -706,7 +706,7 @@ static void input_callback(InputEvent* input_event, void* ctx) { void unirfremix_subghz_alloc(UniRFRemix* app) { // load subghz presets app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); // load mfcodes app->environment = subghz_environment_alloc(); diff --git a/applications/plugins/protoview/app.c b/applications/plugins/protoview/app.c index d060e2242..4765da9e3 100644 --- a/applications/plugins/protoview/app.c +++ b/applications/plugins/protoview/app.c @@ -143,7 +143,7 @@ ProtoViewApp* protoview_app_alloc() { //init setting app->setting = subghz_setting_alloc(); - subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user")); + subghz_setting_load(app->setting, EXT_PATH("subghz/assets/setting_user.txt")); // GUI app->gui = furi_record_open(RECORD_GUI); diff --git a/assets/resources/subghz/assets/setting_user b/assets/resources/subghz/assets/setting_user deleted file mode 100644 index d10392341..000000000 --- a/assets/resources/subghz/assets/setting_user +++ /dev/null @@ -1,45 +0,0 @@ -# to use manual settings and prevent them from being deleted on upgrade, rename *_user.example files to *_user -Filetype: Flipper SubGhz Setting File -Version: 1 - -# Add Standard frequencies for your region -Add_standard_frequencies: true - -# Default Frequency: used as default for "Read" and "Read Raw" -Default_frequency: 433920000 - -# Frequencies used for "Read", "Read Raw" and "Frequency Analyzer" -Frequency: 300000000 -Frequency: 310000000 -Frequency: 320000000 - -# Frequencies used for hopping mode (keep this list small or flipper will miss signal) -Hopper_frequency: 300000000 -Hopper_frequency: 310000000 -Hopper_frequency: 310000000 - -# Custom preset -# format for CC1101 "Custom_preset_data:" XX YY XX YY .. 00 00 ZZ ZZ ZZ ZZ ZZ ZZ ZZ ZZ, where: XX-register, YY - register data, 00 00 - end load register, ZZ - 8 byte Pa table register - -Custom_preset_name: AM_1 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -Custom_preset_name: AM_2 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -Custom_preset_name: FM95 -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 83 10 67 15 24 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -# Honda Presets -Custom_preset_name: Honda1 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -Custom_preset_name: Honda2 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 \ No newline at end of file diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt index 06a699a6c..173511f40 100644 --- a/assets/resources/subghz/assets/setting_user.txt +++ b/assets/resources/subghz/assets/setting_user.txt @@ -78,24 +78,3 @@ Hopper_frequency: 868350000 #Custom_preset_name: AM_2 #Custom_preset_module: CC1101 #Custom_preset_data: 02 0D 03 07 08 32 0B 06 14 00 13 00 12 30 11 32 10 17 18 18 19 18 1D 91 1C 00 1B 07 20 FB 22 11 21 B6 00 00 00 C0 00 00 00 00 00 00 - -# Custom presets added in Unleashed FW -# -- Some presets from forum.flipperzero.one -- - -#2-FSK 200khz BW / 135kHz Filter/ 15.86Khz Deviation + Ramping -Custom_preset_name: FM15k -Custom_preset_module: CC1101 -Custom_preset_data: 02 0D 03 47 08 32 0B 06 15 32 14 00 13 00 12 00 11 32 10 A7 18 18 19 1D 1D 92 1C 00 1B 04 20 FB 22 17 21 B6 00 00 00 12 0E 34 60 C5 C1 C0 - -# -- Other presets -- - -# Honda Presets -Custom_preset_name: Honda1 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 - -Custom_preset_name: Honda2 -Custom_preset_module: CC1101 -# G2 G3 G4 D L0 L1 L2 -Custom_preset_data: 02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00 From 65ed2ed27756ac051ddb07e9b3d3092d6b895ac3 Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:54:06 -0500 Subject: [PATCH 130/231] added pager bruteforce for lrs pagers thanks jimi! --- .../subghz/Misc/Pager_Bruteforce.sub | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 assets/resources/subghz/Misc/Pager_Bruteforce.sub diff --git a/assets/resources/subghz/Misc/Pager_Bruteforce.sub b/assets/resources/subghz/Misc/Pager_Bruteforce.sub new file mode 100644 index 000000000..27e8a5430 --- /dev/null +++ b/assets/resources/subghz/Misc/Pager_Bruteforce.sub @@ -0,0 +1,108 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 467750000 +Preset: FuriHalSubGhzPresetCustom +Custom_preset_module: CC1101 +Custom_preset_data: 02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00 +Protocol: RAW +RAW_Data: 2950 -8252 551 -184 549 -7528 1285 -554 369 -187658 219 -138090 28885 -1206 4003 -2610 3601 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3004 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3393 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 3389 -3002 1801 -1400 3401 -3008 3397 -2994 3401 -1398 1791 -4422 621 -2182 1689 -2756 3511 -3098 3253 -3022 3413 -3010 3403 -3002 3407 -3004 3399 -3006 3393 -2994 3401 -3002 3395 -3002 3393 -3002 3395 -3002 3405 -1400 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3407 -3004 3393 -1398 1791 -3000 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 3397 -3006 1805 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -2998 1805 -1396 3391 -3002 3393 -1402 1799 -3002 1797 -1398 3801 -2190 219 -1768 1767 -2596 3795 -2728 3493 -2872 3443 -3022 3417 -3002 3407 -3006 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1795 -1398 1801 -1400 3407 -3006 3393 -1398 1791 -2998 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 3395 -3002 1795 -1398 3401 -3002 3403 -1396 1791 -3000 3401 -7590 1961 -2458 3749 -2762 3575 -2990 3395 -2998 3393 -2994 3407 -2998 3393 -2994 3407 -2998 3397 -2998 3393 -3002 3395 -3002 3407 -2996 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3393 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -2994 1799 -1402 3407 -2996 3395 -1400 1795 -1394 1797 -3002 3603 -200 1143 -188 1487 -1226 1827 -2996 3419 -3010 3401 -3002 3401 -3004 3401 -2994 3401 -3002 3399 -3006 3393 -2994 3401 -2996 3401 -3002 3399 -2996 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -2992 3401 -3002 1801 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -3002 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 3407 -2996 1797 -1394 3397 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -6446 2633 -2932 3363 -3180 +RAW_Data: 3189 -3190 3395 -2994 3393 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -3002 3395 -3002 3405 -2998 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3393 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -3002 3399 -3004 1795 -1396 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -2996 1799 -1402 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1799 -1402 4405 -2974 1981 -3218 3193 -3160 3241 -3164 3313 -3118 3309 -3084 3243 -3026 3413 -3002 3407 -3010 3409 -3002 3399 -2996 3391 -2994 3401 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -2996 3401 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -2998 1795 -1394 3389 -1402 1801 -3002 1799 -1402 1801 -1400 3401 -3596 385 -560 373 -2330 8379 -2512 3729 -2750 3565 -2984 3385 -2990 3395 -3002 3393 -3002 3393 -3004 3401 -2998 3405 -2994 3395 -2994 3401 -3002 3403 -1400 1795 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1799 -1402 1801 -1396 1791 -1398 3407 -3006 3393 -1402 1795 -2998 3403 -2994 1801 -1400 1801 -1396 1797 -1400 1801 -1402 3401 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 1799 -1402 3407 -1400 1789 -2990 1799 -1402 3401 -3002 3609 -3274 2973 -2884 3537 -2960 3375 -2986 3389 -2996 3393 -2994 3393 -3002 3403 -3002 3401 -2994 3407 -2998 3393 -2994 3401 -3004 3393 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -1400 1795 -2992 3401 -3002 1801 -1402 1795 -1398 1801 -1400 3401 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 1797 -1398 3401 -1396 1797 -3002 1801 -1400 3403 -1396 1791 -3800 205 -3344 203 -612 2395 -2560 3555 -2896 3655 -2830 3419 -3014 3407 -3004 3393 -3004 3401 -2994 3401 -2994 3395 -3002 3401 -3002 3395 -3002 3401 -2998 3397 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1400 1801 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 3407 -3006 1795 -1394 3389 -3002 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 3407 -2998 1795 -1394 3397 -1402 1799 -3002 3395 -3002 1795 -1398 4003 -4020 1655 -3022 3323 -3148 3163 -3176 3385 -2990 3393 -2996 3401 -3002 3401 -2994 +RAW_Data: 3395 -3002 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1799 -3004 3397 -3006 1795 -1394 1791 -1400 1799 -1402 3401 -3002 3401 -2996 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 3401 -3002 1801 -1400 3407 -1400 1789 -2990 3407 -3004 3395 -8162 1623 -2776 3591 -2998 3389 -3000 3397 -2994 3393 -3002 3395 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1799 -1398 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 1795 -1394 1797 -1402 3405 -3006 3393 -1402 1799 -2996 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -3002 1799 -1398 3389 -1402 1801 -3002 3401 -1396 1793 -2998 3403 -1030 647 -3066 1817 -2644 3739 -2768 3575 -2986 3389 -2994 3395 -3002 3393 -3002 3399 -3006 3393 -2994 3401 -3004 3393 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3407 -3004 3399 -1404 1793 -2986 3401 -3002 1797 -1394 1795 -1402 1801 -1400 3407 -1404 1795 -2992 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 +RAW_Data: 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3407 -3004 1795 -1394 3391 -1404 1801 -2994 3403 -1400 1795 -1394 1797 -6646 2743 -2920 3547 -2968 3181 -3190 3391 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3006 1799 -1394 1791 -1398 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3002 1795 -1398 1801 -1400 1801 -1400 3407 -1400 1791 -2996 3405 -2994 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 1799 -1402 3401 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 3595 -1062 1325 -884 2861 -2628 3677 -2784 3481 -2744 3683 -2912 3513 -2916 3333 -3156 3175 -3182 3191 -3190 3393 -2994 3403 -2994 3401 -3002 3393 -3004 3393 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3405 -2998 3393 -1402 1795 -2992 3401 -3002 1801 -1400 1795 -1398 1801 -1402 3401 -1396 1793 -1398 1799 -3004 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 3401 -3002 1801 -1396 3389 -1402 1801 -1400 1801 -3002 1801 -1396 3391 -6068 219 -1318 1767 -2830 3479 -2936 3489 -2910 3509 -2922 3341 -3162 3175 -3186 3391 -2994 3393 -2998 3407 -2994 3393 -3002 3393 -3004 3401 -3002 3401 -1398 1791 -1394 +RAW_Data: 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3407 -1400 1789 -2990 3401 -3004 1799 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1797 -1398 3401 -1396 1797 -1402 1799 -3002 3403 -2994 4221 -2588 2113 -3134 3299 -3086 3259 -3026 3409 -3002 3407 -3006 3397 -3006 3393 -3002 3395 -3002 3393 -3002 3407 -2998 3393 -3002 3393 -3004 3401 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1396 1797 -1402 3401 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 3401 -3006 1795 -1394 3391 -1400 1801 -1400 1801 -3002 3393 -1402 1801 -3802 207 -2344 219 -1326 1669 -2836 3513 -3130 3147 -3176 3181 -3186 3391 -2994 3393 -3002 3395 -3002 3401 -2994 3401 -3004 3393 -3002 3399 -3004 3395 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3403 -1396 1791 -3000 3407 -3004 1797 -1394 1791 -1398 3401 -3002 1801 -1400 1797 -1394 3397 -3002 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 3401 -2998 1805 -1396 3391 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 3607 -4140 2069 -2820 3359 -2938 3533 -2914 3513 -2916 3337 -3160 3173 -3184 3393 -2998 3389 -2996 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -3002 1799 -1398 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -3002 1799 -1402 1801 -1396 3391 -3002 1799 -1402 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 3399 -3002 1799 -1402 3401 -1402 1795 -1394 1797 -1400 1801 -3002 3401 -7030 2161 -3078 3391 -2994 3389 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3006 1799 -1394 1791 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -2994 3403 -3002 1799 -1398 1791 -1398 3403 -3002 1799 -1402 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 1799 -1402 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -3002 3795 -3692 2201 -2812 3459 -2876 3485 -3104 3309 -2934 3359 -3176 3181 -3190 3395 -2994 3393 -3006 3399 -2994 3401 -2994 3407 -3006 3389 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -3002 1801 -1398 1791 -1398 +RAW_Data: 1801 -1400 3401 -3004 3393 -1400 1801 -3002 3399 -3006 1795 -1394 1797 -1400 3401 -2994 3403 -2994 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3394 399 -3496 2327 -2200 3995 -2554 3791 -2544 3685 -2710 3691 -2920 3333 -3156 3373 -2984 3391 -2996 3389 -2996 3401 -2994 3401 -2996 3405 -3002 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3002 3395 -1400 1801 -3002 3401 -2996 1795 -1398 1799 -1402 3401 -2994 3403 -3002 3393 -3002 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 3403 -3002 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3593 -4356 1945 -2932 3435 -3014 3415 -3004 3401 -3004 3393 -3002 3401 -2994 3407 -3006 3393 -2994 3395 -3002 3401 -3002 3399 -3004 3395 -2994 3407 -1400 1789 -1392 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3407 -3004 3395 -1396 1793 -2998 3401 -3004 1799 -1398 1791 -1398 3401 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3407 -3006 1795 -1394 1791 -1398 1801 -1400 1801 -1402 3401 -4768 389 -390 193 -2086 1793 -2908 3435 -3014 3409 -3002 3407 -3006 3401 -3002 3393 -2996 3401 -3002 3407 -2996 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3395 -1400 1797 -2998 3401 -3004 1799 -1398 1791 -1398 3401 -3002 3403 -1396 1797 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 3393 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3593 -1506 4053 -3028 3495 -2918 3323 -3148 3167 -3182 3185 -3192 3389 -2994 3401 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -3002 3395 -3002 3393 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 3393 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1398 3389 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 3397 -3002 3401 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -1402 1801 -5898 771 -378 2029 -2840 3559 -2776 3585 -2986 3395 -2994 3393 -3002 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -2998 3407 -2994 3401 -2994 3395 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1398 1795 -3002 3403 -2994 1799 -1402 +RAW_Data: 1795 -1398 3401 -1402 1801 -3002 1795 -1394 3403 -3004 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3401 -2994 1801 -1400 1795 -1400 3405 -3006 1795 -1394 3591 -3806 2201 -2824 3453 -2878 3493 -2908 3521 -2926 3349 -3164 3177 -3190 3387 -2990 3393 -2996 3401 -3006 3397 -2994 3403 -2994 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -3002 1795 -1398 1801 -1400 1795 -1398 3407 -3006 3393 -1398 1791 -2998 3403 -3002 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -2994 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -2996 3401 -3002 1795 -1394 1797 -1402 3401 -3002 3401 -5912 1929 -3004 3419 -3010 3411 -2996 3395 -2994 3401 -3002 3401 -2994 3407 -2998 3393 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3403 -2994 1801 -1400 1801 -1400 3395 -1400 1801 -2994 3401 -2922 1031 -1104 1767 -3090 3163 -3166 3183 -3186 3393 -2994 3395 -3002 3401 -2994 3403 -3002 3397 -3006 3393 -2996 3401 -2994 3401 -2994 3403 -3002 3399 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1400 1799 -1402 1799 -1402 3407 -2996 3395 -1400 1795 -2992 3401 -3002 1801 -1402 1799 -1398 3389 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3403 -2994 1799 -1398 1797 -1400 3407 -1400 1791 -1394 1797 -8408 1655 -3216 3289 -3110 3319 -3136 3159 -3176 3185 -3200 3197 -3194 3389 -2996 3393 -2994 3401 -3002 3403 -2994 3401 -2994 3403 -3002 3397 -1404 1797 -1394 1795 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1795 -1398 1801 -1402 3401 -1396 1793 -1398 1799 -3004 3405 -3006 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3393 -3002 3401 -2996 1799 -1402 3401 -3002 1795 -1394 1793 -1398 4403 -1594 579 -948 2021 -2944 3439 -3026 3417 -3002 3411 -3002 3401 -3002 3393 -3004 3401 -2994 3407 -2996 3399 -3006 3393 -2994 3395 -3002 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3401 -3004 3393 -1400 1801 -3002 3395 -3002 1799 -1402 1795 -1394 3397 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -3004 3397 -3006 1795 -1394 3391 -3002 1799 -1402 3401 -7960 1797 -3000 3435 -3014 3415 -3004 3407 -3006 3393 -2994 3407 -3002 3397 -2994 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3405 -1400 1793 -2990 3403 -3002 1799 -1402 1795 -1398 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 3407 -3006 3393 -2994 1801 -1396 3391 -3006 3405 -2994 3803 -4598 1425 -3262 3367 -2984 3185 -3190 3391 -2994 3405 -3006 3393 -2996 3401 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -3002 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 +RAW_Data: 1791 -1398 1801 -1400 3401 -3002 3403 -2994 1799 -1402 3401 -2994 3407 -1400 1791 -4212 203 -2976 1877 -3038 3175 -3164 3353 -2932 3519 -3106 3309 -3122 3147 -3168 3183 -3182 3389 -2994 3395 -3002 3393 -3002 3395 -3006 3397 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3403 -2998 3405 -1396 1793 -2998 3403 -3002 1799 -1398 3397 -3002 1797 -1398 1799 -1402 1799 -1402 3407 -2996 1797 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1396 3397 -804 201 -2984 1719 -3010 3487 -2908 3515 -3134 3147 -3168 3183 -3186 3389 -2998 3399 -2994 3393 -3002 3407 -2998 3393 -2994 3401 -3004 3401 -2994 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -2998 1805 -1398 1791 -1394 1797 -1400 3401 -3002 3403 -1400 1801 -2994 3401 -3002 1797 -1394 3397 -3002 1801 -1400 1801 -1402 3401 -2994 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -2994 1799 -1402 3393 -1402 1799 -3004 3393 -3202 803 -3626 2215 -2456 3759 -2776 3585 -2990 3391 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -3006 3397 -2994 3403 -2994 3401 -3002 3395 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3407 -3006 3393 -1398 1795 -3002 3403 -3002 1795 -1394 3397 -3002 1801 -1402 1799 -1398 3389 -1402 1799 -3002 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 +RAW_Data: 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -3002 1801 -1400 3395 -1400 1801 -1396 1797 -3002 4211 -4018 1715 -2938 3433 -3018 3411 -3010 3401 -3002 3401 -3002 3407 -2998 3393 -3002 3395 -3002 3393 -3002 3393 -3004 3401 -3002 3393 -3002 3395 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2992 3401 -3002 1801 -1400 3401 -2994 1801 -1402 3393 -3002 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 3403 -3002 3401 -3002 1795 -1394 3403 -1404 1795 -1394 1797 -1402 1799 -4574 383 -2458 2107 -3020 3263 -3042 3423 -3010 3405 -3006 3401 -3002 3407 -2998 3393 -2994 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -2994 3403 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3000 3405 -3006 1795 -1394 3391 -3002 1799 -1402 3401 -3002 3401 -2996 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 3403 -2994 3401 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 3603 -838 437 -2646 1921 -2808 +RAW_Data: 3533 -2914 3495 -2868 3443 -3022 3417 -3002 3407 -3010 3401 -2998 3393 -2996 3393 -3002 3401 -2994 3403 -3006 3397 -2994 3407 -1400 1789 -1392 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -3002 1795 -1398 3401 -3002 1797 -1398 3401 -1400 1797 -2994 1805 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -3002 3393 -3004 1795 -1398 1799 -1402 3393 -3804 421 -1492 221 -1726 1751 -3022 3331 -3148 3177 -3180 3185 -3194 3199 -3190 3393 -3002 3395 -3002 3393 -3002 3403 -2998 3405 -2998 3397 -2996 3393 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -3002 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3004 1799 -1402 3393 -3002 1801 -1396 3399 -1400 1801 -1400 1801 -2998 1805 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 3401 -3002 3395 -3006 3397 -2994 1801 -1402 3401 -2994 3801 -416 429 -2326 1741 -2894 3367 -2982 3385 -2992 3393 -3002 3401 -2994 3403 -2994 3401 -2994 3403 -3002 3405 -2998 3393 -2996 3401 -2994 3401 -3002 3395 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -3002 1801 -1400 3401 -2994 3403 -2998 1799 -1394 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 +RAW_Data: 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -3006 3397 -2996 1799 -1398 3389 -1402 1799 -7642 1653 -2952 3443 -3022 3413 -3014 3411 -3006 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3395 -2994 3401 -3002 3401 -2996 3401 -2994 3407 -1400 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3006 1801 -1392 1789 -1398 1801 -1400 3401 -3008 3405 -1396 1793 -2990 3403 -3006 1803 -1398 3389 -3002 3403 -2994 1799 -1402 3393 -3002 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -3004 3393 -3002 3401 -2994 3403 -3002 1795 -1394 4397 -596 1899 -1104 1803 -2886 3411 -3002 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2998 3405 -2994 3393 -3004 3393 -2994 3401 -3002 3403 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -3002 1801 -1398 3389 -3002 3401 -3002 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 3393 -3002 3401 -3004 3393 -3002 3393 -3002 3403 -3394 2509 -408 609 -1044 209 -420 1601 -3178 3319 -3096 3253 -3022 3419 -3014 3405 -3010 3403 -2996 3395 -2994 3401 -3002 3395 -2998 3405 -2994 3401 -3000 3397 -2994 3401 -1398 1791 -1398 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1396 3397 -3004 3401 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -3002 3403 -3002 3393 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 3397 -3002 3403 -2994 3401 -2994 3403 -1400 1795 -2992 4001 -212 211 -424 845 -2058 1877 -3244 3181 -3188 3389 -2994 3401 -2996 3401 -2994 3401 -2994 3403 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3407 -1400 1791 -2992 3401 -3002 1801 -1402 3401 -2994 3401 -1402 1799 -2996 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 3397 -3002 3403 -3006 3397 -2994 3401 -1402 1801 -1396 1791 -8006 2651 -1824 3861 -2834 3417 -3014 3407 -3002 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3395 -3006 3405 -2994 3393 -3004 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -2994 1801 -1402 1799 -1398 1791 -1398 3401 -3004 3401 -1400 1801 -2998 3405 -2996 1795 -1394 3397 -3006 3405 -1398 1791 -3000 3401 -2994 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3407 -3004 3395 -3002 3393 -1402 1795 -2992 1799 -1402 1799 -1402 3401 -4546 1981 -2786 3481 -2908 3497 -2922 3337 -3160 3175 -3186 3389 -2994 3399 -2994 3393 -3002 3403 -3002 3393 -2994 3401 -2996 3401 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -2998 1801 -1402 1799 -1398 1797 -1400 3401 -3002 3403 -1400 1801 -2994 3401 -2994 1801 -1402 3401 -2994 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 3401 -3002 3395 -3006 3405 -1398 1789 -2990 1799 -1402 3401 -4586 587 -746 551 -1102 2019 -2888 3497 -2864 3439 -3018 3413 -3002 3401 -3002 3403 -2994 3401 -2998 3407 -2994 3393 -3002 3393 -3004 3401 -2998 3397 -2998 3407 -1396 1791 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3393 -1402 1799 -3004 3393 -3002 1795 -1398 3401 -3004 3405 -1400 1793 -1394 1791 -1398 1801 -3002 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 3403 -2994 3401 -3006 3399 -1396 1791 -2992 3401 -3002 3603 -400 201 -1420 877 -1100 1971 -3176 3319 -2904 3511 -3132 3147 -3168 3181 -3186 3395 -2994 3393 -3002 3393 -3004 3401 -3002 3393 -2994 3403 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3393 -1402 1795 -2998 3403 -3002 1795 -1394 +RAW_Data: 3397 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3395 -3002 3401 -3002 3393 -1402 1795 -2992 3401 -1400 1801 -5262 401 -2280 2005 -2706 3647 -2842 3423 -3010 3405 -3006 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3002 3403 -2998 3405 -2998 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 3395 -3006 1805 -1396 3389 -1402 1801 -3002 1795 -1394 1797 -1400 3401 -3004 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -3002 3407 -1404 1793 -1392 1793 -2998 1801 -1400 3603 -2686 1231 -664 1505 -3148 3313 -3126 3149 -3166 3175 -3190 3193 -3192 3393 -2994 3401 -2996 3401 -3006 3397 -2998 3399 -2994 3401 -2994 3403 -3002 3401 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3006 3397 -1402 1795 -2996 3405 -2998 1805 -1396 3391 -1400 1801 -2994 1801 -1400 3403 -2994 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3407 -3006 3393 -1396 1793 -1398 1799 -3002 3403 -3602 861 -1102 1545 -442 1983 -3002 3277 -3092 3513 -3124 3147 -3168 3173 -3194 3195 -3190 3393 -3002 3395 -3006 3397 -2994 3395 -3002 3401 -2998 3405 -3000 3397 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1398 3401 -3006 3393 -1402 1795 -2992 3401 -3002 1801 -1400 3395 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -3002 3403 -1396 1793 -1398 1799 -1402 1799 -3004 3593 -610 3645 -2950 3285 -3122 3311 -3116 3323 -3138 3163 -3174 3187 -3194 3397 -2996 3393 -2994 3401 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1795 -1394 3399 -1400 1801 -3002 3401 -2994 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -3002 3393 -2994 3403 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -4212 1599 -1486 1653 -3012 3347 -3168 3181 -3188 3389 -2994 3395 -2994 3401 -3002 3401 -2996 3393 -3002 3401 -3002 3395 -3002 3399 -3004 3393 -2996 3401 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3399 -1404 1795 -2996 3405 -2994 1801 -1396 3391 -1400 1801 -3002 3401 -2996 3401 -3006 1799 -1392 1789 -1398 1801 -1402 1799 -1402 +RAW_Data: 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -2994 3401 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 1801 -1402 4617 -3624 1617 -3122 3183 -3190 3389 -2994 3403 -2998 3405 -2994 3393 -3004 3393 -3006 3397 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1400 1801 -3002 3393 -3004 1795 -1394 3397 -1402 1799 -3002 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 3401 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1402 3401 -3200 601 -3982 1955 -2718 3687 -2842 3425 -3016 3405 -3006 3401 -3002 3403 -2994 3401 -3002 3393 -2996 3401 -3006 3397 -2998 3407 -2994 3393 -3006 3399 -1396 1791 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1400 3403 -2998 3405 -1398 1791 -2992 3401 -3006 1799 -1394 3391 -1400 1801 -3002 3401 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 +RAW_Data: 1799 -1402 1801 -1400 1801 -1400 1795 -1394 3399 -3002 3401 -1402 1795 -2998 1801 -1402 1799 -1398 3389 -3002 3603 -608 209 -3446 2219 -2994 3443 -3026 3417 -3002 3407 -3008 3399 -2998 3405 -2994 3395 -2998 3405 -2994 3395 -3002 3405 -2998 3393 -3002 3395 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3403 -1396 1797 -2994 3401 -3002 1801 -1398 3393 -1406 1799 -1402 1795 -3000 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 3389 -3002 3403 -1400 1801 -3002 1795 -1394 1797 -1400 3403 -1400 1801 -5432 663 -1710 1925 -2726 3617 -3010 3419 -3006 3401 -3002 3401 -2998 3407 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -3004 3393 -2994 3401 -3002 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -3002 1801 -1400 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1795 -2992 3401 -3002 1801 -1400 3401 -1398 1791 -1398 1801 -3002 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 1801 -1402 3393 -2994 1801 -1400 3603 -3858 2219 -3186 3253 -3036 3417 -3006 3405 -3002 3403 -2998 3405 -2994 3401 -2996 3401 -2998 3405 -2994 3395 -2994 3401 -3002 3395 -3002 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2998 1799 -1394 1797 -1402 1799 -1402 3401 -2998 3405 -1398 1791 -2992 3401 -3002 1801 -1400 3403 -1400 1795 -1396 1795 -3002 3403 -3002 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 3405 -3002 3397 -1398 1791 -2992 1801 -1400 3401 -3002 3395 -6072 1197 -792 1865 -2724 3513 -2896 3455 -3034 3419 -3002 3401 -3002 3401 -3004 3401 -2994 3401 -2998 3407 -2994 3401 -2994 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3401 -3000 3405 -1396 1793 -2990 3407 -3006 1795 -1394 3397 -1402 1799 -1402 1801 -3002 3393 -1400 1797 -2998 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 3401 -3004 3393 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 4189 -3552 1729 -2986 3429 -3022 3419 -3006 3393 -3002 3399 -3004 3395 -2994 3401 -3002 3395 -3002 3393 -3006 3397 -3000 3405 -2994 3401 -2994 3395 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1400 3395 -1400 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 3393 -1402 1799 -3002 1797 -1394 +RAW_Data: 3397 -1402 1799 -1402 1799 -7326 2401 -2060 4155 -2504 3723 -2690 3661 -2838 3421 -3010 3411 -3002 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3401 -2996 3393 -2994 3401 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -3004 1799 -1398 1795 -1402 1801 -1400 3401 -3004 3393 -1400 1801 -2994 3401 -3004 1795 -1394 3397 -1402 1799 -1402 1799 -1398 1791 -3000 3401 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3004 1799 -1398 1791 -1398 3601 -4120 1733 -2886 3559 -2960 3351 -3094 3255 -3044 3421 -3010 3401 -3006 3407 -3002 3393 -3002 3393 -3004 3401 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2998 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3403 -1400 1795 -2992 3401 -3002 1801 -1400 3403 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -3002 1801 -1396 3399 -5588 365 -1116 2145 -3134 3189 -3190 3395 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3393 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 1801 -1396 3391 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 +RAW_Data: 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 3401 -3002 3393 -1402 1795 -3000 3401 -3002 3393 -3004 3593 -3932 2743 -2594 3595 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -2994 3403 -2998 3405 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -2998 3401 -3004 3401 -1396 1793 -7038 2817 -3032 3167 -3182 3385 -2996 3393 -2994 3401 -3002 3399 -3006 3393 -2994 3403 -2994 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -1404 1795 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3395 -3002 3401 -1398 1795 -3002 3403 -2994 3401 -3002 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2990 3403 -1400 1801 -3002 1801 -1396 3591 -4298 1767 -2654 3559 -2912 3513 -2914 3491 -2868 3443 -3022 3417 -3002 3407 -3006 3393 -3002 +RAW_Data: 3393 -3002 3403 -2994 3407 -3004 3395 -2994 3399 -1404 1795 -1394 1797 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1795 -1398 3401 -3006 3399 -1400 1795 -2992 3401 -3002 3403 -2994 1799 -1402 1801 -1396 1791 -1398 3403 -3002 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -3002 3395 -1396 1797 -3002 3407 -1404 1795 -2992 3401 -3400 1713 -1100 3677 -3172 3377 -2982 3187 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3399 -3004 3393 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 1791 -1398 3401 -3002 3403 -1400 1795 -2992 3401 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1795 -3000 3801 -1120 181 -920 553 -1654 1653 -3052 3481 -2942 3489 -2908 3299 -3120 3335 -3164 3175 -3182 3389 -2996 3393 -3002 3401 -2996 3393 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 3403 -3006 1599 -1592 1789 -1398 3401 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1797 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3402 1057 -3748 1469 -3298 3149 -3174 3175 -3190 3393 -2994 3395 -3002 3393 -3002 3403 -3002 3393 -3002 3399 -3004 3395 -2994 3401 -2994 3403 -2994 3401 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3395 -1400 1801 -3002 3401 -2994 3403 -2994 1799 -1402 1801 -1400 3393 -3004 3393 -3002 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3601 -3262 435 -874 1833 -3302 3235 -3018 3405 -3002 3407 -2998 3393 -3002 3393 -3002 3399 -3006 3393 -2994 3403 -3002 3393 -3002 3393 -3008 3397 -3002 3393 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 1799 -1402 1801 -1396 3403 -3004 3395 -1396 1793 -2998 3401 -3004 3401 -2994 1801 -1400 1795 -1394 3399 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 3401 -2994 3403 -1396 1797 -1400 1801 -3002 1795 -1396 1795 -1402 3401 -7588 1923 -3068 3393 -2994 3403 -2994 3407 -3004 3399 -2998 3393 -2994 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3403 -2998 3409 -1400 1789 -1394 1795 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -2998 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3403 -1400 1801 -2994 3407 -2996 3395 -3002 1795 -1394 1797 -1400 3401 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -3002 3401 -1398 1791 -1398 1801 -3002 1801 -1400 3401 -2996 3607 -1728 833 -1396 2303 -2880 3581 -2986 3391 -2994 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3393 -2994 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3407 -1400 1791 -2992 3401 -3002 3407 -2998 1795 -1394 3389 -3002 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3407 -2996 3395 -1400 1795 -1398 1801 -3002 1801 -1400 3395 -1400 1801 -7606 1737 -3220 3235 -3022 3417 -3002 3407 -3004 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3393 -3004 3401 -2994 3393 -3002 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3407 -1400 1789 -2990 3401 -3002 3403 -3002 1799 -1398 3389 -3002 1801 -1402 3401 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1402 1795 -1394 1797 -3002 3401 -3002 1801 -1398 4195 -1886 1105 -442 1765 -3046 3269 -3160 3341 -3122 3291 -3112 3319 -3140 3163 -3176 3185 -3190 3391 -2994 3393 -2998 3411 -2998 3393 -3002 3393 -3004 3393 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3401 -1402 1795 -2992 3401 -3002 3401 -3004 1795 -1394 3397 -3002 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -3002 3407 -1400 1791 -1394 1797 -3002 3401 -3002 3395 -5516 1583 -3012 3489 -2910 3305 -3120 3337 -3164 3173 -3186 3191 -3190 3399 -3004 3395 -2994 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -3002 1801 -1400 1795 -1394 1797 -1402 3405 -3006 3393 -1402 1795 -2992 3401 -3002 3401 -3002 1797 -1394 3397 -3002 3407 -1404 1795 -2992 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 3401 -3002 3401 -1398 1791 -1398 1801 -3002 3401 -1398 1795 -3002 3595 -3624 415 -206 2483 -2414 3585 -3002 3401 -2994 3401 -3004 3393 -2994 3401 -3002 3403 -2998 3411 -2996 3395 -2994 3393 -3002 3393 -3004 3401 -2994 3401 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 3407 -3004 3399 -1400 1789 -2990 3403 -3002 3401 -3002 1795 -1394 3403 -1404 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1795 -1394 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -1402 1799 -2994 3403 -1400 1795 -1398 1801 -8014 1843 -2840 3389 -2994 3403 -2994 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3401 -2998 3399 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3405 -3006 3393 -1398 1791 -3000 3405 -3006 3393 -2994 1801 -1398 3389 -1402 1799 -3002 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 3395 -4724 1853 -2864 3547 -2976 3181 -3186 3391 -2994 3393 -2994 3407 -3006 3399 -2996 3399 -2998 3393 -3002 3399 -3006 3393 -2994 3401 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3004 3393 -3002 3401 -2994 1801 -1402 3393 -1400 1797 -1398 1799 -3002 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 3397 -5708 365 -1470 9113 -1824 3823 -2614 3609 -3002 3403 -3002 3401 -3002 3401 -2994 3403 -3002 3393 -3002 3399 -3006 3393 -2994 3401 -3004 3393 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -3006 3397 -2996 1799 -1398 3397 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -3002 3407 -1404 1795 -1394 1791 -1398 1801 -3002 3401 -2996 3601 -1690 629 -2154 1905 -2832 3651 -2830 3423 -3006 3401 -3002 3401 -3002 3395 -3002 3401 -3002 3395 -3002 3405 -2998 3393 -2996 3401 -3002 3407 -2996 3395 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1795 -1394 1797 -1402 3401 -3002 3401 -1402 1795 -2996 3409 -2998 3393 -2994 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 3401 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -3002 3401 -1402 1795 -3600 639 -3748 1839 -2644 3729 -2756 3567 -2982 3391 -2990 3393 -2994 3403 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -3002 3403 -2994 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3401 -3004 3397 -1404 1797 -2990 3403 -3002 3405 -2998 3393 -2996 1799 -1398 1795 -1402 3401 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 +RAW_Data: 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -3000 1801 -1400 3801 -4494 1987 -2568 3839 -2822 3413 -3002 3403 -3002 3401 -3002 3401 -3002 3407 -3010 3393 -2994 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -3004 3393 -3002 3401 -3002 1797 -1394 3389 -3002 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -3002 3405 -8042 1841 -2856 3557 -2932 3531 -2910 3509 -2922 3345 -3160 3173 -3188 3395 -2996 3391 -2994 3401 -3002 3399 -2998 3393 -3002 3401 -2994 3395 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -3002 3403 -2994 1795 -1398 3407 -1404 1795 -2992 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 3395 -3002 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -2994 3603 -4452 +RAW_Data: 1771 -2854 3567 -2986 3389 -2992 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1795 -1400 3401 -3002 3393 -1402 1799 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3407 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -7856 8207 -2522 3741 -2764 3573 -2988 3393 -2994 3393 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3006 3399 -2994 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -3002 3401 -2994 3395 -3002 3401 -2998 1805 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 3401 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3601 -4768 1587 -3096 3397 -2990 3395 -2994 3401 -3002 3395 -3006 3397 -2994 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3401 -2998 3399 -2994 3401 -2994 3401 -1402 1795 -3000 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -2994 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -5196 383 -1652 1837 -3050 3375 -2986 3393 -2996 3393 -3002 3401 -2994 3403 -2998 3405 -2994 3395 -3002 3405 -2998 3393 -2996 3401 -3002 3393 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1400 1801 -1400 3395 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1398 1795 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -2994 3603 -836 655 -2854 1731 -2816 3487 -2910 3517 -2910 3481 -2856 3443 -3016 3407 -3006 3401 -3006 3399 -2994 3393 -3006 3397 -2996 3393 -3002 3401 -3002 3395 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -3000 3401 -3002 3401 -2994 3407 -1404 1795 -2992 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1799 -1398 3389 -1402 1799 -7638 2209 -2584 3689 -2704 3511 -2934 3347 -3168 3179 -3186 3389 -2994 3395 -2994 3401 -3002 3393 -3004 3393 -3002 +RAW_Data: 3401 -2994 3403 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3407 -3000 3399 -2994 3393 -1402 1795 -2992 1799 -1402 3401 -3002 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -3004 1799 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 3601 -4312 1887 -2802 3527 -2906 3523 -3128 3147 -3164 3181 -3188 3389 -2994 3395 -2994 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1396 1797 -3002 3407 -2996 3395 -2994 3401 -1402 1795 -2998 3395 -3006 1803 -1398 1791 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3401 -2996 3401 -7934 8393 -2332 3759 -2774 3583 -2990 3389 -2994 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -2994 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3006 3405 -2998 3397 -1398 1791 -3000 3401 -1402 1799 -2994 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -1400 1801 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -1396 1797 -3002 3603 -4584 1767 -2612 3777 -2734 3539 -2912 3505 -2916 3331 -3156 3173 -3182 3187 -3190 3393 -2996 3401 -3002 3401 -2994 3395 -3002 3401 -2994 3407 -1400 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3401 -1398 1791 -2990 3403 -3002 3401 -3002 3401 -1398 1791 -1398 1801 -3006 1805 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1400 3395 -1400 1801 -1398 1795 -8110 2015 -2632 3843 -2818 3415 -3002 3407 -3004 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2998 3405 -2994 3393 -3004 3393 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1801 -3002 3393 -2994 3401 -3002 3395 -1400 1801 -1402 1799 -2994 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 3397 -1400 1801 -3002 1801 -1396 1793 -1398 3401 -3002 1795 -1398 1801 -1400 3603 -4032 1885 -2658 3649 -2852 3473 -2950 3571 -2718 3513 -2898 3455 -3040 3421 -3010 3401 -3006 3411 -2998 3393 -3002 3395 -3002 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -3008 1799 -1394 1791 -1398 1801 -1400 3401 -3004 3401 -1396 +RAW_Data: 1793 -3002 3407 -2994 3401 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 3401 -1402 1801 -2998 1803 -1398 1791 -1394 3399 -3002 1799 -1402 3401 -8194 21409 -2410 3595 -3000 3401 -3002 3403 -3006 3405 -2994 3395 -3002 3401 -2994 3401 -3002 3395 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2994 3407 -1404 1795 -2992 3401 -3002 3395 -3006 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1795 -3000 1799 -1402 1799 -1398 3397 -3002 3403 -3002 3593 -4590 8471 -2416 3813 -2802 3403 -3002 3401 -3002 3401 -3004 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -3002 3401 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -3004 1795 -1398 1799 -1402 1795 -1398 3401 -3004 3401 -1396 1793 -2998 3401 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 +RAW_Data: 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -3006 1803 -1398 1791 -1398 3401 -3004 3393 -1400 1801 -6470 1853 -2954 3275 -3044 3425 -3014 3405 -3004 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2998 3405 -2994 3395 -3006 3397 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3401 -2994 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3000 1803 -1398 1791 -1398 3401 -1402 1799 -3004 1795 -1394 3797 -4442 2001 -2426 3829 -2818 3415 -3014 3401 -3002 3393 -3002 3395 -3002 3401 -3002 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1402 1799 -3006 3399 -2994 3401 -1398 1791 -2998 1801 -1402 1799 -1402 3393 -3002 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -1400 1801 -3002 1801 -1396 1791 -1398 3403 -1400 1801 -3002 3401 -8088 1813 -2902 3441 -3020 3417 -3010 3411 -3004 3399 -3002 3397 -2994 3395 -3002 3393 -3002 3393 -3004 3401 -2998 3405 -2994 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3403 -1400 1795 -2992 3401 -3002 3403 -1400 1795 -2992 1801 -1400 1801 -1400 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -1402 1795 -2992 3601 -4130 1967 -2648 3605 -2874 3687 -2732 3527 -2902 3507 -2930 3349 -3168 3173 -3186 3395 -2998 3397 -2994 3395 -3002 3401 -2994 3401 -3000 3405 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -3002 3401 -1402 1795 -2992 1799 -1402 3401 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -1400 1801 -3002 1801 -1400 1797 -1398 3401 -1400 1801 -1402 1795 -1394 1797 -3402 1061 -2794 2197 -2242 4043 -2320 3991 -2722 3497 -2880 3643 -2830 3415 -3010 3401 -3002 3401 -3002 3403 -3002 3401 -3002 3399 -3006 3393 -2994 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3407 -2998 3393 -1396 1793 -3002 3407 -3002 3393 -1398 1795 -3002 1801 -1402 3401 -3002 3393 -3002 1797 -1394 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 +RAW_Data: 1799 -1398 1797 -1400 3401 -1402 1795 -2992 1801 -1400 3401 -3002 1801 -1402 1799 -1398 1791 -1398 3601 -2170 4255 -2912 3537 -2960 3375 -2986 3189 -3192 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3006 3399 -1396 1791 -3000 3401 -3002 3403 -1400 1795 -2992 1801 -1400 3401 -1402 1795 -3000 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -1402 1799 -3002 1797 -1398 3401 -3002 1801 -1396 1793 -1398 3401 -8220 2607 -1888 3817 -2618 3609 -3002 3403 -3002 3401 -3002 3401 -3006 3399 -2994 3401 -2994 3403 -2998 3405 -2994 3393 -3004 3401 -2998 3405 -1398 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -2996 1799 -1402 1799 -1398 1797 -1400 3401 -2996 3401 -1400 1801 -2994 3401 -3004 3393 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3401 -1398 1791 -3004 1803 -1402 3393 -3002 1795 -1396 3397 -3002 3401 -4962 8235 -2580 3581 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3393 -3002 3403 -3002 3393 -2994 3403 -3006 3397 -2998 3405 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3395 -3002 3397 -1404 1797 -2990 3403 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 +RAW_Data: 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 3401 -1398 1791 -3000 1799 -1402 3401 -2998 1805 -1398 3389 -1400 1801 -8008 2567 -1896 3829 -2822 3413 -3014 3407 -3002 3401 -2994 3401 -3002 3395 -3002 3399 -3004 3395 -3002 3399 -3004 3393 -2996 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1797 -2990 3401 -3004 3401 -1400 1801 -2998 3397 -3004 1795 -1394 3397 -3002 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 3403 -1404 1795 -2992 1799 -1402 3401 -2998 3407 -2998 1799 -1394 3791 -4154 1719 -2816 3391 -3170 3285 -3136 3323 -3106 3317 -3134 3147 -3164 3173 -3188 3389 -2994 3395 -3002 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3006 1801 -1394 1791 -1398 1801 -1400 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1793 -2998 3401 -3008 3405 -2994 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 1799 -1402 3401 -2994 3403 -2994 3405 -8152 1939 -2656 3635 -2818 3409 -3014 3405 -3002 3403 -2994 3401 -2994 +RAW_Data: 3401 -3004 3393 -3002 3401 -2998 3211 -3198 3393 -2994 3395 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1398 3397 -3002 3401 -1402 1795 -3000 3401 -2994 3403 -1400 1795 -2992 3401 -3006 3407 -1396 1791 -2996 1805 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1799 -2994 1801 -1402 3401 -3002 3393 -1398 1797 -3002 3601 -4348 1837 -2790 3659 -2830 3417 -3016 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3006 3205 -1598 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -3000 1799 -1398 1797 -1400 1801 -1396 3399 -3002 3401 -1398 1795 -3002 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -2998 1805 -1396 1791 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 3399 -1400 1801 -2994 1801 -1400 3401 -3002 3395 -1400 1801 -1398 1791 -8144 1783 -2880 3443 -3018 3419 -3004 3403 -3002 3401 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -2998 3403 -3002 3401 -1400 1797 -2990 3407 -1404 1795 -2992 3401 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 3397 -1402 1801 -3002 1799 -1398 3397 -1402 1799 -3004 1795 -1394 1797 -1400 4407 -3438 2423 -2508 3661 -2838 3421 -3020 3405 -3002 3401 -3002 3403 -3006 3397 -2994 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3401 -3002 3399 -1400 1791 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -3002 3401 -1398 1791 -2998 3403 -1400 1795 -1398 1801 -3002 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -1400 1791 -2992 1801 -1400 3401 -1402 1801 -2994 1799 -1402 3401 -5710 183 -2022 2537 -1884 3815 -2610 3601 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3405 -3006 3393 -2996 3393 -3002 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3407 -1400 1791 -2996 3405 -2994 3403 -1396 1791 -3000 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -3008 1803 -1398 3395 -1404 1795 -2990 3403 -3002 3593 -4580 1655 -3024 3321 -2950 3563 -2974 3385 -2992 3397 -2998 3393 -3000 3397 -3002 3393 -2996 3401 -3006 3397 -2994 3403 -3002 3399 -3004 3399 -1400 1789 -1392 1797 -1402 1799 -1402 1799 -1402 1801 -3002 +RAW_Data: 1795 -1394 1791 -1398 1801 -1400 3403 -3002 3401 -1402 1799 -2994 3403 -2994 3401 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1795 -3000 1801 -1400 3401 -1402 1801 -2994 3401 -1400 1797 -7502 3329 -1154 3985 -2602 3601 -3008 3397 -2994 3393 -3002 3407 -2998 3393 -2998 3407 -2994 3401 -2994 3401 -2996 3401 -2994 3401 -2994 3403 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1400 3403 -3002 3401 -1402 1795 -2992 3401 -3002 3401 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 3389 -3004 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -1400 1801 -2994 1801 -1400 4395 -3776 14743 -2408 3811 -2802 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -3002 3401 -2994 3395 -3002 3393 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1398 3397 -3002 3407 -1404 1795 -2992 3407 -2996 3395 -1400 1795 -1398 1801 -3002 1801 -1396 3391 -3002 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 +RAW_Data: 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 3397 -1402 1799 -3002 1797 -1394 3397 -1402 1799 -1402 1799 -2996 3401 -3996 385 -3102 2063 -3036 3275 -3042 3425 -3020 3405 -3006 3411 -2996 3395 -3002 3393 -3002 3395 -3002 3393 -3002 3401 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3407 -1404 1595 -3194 3407 -2994 3393 -1402 1795 -1394 1797 -3002 1799 -1402 3401 -1398 1795 -3004 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 1795 -1394 3391 -1400 1801 -1400 1801 -1400 1801 -3002 3603 -1478 645 -1724 2035 -3064 3173 -3182 3187 -3190 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3407 -2998 3393 -3002 3393 -3002 3403 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1402 1795 -1394 3397 -3002 3403 -1400 1795 -3000 3401 -3002 3395 -1400 1801 -1396 1797 -3002 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 3407 -1400 1791 -2992 1799 -1402 3401 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -8088 1731 -3044 3437 -3028 3261 -3072 3287 -3118 3295 -3118 3323 -3148 3167 -3180 3185 -3190 3395 -2994 3399 -3004 3393 -2996 3401 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1801 -1396 3397 -2996 3401 -1400 1801 -3006 3403 -2998 3397 -1400 +RAW_Data: 1793 -1394 1795 -3004 3401 -3002 3393 -2994 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1801 -3002 3393 -3002 1801 -1396 1797 -1402 1799 -1402 1795 -1398 3401 -4926 8515 -2468 3839 -2822 3413 -3010 3407 -3004 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3393 -3002 3403 -3002 3401 -2994 3395 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -2994 3403 -3002 3393 -1402 1799 -1398 1795 -3004 3401 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 3403 -1396 1791 -3000 3401 -3002 1801 -1400 1797 -1394 1795 -1402 3407 -8138 1617 -2850 3563 -2976 3385 -2990 3395 -2994 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -2994 3407 -3006 3393 -2994 3407 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -1398 1795 -3004 1799 -1402 1795 -1398 1801 -1400 3401 -2996 3401 -1400 1797 -2990 3403 -3002 3401 -1400 1801 -1398 1791 -2998 3403 -1400 1801 -1400 1801 -2994 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 +RAW_Data: 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -1402 1801 -3002 3397 -3006 1795 -1394 1793 -1398 3401 -3002 3601 -4378 1989 -2806 3493 -2902 3511 -2930 3349 -3166 3183 -3186 3389 -2994 3403 -2994 3407 -3004 3395 -2994 3393 -3002 3401 -3004 3393 -3002 3393 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -3002 3403 -1396 1793 -3002 3405 -2996 3405 -1400 1793 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -2994 1801 -1400 1801 -1402 3393 -1400 1801 -7422 9169 -1802 3795 -2600 3611 -3002 3401 -3006 3405 -2996 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3403 -2994 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 3401 -2996 3401 -1396 1797 -3002 3403 -3002 3393 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 3401 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1795 -3000 3401 -3002 1795 -1394 3399 -3002 1799 -1402 3801 -1286 649 -2066 2205 -3008 3427 -3012 3407 -3006 3393 -2994 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -2994 3401 -1402 1795 -2998 3403 -3002 3393 -1402 1799 -1398 1791 -1398 1801 -3002 3401 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 +RAW_Data: 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -3002 3393 -3002 1797 -1394 3397 -3002 3401 -7732 14869 -2460 3833 -2818 3409 -3004 3401 -3002 3401 -3002 3403 -3002 3405 -2998 3393 -3004 3393 -2994 3407 -3008 3199 -1596 1789 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1801 -1398 3397 -3002 3401 -1398 1791 -3000 3401 -2994 3401 -1402 1801 -1400 1801 -1400 1795 -2992 3401 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3407 -1400 1791 -2992 3401 -3004 1799 -1402 3407 -1400 1789 -2988 3403 -3830 2193 -2986 3375 -2944 3501 -3050 3235 -3018 3409 -3002 3403 -3002 3401 -3002 3401 -3002 3395 -3002 3393 -3006 3207 -3194 3399 -3004 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3401 -1398 1795 -3002 3395 -3002 3393 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -3006 1601 -1592 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 3399 -1400 1801 -3002 3401 -2994 +RAW_Data: 1801 -1400 3403 -1400 1795 -1396 1795 -6110 417 -1290 1655 -3016 3535 -2958 3375 -2982 3187 -3194 3399 -2996 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3407 -2996 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1402 1799 -2994 3407 -2998 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 3401 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 3395 -1400 1801 -2994 3401 -3002 3395 -3002 1799 -1398 1791 -1398 3603 -200 965 -3206 1811 -2896 3423 -3010 3405 -3006 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -3002 3401 -2994 3403 -2994 3393 -3002 3407 -2998 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -3002 3403 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -1402 1795 -2990 3403 -3002 3401 -3002 1801 -1396 3391 -7600 1899 -3004 3287 -3106 3315 -3134 3159 -3176 3185 -3190 3391 -2994 3393 -3002 3395 -3002 3401 -2994 3401 -3004 3393 -3002 3407 -2996 3395 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -3002 1795 -1398 1801 -1400 1801 -1402 3401 -2994 3401 -1402 1795 -3000 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 3403 -1404 1795 -2992 3401 -3004 3393 -3002 3401 -2994 3403 -4206 2011 -3272 3159 -3176 3181 -3186 3195 -3190 3393 -3002 3395 -3002 3401 -2998 3405 -2996 3405 -2998 3393 -2994 3403 -2994 3401 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1396 3391 -3002 3401 -1402 1799 -3002 3403 -1396 1797 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 3407 -1404 1597 -3190 3401 -2994 3403 -3002 3393 -1402 1795 -7316 8443 -2572 3577 -2988 3393 -3002 3393 -3004 3393 -3006 3405 -2994 3395 -3002 3393 -3002 3395 -3002 3401 -3002 3393 -3006 3399 -1396 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 3401 -2998 3397 -1398 1791 -3000 3401 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 3395 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 3401 -1400 1801 -3002 3401 -3004 3393 -1400 1801 -2994 1801 -1400 3603 -4490 1839 -2642 3731 -2746 3573 -2988 3389 -2990 3393 -3000 +RAW_Data: 3405 -2994 3393 -3002 3403 -2994 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3403 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -3000 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -3004 3405 -1396 1793 -2990 1801 -1400 1801 -1402 1799 -1402 1799 -1398 3397 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1400 1801 -3002 3393 -3002 3403 -1396 1793 -2998 3401 -8350 1743 -2940 3389 -3000 3397 -2994 3393 -3004 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3407 -2998 3393 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -2998 3407 -1396 1791 -2992 3401 -1402 1801 -3002 1799 -1398 1797 -1400 1801 -1400 1795 -1396 3397 -1400 1801 -3002 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3002 3395 -3002 3407 -1400 1789 -1392 1797 -3006 3605 -4930 1985 -2864 3367 -2982 3185 -3192 3393 -2994 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2998 3207 -3194 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1801 -1400 1801 -1400 1801 -1400 3407 -2998 3393 -1398 1791 -3000 3401 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -3002 3393 -3002 3407 -1400 1789 -1392 1797 -1402 1799 -8070 9613 -1418 3825 -2614 3609 -3002 3401 -3004 3401 -3002 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -3002 3401 -2994 3403 -1396 1797 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1398 1791 -2998 3403 -1400 1801 -3002 1801 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -2996 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -3002 3393 -1402 1795 -3000 1799 -1402 1799 -1398 1791 -1398 3603 -4754 8207 -2562 3569 -2984 3389 -2996 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -2992 3401 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 3401 -1402 1799 -3002 3395 -1400 1801 -2994 1801 -1400 1801 -1398 3389 -7746 8811 -2430 3817 -2810 3411 -3002 3401 -3002 3401 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -3006 3397 -2996 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -2994 3403 -1400 1801 -3002 3401 -1398 1791 -3000 1799 -1402 1801 -1396 1797 -1400 3403 -1396 1791 -1398 1801 -3002 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 3393 -1402 1799 -3004 3393 -1400 1797 -2998 1801 -1400 3403 -3002 3993 -3076 14679 -2816 3619 -2804 3401 -3002 3403 -3002 3393 -3002 3401 -3004 3393 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -2994 3403 -1396 1797 -3002 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3395 -1400 1801 -3002 3393 -1402 1799 -3002 1801 -1398 3389 -1400 1801 -6990 1657 -3042 3285 -3122 3305 -3118 3323 -3152 3167 -3174 3187 -3190 3389 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1801 -1398 3389 -3006 3405 -1398 1791 -3000 3401 -1402 1795 -2990 1801 -1402 1799 -1402 3401 -3002 1795 -1394 3399 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 +RAW_Data: 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 3401 -1400 1801 -3006 3399 -1396 1797 -3002 3393 -3002 1797 -1394 3597 -4444 1939 -2940 3439 -3026 3413 -3006 3407 -3002 3397 -3006 3393 -2996 3393 -3002 3393 -3006 3207 -3194 3393 -2994 3403 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -2996 3401 -1400 1801 -3002 3393 -1398 1791 -3004 1803 -1402 1795 -1398 3403 -3002 3401 -2998 1799 -1394 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 3393 -1402 1801 -3002 3393 -1402 1799 -3002 3399 -3006 3393 -7892 1731 -2682 3593 -2994 3403 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3399 -3006 3393 -2994 3401 -3006 3399 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1402 1795 -2992 3401 -1402 1799 -3002 1795 -1400 1799 -1402 3401 -2994 3401 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 3401 -1400 1797 -2994 3407 -1400 1795 -3000 3401 -1402 1795 -2992 3601 -4720 1955 -2804 3439 -3022 3413 -3002 3401 -3002 3403 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 1797 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 1801 -1402 1795 -1394 3397 -1402 +RAW_Data: 1799 -3002 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 3401 -1400 1801 -2994 3403 -1400 1801 -2998 3411 -1400 1789 -1392 1797 -7942 14895 -2618 3611 -2810 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -2998 3407 -2994 3393 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3403 -1400 1795 -2992 3401 -1402 1799 -3008 1599 -1594 1795 -1402 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1402 1799 -2994 3403 -1400 1795 -1398 1801 -3002 1795 -1400 1799 -1402 3801 -1294 1075 -840 2397 -2992 3435 -3022 3249 -3164 3313 -2942 3519 -3068 3247 -3026 3413 -3010 3401 -3002 3403 -3002 3393 -3002 3403 -3002 3393 -3002 3393 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -3004 1799 -1398 1791 -1398 1801 -1400 3401 -3004 3401 -1396 1793 -2998 3403 -1400 1801 -3002 1795 -1394 1797 -1400 3403 -1400 1801 -1400 1795 -3000 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 3401 -1402 1801 -2994 3401 -1402 1795 -1398 1799 -3004 1795 -1394 3397 -7240 3691 -1402 3855 -2834 3419 -3018 3413 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3407 -2996 3399 -1404 1597 -1590 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -3002 1801 -1396 1793 -1398 3405 -1404 1797 -1394 1795 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1396 1797 -3002 3401 -1402 1795 -1394 1797 -3002 3407 -3004 3595 -4610 1539 -3058 3381 -2988 3193 -3194 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3407 -3004 3395 -2994 3393 -3002 3401 -2996 3401 -2994 3401 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1398 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1801 -2994 1799 -1402 3401 -3002 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -3002 3403 -1400 1801 -6986 367 -734 1837 -2952 3531 -2896 3279 -3050 3435 -3018 3401 -3002 3401 -3008 3405 -2998 3205 -3198 3399 -2994 3401 -2998 3399 -2994 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1400 1801 -3002 3395 -1400 1801 -3002 1795 -1394 3397 -3004 1799 -1402 1799 -1398 3395 -3004 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 3399 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 3793 -4322 1655 -3012 3323 -3148 3167 -3186 3189 -3192 3389 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2998 3407 -2994 3401 -2994 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1396 3397 -3004 3401 -1396 1793 -2998 3403 -1400 1801 -3002 1795 -1394 3397 -3002 1801 -1402 3401 -2994 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -1402 1801 -2994 3401 -1398 1795 -1402 1799 -1402 1801 -2994 3401 -3594 835 -1436 377 -1140 1711 -3242 3159 -3176 3181 -3190 3193 -3200 3201 -3202 3197 -3196 3393 -2994 3407 -2996 3395 -2994 3401 -3002 3403 -2994 3401 -2994 3407 -1404 1597 -1592 1791 -1398 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1396 1797 -3002 3403 -1400 1801 -2994 1801 -1400 3401 -3002 1797 -1394 3397 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1402 1799 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3006 3397 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3006 3799 -1526 441 -1060 2475 -2980 3315 -2928 3499 -3076 3249 -3030 3417 -3014 3411 -3006 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -3000 3209 -3202 3197 -1596 1787 -1392 1797 -1400 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1801 -1400 1801 -1396 3399 -3002 3401 -1396 1793 -2998 3403 -1396 1797 -3002 1801 -1400 3401 -3002 3395 -2994 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -7480 27707 -2512 3729 -2756 3573 -2984 3385 -2994 3399 -2990 3393 -3006 3205 -3192 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -3002 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1400 1797 -2998 3403 -1400 1795 -2992 1801 -1400 3401 -3008 3205 -3194 3393 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 3403 -1400 1801 -1400 1795 -3000 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 3803 -4446 1433 -3026 3349 -3164 3173 -3188 3189 -3198 3197 -3194 3395 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3401 -2998 3397 -2994 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -2994 3401 -1402 1799 -3004 3393 -1400 1797 -2990 1801 -1400 3403 -3002 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 +RAW_Data: 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1801 -1400 3393 -7770 21481 -2430 3819 -2810 3401 -3002 3401 -3002 3403 -3002 3401 -3002 3393 -3002 3407 -2998 3393 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -3000 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -3002 3395 -1400 1801 -2994 1801 -1400 3395 -3002 3401 -1402 1795 -1394 1795 -3004 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 3403 -1400 1801 -1396 1797 -3002 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3795 -3852 2121 -3066 3329 -3156 3167 -3174 3189 -3192 3393 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -3004 3401 -1400 1801 -3002 3393 -1402 1801 -2994 1799 -1402 3401 -1398 1795 -2996 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1795 -1396 3397 -1400 1801 -7618 +RAW_Data: 15035 -2418 3813 -2810 3403 -3002 3401 -3002 3401 -2996 3401 -3002 3393 -3002 3403 -3002 3393 -2994 3401 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -2996 1805 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -1402 1795 -2992 3401 -1402 1799 -3002 1801 -1402 3393 -1400 1801 -2994 1801 -1400 3403 -2994 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 3401 -3002 1795 -1394 3399 -4894 8261 -2584 3593 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -2996 3405 -3006 3393 -2994 3395 -3002 3401 -2994 3403 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3401 -1398 1791 -2998 3403 -1400 1801 -2998 1805 -1396 3395 -1404 1795 -2992 3401 -3006 1805 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1396 1797 -1400 1801 -3002 1795 -1394 1797 -1402 3401 -3006 3397 -7586 1729 -3040 3443 -3022 3417 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3000 3405 -2994 3401 -2994 3403 -2998 3205 -3198 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1396 3395 -3006 3393 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1394 3399 -1400 1801 -3002 3401 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -1400 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 3403 -1400 1801 -1400 1801 -3002 1801 -1396 1793 -1398 3401 -1400 1801 -3002 3803 -3690 8945 -2004 3805 -2802 3407 -3006 3401 -3002 3393 -3002 3395 -3002 3401 -2998 3211 -3198 3393 -2994 3395 -3002 3401 -3002 3393 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -3008 1803 -1398 1791 -1394 1797 -1400 3401 -3004 3401 -1400 1801 -3002 3393 -1402 1801 -2994 1799 -1402 3401 -1398 1797 -1396 1797 -3002 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 3401 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1396 3399 -1400 1801 -1400 1801 -8146 15111 -2438 3821 -2814 3409 -3004 3401 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3407 -1404 1597 -1592 1793 -1398 1799 -1402 1799 -1402 1801 -2994 1801 -1400 1801 -1396 1797 -1400 3407 -3006 3393 -1398 1791 -3000 3401 -1400 1801 -2994 1801 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -3004 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1400 1795 -1394 1797 -3002 1801 -1400 3407 -3006 1595 -1592 1789 -1398 3603 -4270 1767 -2816 3481 -2910 3319 -3118 3293 -3244 3229 -3014 3409 -3006 3407 -3002 3407 -3004 3395 -2994 3401 -2994 3401 -2996 3401 -3002 3399 -1404 +RAW_Data: 1595 -1594 1797 -1400 1801 -1400 1797 -1398 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3006 3207 -1596 1791 -2992 3401 -1402 1801 -3002 1795 -1398 3401 -1402 1795 -1398 1801 -1400 1801 -3002 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 3401 -1402 1795 -1394 1797 -3002 1801 -1400 3407 -3004 1597 -1594 3393 -8044 8285 -2600 3601 -3004 3401 -2994 3401 -2994 3403 -3002 3407 -2996 3393 -3004 3393 -3002 3399 -3004 3395 -2994 3401 -3002 3399 -1404 1597 -1592 1793 -1398 1799 -1402 1799 -1402 1801 -3002 1799 -1398 1797 -1396 1797 -1400 3407 -3006 3393 -1398 1791 -3000 3405 -1404 1597 -3190 1801 -1400 3403 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1396 1791 -1400 1799 -3002 1801 -1400 3403 -3002 3393 -3002 4003 -3520 1767 -3000 3331 -3084 3243 -3026 3409 -3002 3407 -3010 3405 -2998 3397 -2996 3401 -2994 3401 -2994 3403 -3002 3401 -2994 3403 -2994 3401 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -2994 3407 -1404 1793 -2988 3403 -3002 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 +RAW_Data: 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -3004 1799 -1402 3393 -3002 3393 -1402 1801 -4796 787 -2262 1869 -2942 3419 -3010 3405 -3006 3393 -3004 3393 -3002 3401 -2994 3403 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -3002 3399 -3010 3193 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -3000 3401 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1400 1797 -1394 3397 -3002 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 3401 -1398 1791 -1398 1801 -3002 1799 -1402 3393 -1402 1799 -3004 1799 -1398 4199 -2960 2227 -2872 3355 -3174 3183 -3186 3389 -2994 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3395 -3002 1795 -1398 1801 -1400 3407 -2998 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -3004 1799 -1398 3389 -1402 1799 -3002 3403 -7954 27839 -2554 3573 -2988 3389 -2994 3393 -3002 3395 -3002 3399 -3004 3395 -2994 3393 -3002 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1400 1801 -3002 3395 -1400 +RAW_Data: 1801 -2994 3401 -3002 1797 -1394 1795 -1402 3401 -1402 1799 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -3002 1801 -1400 3401 -1398 1797 -1400 1801 -3002 3393 -4822 15921 -1406 3809 -2610 3601 -3002 3403 -3002 3401 -3002 3401 -3004 3393 -2994 3407 -3004 3395 -2994 3407 -2996 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3399 -1404 1595 -3190 3403 -1400 1801 -3002 3393 -3006 1601 -1594 3389 -3002 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -1396 1791 -1398 1801 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -1402 1799 -6668 1963 -2566 3673 -2838 3421 -3014 3411 -3006 3393 -3002 3393 -3004 3401 -2994 3407 -2996 3395 -3002 3393 -3002 3395 -3002 3405 -2998 3399 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -3002 3393 -3002 1797 -1394 3397 -3002 3401 -3002 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 3407 -1400 1791 -1394 1797 -3002 3401 -3002 1797 -1394 1795 -1402 1801 -1400 3803 -4242 1899 -2672 3763 -2746 3509 -2864 3439 -3026 3417 -3002 3401 -3008 3405 -3002 3393 -2994 3403 -3002 3405 -2998 3393 -2996 3401 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1797 -1394 1795 -1402 1801 -1400 3401 -3002 3403 -1396 1793 -2998 3401 -1398 1797 -3002 3401 -3002 1795 -1394 3399 -1400 1801 -3002 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 3401 -1398 1791 -1398 1801 -3002 3401 -3002 1795 -1394 1797 -1402 3401 -7512 21165 -2482 3841 -2832 3413 -3010 3401 -3002 3403 -3002 3401 -3002 3407 -2998 3393 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1795 -1398 1801 -1400 3403 -2994 3401 -1402 1799 -3002 3395 -1400 1797 -2990 3401 -3004 1799 -1402 3401 -1398 1791 -1398 1799 -3004 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1795 -1398 3403 -1400 1801 -1400 1797 -2998 3395 -3002 1799 -1402 3401 -3002 3399 -3900 661 -442 1987 -2846 3333 -3156 3167 -3182 3185 -3192 3393 -3002 3393 -3004 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3401 -2996 3393 -3002 3401 -1402 1795 -1398 1795 -1398 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -2994 3401 -1402 1801 -2994 3407 -3004 3193 -3196 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 3407 -1400 1789 -1392 1797 -3002 3401 -3002 1801 -1402 3393 -1400 1801 -5854 1449 -3028 3481 -2910 3519 -2928 3493 -2856 3433 -3014 3415 -3010 3405 -2998 3397 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -1404 1595 -1588 1795 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1402 1801 -1396 1791 -1398 3403 -3002 3401 -1402 1799 -2994 3403 -1400 1801 -2994 3393 -3002 3403 -3002 1795 -1394 3397 -3002 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -1400 1801 -2994 3401 -2996 3401 -3002 1801 -1396 4197 -2116 2985 -3040 3495 -2924 3333 -3160 3173 -3184 3189 -3198 3189 -3196 3393 -3002 3393 -3002 3407 -2998 3393 -3002 3395 -3002 3401 -2994 3393 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1400 1801 -3002 3395 -1400 1801 -2994 3407 -2996 3395 -3002 3401 -2994 1801 -1396 1797 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 3395 -1400 1801 -1400 1795 -2992 3401 -3006 +RAW_Data: 3207 -3194 3407 -3798 1471 -2756 1731 -2764 3451 -3026 3423 -3014 3401 -3002 3393 -3008 3401 -2998 3393 -3002 3403 -2994 3401 -2994 3395 -3002 3401 -3002 3393 -3002 3395 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3405 -2998 3393 -1398 1797 -3002 3393 -1400 1801 -3002 3395 -3002 3401 -2998 3209 -1602 1591 -3184 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 3401 -1400 1801 -1398 1791 -3000 3401 -3002 3401 -1398 1795 -3002 3603 -3528 2573 -2892 3567 -2982 3387 -2990 3393 -2994 3407 -3006 3393 -2994 3401 -2996 3393 -3002 3401 -3002 3403 -2994 3401 -2994 3399 -3006 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1795 -1394 3403 -3004 3401 -1398 1791 -3000 3401 -1402 1795 -3000 3401 -3006 3403 -1400 1791 -2992 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 3401 -1398 1795 -1402 1801 -3006 3197 -3194 3393 -1402 1801 -1400 1801 -7732 2037 -2694 3601 -2994 3401 -2994 3403 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -3002 3399 -3004 3399 -2998 3399 -2996 3395 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1396 3399 -3002 3393 -1402 1799 -3002 3395 -1396 1797 -3006 3397 -3002 3395 -1400 1801 -3002 3393 -3002 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 +RAW_Data: 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1400 3403 -1396 1797 -1400 1801 -3002 3393 -1402 1795 -3004 1803 -1398 1791 -1398 3403 -4578 1767 -2596 3785 -2526 3699 -2874 3441 -3022 3419 -3010 3401 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3407 -1404 1595 -3192 3401 -1396 1793 -2998 3403 -3002 3393 -1402 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1396 1797 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1398 3401 -7634 8423 -2600 3601 -3002 3403 -3002 3401 -3002 3403 -2994 3401 -3002 3393 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1402 1799 -1398 1791 -1398 3403 -3002 3401 -1396 1793 -2998 3403 -1400 1801 -3002 3407 -2996 3395 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 3401 -1402 1795 -1398 1801 -3002 3401 -1398 1795 -3004 3401 -2994 3601 -5084 8583 -2458 3835 -2822 3413 -3010 3403 -3002 3401 -3002 3395 -3002 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3403 -3002 +RAW_Data: 3393 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3407 -1404 1595 -1594 1791 -3000 3405 -1404 1597 -3190 3407 -1404 1597 -7988 1673 -3042 3323 -3148 3363 -2976 3185 -3194 3399 -2998 3393 -3002 3401 -2994 3395 -3006 3397 -2994 3403 -2994 3407 -3004 3393 -2996 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 3403 -3002 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1398 1791 -3000 3401 -1400 1801 -1400 1801 -3002 1795 -1396 3597 -4754 1619 -2850 3567 -2982 3389 -2990 3395 -3002 3393 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -3002 3407 -2996 3395 -2998 3397 -2994 3403 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1398 1791 -2998 3403 -1400 1801 -2994 3401 -1402 1795 -2992 1799 -1402 3401 -3002 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 +RAW_Data: 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -1402 1795 -1398 1801 -3002 3401 -7874 2005 -2696 3647 -2838 3423 -3002 3401 -3002 3401 -3004 3401 -3006 3397 -3002 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3407 -2998 3397 -1400 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1398 3401 -3006 3393 -1398 1791 -3000 3401 -1402 1799 -2994 3403 -1400 1795 -2992 1801 -1400 3401 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 3401 -1396 1797 -1402 1799 -2994 3403 -1400 1795 -1396 1795 -1402 1799 -3004 3801 -4428 14941 -2434 3819 -2810 3415 -3004 3403 -3002 3405 -2998 3393 -3000 3397 -3002 3393 -3002 3395 -3002 3393 -3002 3403 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -3004 1803 -1398 1791 -1398 1801 -1400 3407 -2998 3393 -1402 1799 -3002 3395 -1400 1801 -2994 3401 -1398 1797 -3002 3401 -2998 1805 -1396 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 3395 -1400 1801 -1400 1801 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -7354 2019 -2886 3413 -3002 3401 -3002 3403 -3002 3401 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3407 -2996 3395 -2994 3393 -3002 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 3401 -3004 +RAW_Data: 3205 -1600 1591 -3188 3403 -1400 1801 -3002 3407 -1400 1791 -2992 3401 -3002 3401 -2994 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 3401 -1402 1801 -1396 1791 -1398 1801 -3006 1805 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -1476 3223 -2870 3519 -2934 3559 -2974 3187 -3190 3389 -2994 3395 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -2996 3401 -2994 3407 -3004 3395 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -3000 1801 -1400 1801 -1396 1797 -1400 3403 -3002 3401 -1398 1791 -3000 3401 -1400 1801 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -3002 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -1400 1801 -1396 1793 -1398 1799 -3002 1801 -1402 1799 -1402 1795 -1394 3397 -7212 2943 -1710 3963 -2582 3587 -2990 3389 -2994 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3401 -3002 3399 -3006 3393 -2994 3401 -2996 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1396 1797 -1402 3401 -3002 3393 -1402 1801 -2994 3401 -1402 1795 -2990 3403 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -1400 1801 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 3401 -3002 3795 -412 421 -3620 8315 -2598 3797 -2810 3401 -3002 3403 -3002 3401 -2994 3403 -3002 3393 -3002 3393 -3002 3395 -3002 3407 -2996 3395 -3002 3393 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1799 -1402 1801 -1400 3395 -1400 1801 -7154 1989 -2800 3335 -3110 3489 -3052 3239 -3024 3411 -3006 3393 -3002 3401 -3002 3395 -3006 3397 -2994 3407 -3002 3197 -3194 3395 -3002 3393 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1396 1797 -3006 1605 -1596 3391 -3002 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1797 -1398 1799 -1402 3401 -1402 1795 -1394 1795 -1402 1801 -3002 1799 -1402 3401 -2994 1801 -1402 3393 -4578 1987 -2610 3585 -2746 3685 -2908 3493 -2922 3337 -3160 3375 -2982 3389 -2990 3395 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -3002 3395 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -3006 1805 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1398 1791 -3004 3405 -1396 1793 -2998 3403 -1400 1801 -1396 +RAW_Data: 1793 -2998 3401 -3004 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 3401 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 3401 -2994 3401 -7890 1857 -2976 3401 -3002 3401 -3002 3403 -2994 3401 -3006 3397 -2996 3405 -3002 3197 -3194 3395 -3002 3205 -3198 3395 -2994 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -2996 1799 -1402 1799 -1402 1795 -1394 3403 -3006 3393 -1402 1799 -2998 3207 -1596 1791 -2996 3405 -1402 1795 -1398 1801 -3006 3397 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -1400 1801 -1400 1797 -1394 1795 -3002 1801 -1402 3401 -1400 1801 -3006 3397 -4660 9619 -1422 3829 -2818 3415 -3014 3405 -3002 3401 -3002 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3004 3401 -3002 3393 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -3002 1801 -1398 1795 -1402 1801 -1400 3393 -3002 3403 -1396 1793 -2998 3401 -1402 1801 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -3006 1803 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -4378 191 -1688 551 -1116 1659 -3106 3309 -3094 3259 -3038 3421 -3012 3405 -3006 3401 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -3002 1801 -1402 1795 -1394 1795 -1402 3401 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1801 -3002 3401 -2994 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -3002 3393 -3002 1797 -1394 1795 -1402 3401 -4704 2681 -1944 3813 -2610 3603 -3002 3401 -3002 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3403 -3002 3393 -3002 3401 -2998 3399 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -3000 1801 -1400 1795 -1398 1801 -1402 3401 -2994 3401 -1402 1799 -2996 3401 -1400 1797 -2990 3407 -1404 1801 -1396 1793 -1398 1799 -1402 1799 -3004 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 3397 -1400 1801 -1402 1795 -1394 1797 -3002 3401 -3002 1801 -1400 3395 -8456 8271 -2610 3601 -3002 3401 -3002 3403 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -3006 3399 -2994 3401 -2994 3403 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -3002 1805 -1396 1797 -1402 1799 -1402 3401 -2998 3205 -1598 1791 -2992 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 +RAW_Data: 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -3002 3393 -3002 3395 -3002 3601 -3880 2553 -2922 3559 -2976 3381 -2988 3389 -2994 3401 -3004 3393 -2994 3401 -3002 3203 -3198 3397 -2994 3403 -2994 3401 -3002 3407 -2996 3395 -1396 1797 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1793 -1398 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -1402 1799 -3006 1601 -1594 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 3389 -1400 1801 -1402 1799 -1402 1799 -3004 3393 -3002 3393 -1402 1801 -4436 421 -3014 1755 -3064 3393 -2996 3401 -2998 3205 -3194 3403 -2998 3397 -2994 3403 -3002 3393 -2994 3401 -3002 3395 -3002 3401 -2994 3403 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 +RAW_Data: 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 3401 -1402 1799 -1402 1799 -1402 1801 -2994 3401 -1398 1795 -3006 1605 -1598 4387 -1926 735 -564 1715 -3138 3393 -2994 3403 -3006 3197 -3194 3393 -3004 3401 -3002 3393 -3002 3395 -2994 3401 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -3002 1801 -1400 1801 -1400 1801 -1402 3393 -3006 3205 -1598 1791 -2992 3401 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 3401 -1400 1801 -1398 1791 -1398 1799 -3004 3401 -1400 1797 -2998 3401 -7510 1767 -2584 3763 -2696 3659 -2834 3419 -3010 3409 -3002 3401 -3002 3403 -3006 3397 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -2990 1801 -1402 1799 -1402 1799 -1402 3407 -3004 3195 -1596 1791 -2992 3401 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1400 1795 -1398 3403 -1400 1801 -2994 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 3401 -1402 1799 -1402 1795 -1394 1797 -3002 3401 -1402 1799 -1402 1795 -2992 3601 -4408 9397 -1404 3805 -2602 3601 -3002 3401 -3006 3407 -2998 3405 -2994 3395 -3006 3397 -2994 3395 -3002 3401 -2994 3401 -2994 3403 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1402 3393 -3006 3397 -1402 1795 -2992 3401 -1400 1801 -1402 1795 -2998 1801 -1402 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 3401 -1396 1797 -1402 1799 -1402 1799 -2996 3401 -1400 1797 -1394 1795 -1402 1799 -7782 2983 -1722 3737 -2764 3575 -2986 3389 -2994 3395 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3401 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -2994 1801 -1400 1801 -1402 1795 -1394 3397 -3002 3403 -1400 1795 -3004 3205 -1598 1791 -1394 1797 -3002 1801 -1400 1801 -1400 3401 -3004 3393 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -3002 1801 -1400 1801 -1402 1799 -1398 3789 -3630 9707 -1196 3995 -2602 3601 -3004 3401 -3002 3393 -3002 3403 -2994 3407 -3004 3193 -3200 3397 -2994 3401 -2996 3401 -3002 3393 -1402 1799 -1402 1795 -1398 1795 -1394 1797 -1402 1799 -3002 1801 -1402 1799 -1402 1799 -1398 3397 -3002 3407 -1400 1791 -2992 3401 -1402 1801 -1400 1795 -3000 1801 -1400 1795 -1398 3403 -1400 1795 -2992 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 +RAW_Data: 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 3401 -7046 2191 -2780 3479 -2930 3487 -3108 3315 -2934 3349 -3166 3383 -2990 3389 -2994 3395 -3002 3401 -2998 3399 -2994 3401 -3002 3393 -3004 3393 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2996 3409 -1400 1789 -1394 1795 -3004 1799 -1402 1799 -1402 3407 -1404 1595 -1594 1791 -3000 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 3393 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -3002 1801 -1400 3401 -2996 3401 -5646 14913 -2416 3815 -2810 3401 -3002 3401 -3006 3407 -2994 3401 -2998 3205 -3196 3201 -3194 3393 -3008 3205 -3194 3393 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -3004 1799 -1402 1799 -1402 1795 -1398 3401 -3002 3395 -1400 1797 -2998 3401 -1402 1801 -1396 1791 -3000 1801 -1400 3401 -3004 1795 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 3395 -1400 1801 -6192 2021 -2460 3747 -2764 3583 -2986 3389 -2994 3395 -3002 3393 -3002 3403 -2994 3405 -3002 3197 -3196 3393 -3002 3393 -3002 3403 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -3002 1795 -1396 1795 -1402 1799 -1402 3401 -3002 3395 -1400 1801 -3002 3393 -1402 1795 -1398 1801 -3002 1795 -1398 3401 -3002 1797 -1398 3401 -3002 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 +RAW_Data: 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3401 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -3002 3401 -3002 1797 -1398 3601 -4152 2483 -2464 3741 -2764 3575 -2986 3385 -2994 3395 -3002 3393 -3002 3395 -3002 3401 -2994 3401 -2996 3401 -3002 3393 -3002 3403 -3002 3393 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3006 1601 -1592 1793 -1394 1795 -1402 3401 -3002 3403 -1400 1795 -3000 3401 -1398 1795 -1402 1801 -3006 1599 -1594 3389 -3002 3403 -3002 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1402 1799 -1402 3393 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -3002 3393 -3002 3403 -5836 199 -800 2239 -2970 3487 -2904 3313 -3324 3147 -3168 3173 -3188 3389 -2998 3397 -2996 3401 -2994 3401 -3002 3403 -2994 3393 -3002 3401 -3000 3205 -1596 1793 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -3002 1801 -1396 1793 -1398 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -1398 1795 -3004 1799 -1402 3393 -3006 3205 -1598 1791 -2992 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 3401 -1402 1799 -1402 1799 -1402 1795 -1394 +RAW_Data: 1797 -3002 3401 -1402 1799 -3004 4001 -1452 1311 -1732 1679 -2834 3579 -2986 3395 -2998 3189 -3198 3199 -3194 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3403 -2994 3401 -2994 3403 -2994 3401 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1402 1799 -1402 1799 -3000 1799 -1394 3397 -1402 1799 -3002 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 3389 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -7878 14949 -2414 3809 -2802 3401 -3004 3401 -3002 3401 -3002 3395 -2994 3401 -3002 3393 -3004 3401 -2998 3205 -3194 3395 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3403 -1404 1595 -3188 3401 -1400 1801 -1398 1791 -2998 1801 -1402 3401 -1400 1801 -2994 3403 -2994 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 3401 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1396 1797 -1400 3603 -4378 1987 -2808 3349 -2882 3487 -3116 3325 -2936 3561 -2974 3385 -2992 3389 -2994 3393 -3004 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1400 3395 -3002 3401 -1400 1797 -2990 3403 -1400 1801 -1400 1801 -3006 1601 -1592 3391 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 +RAW_Data: 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 3399 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -3004 1803 -1398 3389 -7766 3599 -1180 3997 -2602 3601 -3002 3401 -3002 3403 -2994 3401 -3002 3401 -2996 3393 -3006 3397 -2994 3403 -3002 3401 -2994 3403 -2994 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -2994 1801 -1400 1801 -1398 1791 -1398 3401 -3002 3403 -1400 1801 -2994 3401 -1398 1791 -1398 1801 -3002 1799 -1402 3401 -1402 1799 -1398 1791 -1398 1801 -3002 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2990 3401 -3004 3801 -4548 2039 -2540 3875 -2646 3423 -3026 3413 -3006 3405 -2996 3401 -2994 3401 -3002 3395 -3002 3393 -3002 3407 -2998 3393 -2994 3401 -3000 3205 -1596 1793 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1400 1799 -1402 3401 -3002 3393 -1402 1801 -3002 3393 -1402 1799 -1402 1799 -2996 3401 -2994 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -3002 3407 -1404 +RAW_Data: 1595 -7378 2189 -2976 3371 -2942 3489 -2908 3291 -3120 3341 -3164 3173 -3188 3189 -3190 3393 -3006 3399 -2998 3205 -3194 3395 -2994 3401 -3006 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -3002 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1402 1795 -2994 3407 -1396 1791 -1398 1801 -3002 3403 -2994 1799 -1402 1801 -1400 3401 -2994 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -2994 1801 -1400 3401 -4138 9989 -410 4445 -2630 3617 -2814 3415 -3006 3401 -3002 3401 -3004 3393 -3002 3393 -3002 3403 -2994 3401 -3002 3395 -3006 3197 -1598 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -3006 1605 -1596 1793 -1394 1795 -1402 3401 -3002 3401 -1398 1791 -3000 3401 -1402 1799 -1402 1801 -3002 3393 -3002 1795 -1394 3399 -3002 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -3002 3395 -7518 2025 -2782 3651 -2826 3417 -3010 3407 -3010 3409 -2998 3389 -2996 3397 -3010 3397 -2994 3203 -3194 3393 -3002 3395 -3002 3401 -2994 3401 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1396 3389 -3004 3401 -1400 1801 -3006 3397 -1398 1791 -1398 1801 -3002 3401 -2994 1801 -1402 3401 -1400 1597 -3190 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 +RAW_Data: 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -2994 4001 -3412 1945 -2826 3517 -2924 3347 -3164 3381 -2988 3389 -2994 3393 -2996 3401 -3002 3393 -3002 3403 -2994 3401 -2994 3401 -3004 3393 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -2992 1799 -1402 1799 -1402 1801 -1400 3393 -3004 3401 -1396 1793 -3002 3405 -1398 1797 -1400 1801 -3002 3393 -3006 3399 -2994 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1394 3397 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 3601 -1280 5101 -3078 3271 -3238 3223 -3014 3409 -3006 3405 -2996 3401 -3002 3401 -2994 3395 -3002 3401 -3002 3393 -3004 3393 -3002 3393 -3002 3395 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1801 -1400 1801 -1396 1791 -1400 3401 -3002 3401 -1402 1799 -3002 3395 -1400 1797 -1394 1795 -3002 3403 -3002 3393 -3006 3205 -3196 1595 -1594 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 3395 -3002 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 3401 -7728 1751 -3100 3263 -3030 3413 -3014 3415 -3006 3209 -3202 +RAW_Data: 3193 -3200 3197 -3194 3393 -2998 3407 -2994 3393 -3002 3403 -2994 3401 -3002 3393 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -3002 1799 -1402 1801 -1400 1801 -1396 3391 -3002 3401 -1400 1801 -3002 3403 -1396 1791 -1398 1801 -3002 3401 -2996 3401 -1400 1797 -2990 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 3401 -3004 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1797 -1398 3401 -3002 4405 -2758 2807 -2942 3325 -2938 3367 -3180 3379 -2990 3189 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2998 3205 -3196 3393 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -3004 1799 -1402 1799 -1402 1801 -1396 3397 -3004 3393 -1400 1801 -3002 3395 -1400 1801 -1400 1795 -2996 3405 -2996 3401 -1400 1801 -1402 1795 -2990 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1398 1801 -1400 3401 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 3401 -1400 1801 -5956 861 -1316 1441 -3046 3355 -3176 3381 -2988 3389 -2994 3201 -3196 3393 -3002 3401 -2994 3403 -2994 3401 -3006 3397 -2996 3401 -2994 3401 -2994 3403 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -3002 1801 -1400 1801 -1400 1801 -1398 3389 -3002 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -3002 3403 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 +RAW_Data: 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1398 3403 -3002 1801 -1400 1801 -1400 1795 -1396 1795 -1402 3401 -3002 1801 -1396 3991 -4480 15153 -2008 3809 -2602 3603 -3002 3401 -3002 3407 -3006 3393 -2994 3393 -3004 3405 -2998 3393 -3002 3395 -3002 3393 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -2994 1805 -1396 1797 -1402 1795 -1398 3401 -3002 3395 -1400 1801 -3006 3397 -1398 1791 -1398 1801 -3002 3401 -1398 1795 -3002 1801 -1402 3401 -3002 1597 -1592 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1597 -1592 1797 -1402 1799 -1402 3401 -3002 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3002 3395 -7734 2455 -2034 3851 -2834 3419 -3014 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3403 -2994 3401 -3002 3393 -2996 3401 -3002 3407 -2996 3399 -1404 1795 -1392 1795 -1402 1799 -1398 1797 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1398 1791 -3000 3401 -1400 1801 -1398 1791 -2998 3403 -1400 1801 -3002 3401 -2994 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 3403 -3002 1795 -1394 1797 -1400 1801 -1402 1799 -1402 3401 -1398 1791 -3002 3607 -3594 2481 -2584 3775 -2732 3541 -2914 3491 -2916 3333 -3160 3375 -2982 3389 -2992 3393 -2994 3401 -3004 3393 -3002 3401 -2994 3395 -3002 3401 -1402 +RAW_Data: 1795 -1394 1797 -1400 1801 -1396 1797 -1400 1801 -3002 1801 -1400 1801 -1398 1795 -1402 3401 -3002 3395 -1400 1801 -2994 3401 -1402 1799 -1398 1791 -3000 3401 -1402 1799 -3002 3395 -1400 1801 -3002 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -1402 1801 -1400 1801 -7478 1559 -3038 3563 -2982 3391 -2992 3395 -3002 3399 -2996 3391 -2994 3393 -2994 3403 -3002 3407 -3004 3393 -2996 3393 -3002 3401 -2994 3403 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -2994 1801 -1400 1801 -1396 1797 -1402 3401 -2994 3401 -1402 1799 -2996 3401 -1400 1801 -1402 1795 -2990 3407 -1404 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 3403 -2998 1603 -1598 1791 -1394 1797 -1402 3401 -3006 1605 -1596 1791 -1394 3399 -4468 1663 -3040 3363 -2940 3339 -3112 3293 -3122 3329 -3156 3367 -2982 3387 -2990 3393 -2994 3403 -3002 3401 -2994 3401 -3002 3395 -2994 3401 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -3002 1801 -1398 1791 -1398 1801 -1400 3401 -3006 3207 -1594 1789 -2992 3401 -1402 1801 -1400 1801 -2994 3401 -1402 1799 -1398 1797 -3002 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 3401 -3002 1801 -1398 1795 -1402 1799 -1398 3403 -3004 1597 -1594 3389 -3402 1499 -440 419 -600 599 -766 1693 -3100 3401 -3002 3395 -2994 3401 -3002 3395 -3002 3393 -3006 3205 -3200 3197 -3194 3201 -3194 3403 -2994 3401 -3002 3395 -3006 3397 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3393 -3002 3401 -1398 1797 -3002 3401 -1398 1795 -1398 1797 -3002 3401 -1400 1797 -1398 1799 -1402 1801 -2994 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 3401 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3401 -2996 4203 -806 201 -2874 1835 -2990 3405 -2996 3393 -3002 3393 -3004 3405 -2998 3393 -2994 3403 -2994 3401 -2994 3403 -3002 3401 -2994 3401 -2994 3403 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -3002 1801 -1398 1795 -1402 1799 -1398 3397 -3002 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -3006 1605 -1596 1793 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 3401 -3002 1801 -1402 1799 -1402 1799 -1402 3393 -3002 3395 -1400 1801 -6000 21381 -2268 3645 -2836 3417 -3010 3407 -3008 3403 -2998 3399 -2996 3395 -2994 3401 -2994 3401 -2996 3401 -1400 1801 -1402 1795 -1398 1799 -1402 1795 -1394 1797 -3002 1801 -1400 +RAW_Data: 1801 -1400 1801 -1398 3397 -3002 3401 -1398 1791 -3000 3405 -1406 1795 -1394 1795 -1402 1801 -3006 1599 -1594 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 3401 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -1398 1795 -3002 1801 -1402 3601 -4730 8239 -2608 3611 -3002 3401 -3002 3401 -3002 3395 -3002 3401 -2998 3399 -3002 3393 -3002 3401 -2996 3401 -2994 3407 -2996 3399 -1404 1795 -1396 1791 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3006 3397 -1398 1791 -3000 3401 -1400 1801 -1398 1795 -1402 1801 -3002 1795 -1398 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 3401 -2994 1801 -1400 1801 -1396 1793 -1398 3401 -1400 1801 -3006 3397 -7406 3789 -200 4429 -2816 3413 -3014 3407 -3002 3407 -3004 3393 -3000 3397 -2994 3401 -2996 3401 -3002 3399 -3004 3395 -2994 3401 -2994 3401 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -3002 3393 -1402 1799 -3002 3395 -1400 1801 -1396 1793 -1398 1799 -3006 1805 -1398 1791 -1398 3401 -3002 1801 -1396 1797 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 +RAW_Data: 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 3403 -2994 1799 -1402 1801 -1396 1797 -1400 3403 -1400 1795 -1394 1797 -3002 3401 -4904 1617 -2646 3741 -2764 3575 -2986 3385 -2994 3395 -3002 3393 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -3002 3403 -2994 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -2994 3401 -1402 1801 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -3002 1801 -1400 1801 -1396 3399 -1400 1801 -3002 1595 -1594 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 3401 -3002 1801 -1400 1801 -1400 1797 -1394 3397 -1402 1799 -1402 1799 -1402 1795 -6010 3547 -1150 3953 -2576 3585 -2990 3395 -2994 3393 -3002 3403 -3002 3393 -3002 3407 -2996 3395 -2994 3401 -3002 3395 -3002 3401 -2994 3393 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -3002 1799 -1402 1795 -1398 1801 -1400 3403 -2994 3401 -1400 1797 -2998 3407 -1404 1597 -1592 1793 -1398 1799 -3002 1801 -1402 3401 -2998 1605 -1596 1791 -1398 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3403 -2994 1799 -1402 1801 -1400 3401 -2998 1805 -1398 1791 -1398 1801 -1400 3601 -4812 1887 -2884 3391 -2994 3393 -2994 3395 -2994 3401 -3002 3393 -3004 3401 -2994 3401 -3002 3395 -3002 3201 -3198 3205 -3196 3393 -2998 3205 -1598 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1398 1799 -1402 1801 -1400 3393 -3004 +RAW_Data: 3393 -1400 1801 -3002 3395 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 3403 -2994 3401 -3002 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 3401 -3002 1797 -1394 1795 -1402 3401 -3002 1801 -1398 1791 -1398 3401 -4786 197 -1694 1655 -3046 3275 -3160 3351 -3136 3317 -2902 3311 -3324 3147 -3168 3181 -3186 3191 -3194 3393 -3002 3401 -2996 3401 -3002 3393 -3002 3395 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3401 -3002 3401 -1398 1791 -3004 3405 -1396 1793 -1398 1799 -1402 1801 -3002 1799 -1398 3397 -1402 1799 -2994 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1400 3401 -2998 1805 -1396 1791 -1394 3399 -3002 1801 -1400 3401 -3002 4219 -3452 1989 -2654 3675 -2776 3467 -2882 3487 -3110 3319 -2932 3561 -2974 3381 -2992 3393 -2998 3393 -2996 3393 -3002 3401 -3002 3395 -3002 3393 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1402 3401 -2994 3403 -1400 1795 -2992 3401 -1402 1799 -1402 1801 -1400 1795 -2992 1801 -1396 3397 -1402 1801 -1400 1801 -3002 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 +RAW_Data: 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 3401 -2998 1805 -1400 1797 -1394 3397 -3002 1801 -1396 3399 -1400 1801 -7622 2021 -2904 3499 -2872 3443 -3018 3413 -3006 3411 -3006 3397 -3006 3393 -2994 3403 -2994 3401 -3002 3395 -3002 3393 -3002 3401 -2994 3403 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -2998 1605 -1596 1791 -1394 1797 -1402 3401 -3002 3401 -1402 1799 -3004 3393 -1396 1797 -1402 1799 -1402 1801 -2994 3401 -3002 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 3397 -3002 1801 -1396 1797 -1400 3403 -3002 3393 -3002 1801 -1396 4405 -3580 2203 -2680 3433 -3018 3415 -3006 3401 -3002 3401 -3002 3395 -3002 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3203 -3194 3399 -3004 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -3002 1795 -1394 1797 -1400 1801 -1400 3403 -2998 3205 -1598 1791 -2998 3403 -1400 1801 -1396 1793 -1398 1799 -3002 3403 -3006 1599 -1592 3401 -3004 1597 -1594 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 3391 -3002 1799 -1402 1801 -1400 3401 -2996 3401 -2994 3401 -7234 3089 -2524 3707 -2718 3733 -2896 3467 -2840 3421 -3020 3405 -3002 3401 -3002 3403 -3002 3393 -3002 3407 -2998 3393 -3002 3399 -2996 3399 -1404 1795 -1394 1793 -1398 1799 -1402 1801 -1400 1801 -3002 1801 -1400 1795 -1394 1793 -1398 3401 -3002 3401 -1402 1799 -3004 3401 -1396 1793 -1398 1799 -1402 1799 -3004 3401 -2994 3401 -2998 1805 -1398 +RAW_Data: 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1595 -1594 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1400 3401 -3006 1599 -1594 1791 -1398 3403 -3002 3401 -1398 1791 -2998 4003 -4646 1945 -2714 3615 -2814 3409 -3002 3407 -3004 3395 -3006 3397 -2994 3395 -3002 3401 -3002 3393 -3004 3401 -2994 3393 -3002 3403 -2994 3401 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -3002 1795 -1398 1801 -1402 1799 -1402 3401 -2994 3395 -1400 1801 -3002 3393 -1402 1795 -1398 1801 -1400 1801 -2990 3401 -3002 3403 -1400 1801 -3002 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 3401 -3002 1801 -1396 1797 -1402 3401 -3002 3393 -1398 1797 -1400 1801 -7714 15297 -1990 4001 -2594 3603 -2994 3401 -3002 3393 -3004 3393 -3002 3401 -3002 3395 -3002 3393 -2994 3407 -3006 3393 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -2998 1801 -1396 1797 -1402 1799 -1402 3401 -2994 3401 -1402 1795 -3000 3401 -1402 1795 -1398 1799 -1398 1797 -3002 3401 -1402 1799 -2994 1801 -1402 1799 -1402 1595 -1594 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 +RAW_Data: 1795 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 3401 -3002 1801 -1396 1797 -1402 3401 -1396 1797 -3002 1801 -1400 1801 -1398 3389 -4226 1665 -3024 3337 -3164 3373 -2986 3387 -2994 3393 -3002 3395 -3002 3407 -2996 3395 -2994 3401 -3002 3393 -2996 3401 -3002 3401 -2998 3399 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 1799 -1398 1797 -1400 1801 -1400 3407 -3006 3193 -1598 1791 -3000 3401 -1396 1797 -1402 1799 -1402 1799 -3002 3395 -1396 1797 -3002 3401 -2996 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1597 -1592 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 3403 -2998 1603 -1598 1791 -1394 3399 -1400 1801 -3002 1801 -1400 3401 -7946 8393 -2610 3601 -3002 3403 -3002 3401 -3002 3393 -3004 3401 -3002 3393 -2994 3407 -3006 3393 -2994 3401 -2996 3401 -2994 3401 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3002 1801 -1398 1791 -1394 1797 -1400 3407 -3006 3401 -1396 1793 -2998 3407 -1404 1795 -1394 1797 -1400 1801 -3002 3403 -1396 1791 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1791 -1400 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1595 -1594 1795 -1402 3401 -1402 1799 -3004 3393 -3002 4001 -3612 15189 -2202 3801 -2602 3601 -3004 3401 -3002 3401 -2994 3403 -2994 3407 -2996 3399 -3010 3397 -2990 3393 -3004 3393 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1801 -1396 1791 -1398 1801 -1402 3401 -3002 3401 -1402 1795 -2992 3401 -1400 1801 -1402 1799 -1402 1795 -2992 3401 -1402 1799 -1402 1799 -1398 1791 -3000 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 +RAW_Data: 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3401 -3006 1601 -1596 1791 -1398 3403 -1400 1801 -3002 3393 -1402 1801 -4208 1657 -1960 1633 -3076 3349 -3166 3183 -3186 3189 -3194 3395 -3002 3401 -2994 3403 -2994 3401 -3002 3399 -3004 3395 -3002 3393 -2994 3403 -3002 3201 -1598 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1793 -2998 1801 -1400 1801 -1402 1799 -1402 3401 -2994 3403 -1396 1791 -3000 3401 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 3395 -3004 1601 -1596 1793 -1398 3401 -1400 1801 -1402 1799 -3002 1797 -1394 3797 -4466 8859 -2000 3797 -2810 3401 -3002 3401 -3002 3403 -3002 3393 -2994 3403 -3002 3401 -3002 3393 -3002 3407 -2998 3399 -2996 3395 -1396 1797 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -2998 3205 -1598 1791 -3000 3401 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -3002 1799 -1398 1791 -1398 3401 -3004 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 3403 -2994 1799 -1402 1795 -1394 +RAW_Data: 3399 -1400 1801 -1400 1801 -3002 3205 -4194 389 -3580 1837 -3274 3173 -3082 3345 -3118 3293 -3118 3317 -3142 3163 -3174 3183 -3190 3393 -2996 3401 -2994 3401 -3002 3395 -3002 3205 -3198 3393 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -2992 1799 -1402 1801 -1400 1801 -1400 3403 -2994 3401 -1400 1801 -3002 3395 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1398 3389 -3002 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1595 -1594 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 3401 -3004 1799 -1402 1795 -1398 3401 -1398 1797 -1400 1801 -1400 1801 -2994 4407 -3396 2177 -2946 3427 -3018 3407 -3004 3407 -3006 3389 -3002 3393 -3004 3393 -3002 3401 -2994 3403 -3002 3393 -3002 3395 -3002 3393 -3002 3393 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 1799 -2996 1799 -1402 1795 -1398 1801 -1400 3407 -3004 3395 -1396 1793 -2998 3407 -1400 1791 -1394 1797 -1400 1801 -1402 1799 -3002 1801 -1402 3401 -1396 1793 -2998 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3004 1799 -1402 1795 -1394 3397 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -7678 1989 -2426 3963 -2528 3683 -2904 3519 -2930 3347 -3168 3373 -2988 3389 -2994 3401 -2996 3401 -2994 3401 -3002 3395 -3002 3401 -2994 3401 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -3002 1799 -1402 1801 -1396 1797 -1400 3403 -3002 3393 -1400 1801 -2994 3403 -1400 1795 -1398 1801 -1402 1799 -1402 1795 -2992 3401 -3002 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 +RAW_Data: 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1599 -1598 1791 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1601 -1598 1791 -1398 1799 -1402 1801 -1396 3397 -3004 1799 -1402 3401 -2994 1801 -1400 1601 -1598 1791 -1398 1801 -1400 3401 -4800 1839 -2646 3737 -2764 3573 -2988 3385 -2994 3395 -3002 3393 -3002 3401 -2994 3403 -3002 3393 -2994 3407 -3006 3393 -2994 3401 -3004 3393 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1400 1801 -1402 3405 -3006 3389 -1398 1797 -3002 3405 -1402 1789 -1392 1797 -1400 1801 -1400 1801 -3002 3401 -3002 3395 -3002 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1795 -1400 1799 -1402 1799 -1402 1795 -1398 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1398 1801 -1402 3401 -2994 1801 -1400 3403 -3002 1595 -1594 1797 -1400 1801 -1400 3403 -7764 1463 -3208 3319 -2936 3563 -2976 3385 -2986 3391 -2994 3401 -2994 3401 -2996 3401 -3002 3401 -2994 3403 -2994 3401 -3002 3395 -3002 3401 -1396 1793 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -3002 1801 -1396 1797 -1402 1799 -1402 3405 -2998 3399 -1404 1597 -3190 3401 -1402 1795 -1398 1801 -1400 1795 -1394 1797 -3002 3401 -1402 1801 -2994 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 3401 -3002 1801 -1396 3391 -3002 1799 -1402 1801 -1400 3401 -2996 3401 -1706 +RAW_Data: 219 -2872 1657 -2822 3519 -2942 3559 -2974 3385 -2992 3389 -3002 3401 -2994 3403 -2994 3407 -2996 3395 -3002 3393 -3002 3393 -3004 3401 -2994 3401 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -3000 1803 -1398 1791 -1398 1801 -1400 3401 -3004 3393 -1400 1801 -2994 3401 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -3002 3403 -1400 1597 -1592 1797 -3002 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 3401 -2994 1801 -1402 3401 -2994 1801 -1400 1795 -1396 3397 -1400 1801 -7342 2387 -2176 3817 -2614 3601 -3002 3403 -3002 3401 -3002 3401 -2996 3405 -2998 3393 -3002 3395 -3002 3393 -3002 3407 -2998 3401 -2994 3393 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -3004 1799 -1402 1795 -1394 1797 -1400 3403 -3002 3401 -1400 1597 -3190 3403 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1795 -3000 1799 -1402 1795 -1398 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1799 -1398 3389 -3002 1801 -1400 3395 -3002 1799 -1402 3593 -4558 1793 -2438 3923 -2740 3563 -2976 3385 -2990 3395 -2994 3401 -2994 3403 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -2994 3407 -2998 3393 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -2994 1801 -1402 1799 -1402 1799 -1398 3389 -3002 3403 -1400 1801 -3002 3401 -1398 1791 -1398 1801 -1396 1797 -1400 1801 -1402 1799 -3002 1801 -1398 3397 -3002 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 +RAW_Data: 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1597 -1592 1797 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 3401 -2994 1801 -1400 3403 -3002 1595 -1594 3397 -3002 3395 -7924 1529 -3076 3409 -3002 3403 -3002 3401 -3002 3401 -2996 3393 -3002 3401 -2994 3407 -2998 3393 -3002 3401 -2996 3401 -2994 3401 -3002 3399 -1404 1795 -1394 1793 -1398 1799 -1402 1801 -1396 1797 -3002 1801 -1400 1801 -1400 1801 -1402 3397 -3010 3197 -1598 1791 -2992 3401 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -3002 3401 -3000 1603 -1598 1791 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1394 3397 -3002 1801 -1400 3401 -2996 1799 -1402 3407 -1400 1591 -3192 3601 -4992 1735 -2952 3401 -3006 3401 -3002 3403 -3002 3401 -2998 3397 -2996 3405 -2998 3399 -3004 3195 -3194 3401 -2994 3403 -2994 3405 -3006 3193 -1598 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1791 -3000 1799 -1402 1799 -1402 1801 -1400 3395 -3002 3401 -1396 1793 -2990 3403 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -3002 3401 -1398 1791 -2998 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1396 1797 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1402 1599 -1598 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1398 1795 -1402 1801 -1400 1801 -1400 1601 -1596 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1797 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 3401 -3004 1795 -1394 3397 -3002 1801 -1400 3403 -1396 1791 -1398 1801 -8002 1987 -2168 4109 -2272 4115 -2502 3705 -2690 3651 -2834 3423 -3016 +RAW_Data: 3403 -3002 3401 -3002 3401 -3002 3395 -3002 3407 -2996 3395 -2994 3401 -1402 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -3002 1795 -1394 1797 -1402 1799 -1402 3401 -3002 3395 -1400 1795 -3000 3407 -1404 1795 -1394 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -3004 1795 -1394 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1799 -1402 1801 -1396 1791 -1398 1801 -1402 1799 -1402 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1402 1597 -1592 1797 -1400 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1400 1801 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -1398 1795 -1402 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1402 3401 -2994 1801 -1400 3403 -2994 3401 -3002 1795 -1394 1797 -1402 3601 -1036 421 -848 843 -1518 1659 -2912 3565 -2984 3385 -2994 3395 -2994 3401 -2994 3401 -3004 3401 -2994 3401 -2994 3403 -2994 3401 -3002 3393 -3004 3393 -3002 3401 -1402 1795 -1394 1797 -1400 1801 -1400 1801 -1402 1799 -3002 1597 -1594 1795 -1402 1801 -1400 3401 -2996 3401 -1396 1797 -3002 3407 -1404 1597 -1592 1791 -1398 1801 -1402 1799 -1402 1799 -1402 1801 -2994 3401 -2994 1801 -1400 1801 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1801 -1402 1795 -1398 1795 -1398 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1801 -1400 1795 -1394 1797 -1402 1799 -1402 1801 -1396 1797 -1400 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1795 -1396 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1797 -1402 1799 -1398 1795 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1398 1797 -1400 1801 -1400 1797 -1398 1799 -1402 1799 -1402 1795 -1398 1801 -1400 1801 -1402 1795 -1394 1797 -1400 3401 -3002 1801 -1402 3393 -3002 3401 -3002 1597 -1594 3389 -7770 2021 -2912 3517 -3074 3245 -3022 3423 -3008 3411 -2998 3393 -2994 3401 -2994 3403 -3002 3393 -3002 3407 -2998 3393 -2994 3401 -3008 3397 -1396 1793 -1398 1799 -1402 1801 -1400 1801 -1396 1791 -3000 1801 -1400 1801 -1400 1801 -1402 3401 -2994 3393 -1402 1801 -3002 3401 -1398 1791 -1398 1799 -1402 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -3004 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1795 -1398 1801 -1400 1801 -1400 1797 -1394 1795 -1402 1801 -1400 1801 -1400 1801 -1400 1801 -1398 1791 -1398 1801 -1400 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 +RAW_Data: 1791 -1398 1801 -1400 1801 -1402 1799 -1402 1799 -1398 1791 -1398 1801 -1400 1801 -1402 1799 -1398 1797 -1400 1801 -1400 1801 -1400 1797 -1398 1799 -1402 1801 -1400 1795 -1398 1801 -1400 1801 -1402 1795 -1398 1801 -1400 1801 -1400 1597 -1594 1795 -1402 1799 -1402 1801 -1400 1801 -1396 1793 -1398 1799 -1402 1799 -1402 1801 -1400 1795 -1398 1801 -1402 1799 -1402 1799 -1402 1795 -1394 1797 -1400 1801 -1402 3401 -3002 1795 -1394 3399 -3002 3401 -2998 3405 -2996 3599 -1948 429 -20132 735 -734 1509 -192 387 -196272 217 -93450 441 -8180 183 -495896 381 -952 379 -594116 183 -209314 209 -296722 651 -199272 381 -98996 441 From 8370367f31536a1ce97028e18c1d5ad0767038fb Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 13:56:31 -0500 Subject: [PATCH 131/231] added common lrs pager frequency to subghz read & analyzer --- assets/resources/subghz/assets/setting_user.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/resources/subghz/assets/setting_user.txt b/assets/resources/subghz/assets/setting_user.txt index 173511f40..67e89dceb 100644 --- a/assets/resources/subghz/assets/setting_user.txt +++ b/assets/resources/subghz/assets/setting_user.txt @@ -51,6 +51,7 @@ Frequency: 438900000 Frequency: 440175000 Frequency: 446000000 Frequency: 464000000 +Frequency: 467750000 Frequency: 779000000 Frequency: 868350000 Frequency: 868400000 From 7bde578a368f198ae92eda136d7a6f36bf170fab Mon Sep 17 00:00:00 2001 From: jbohack Date: Wed, 8 Feb 2023 14:04:53 -0500 Subject: [PATCH 132/231] added pager bruteforce playlist for looping --- assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt diff --git a/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt b/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt new file mode 100644 index 000000000..5ea4e2df5 --- /dev/null +++ b/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt @@ -0,0 +1,2 @@ +# Pager Bruteforce Playlist +sub: /ext/subghz/Misc/Pager_Bruteforce.sub From d7ecc95de44d3b4d57a80cab045ba787807dc50b Mon Sep 17 00:00:00 2001 From: Victor Nikitchuk Date: Thu, 9 Feb 2023 06:40:04 +0300 Subject: [PATCH 133/231] Firmware fixes and improvements for flashing via blackmagic (#2321) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: quen0n Co-authored-by: あく Co-authored-by: hedger --- scripts/fbt_tools/fbt_debugopts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/fbt_tools/fbt_debugopts.py b/scripts/fbt_tools/fbt_debugopts.py index f4b021c20..9abe59893 100644 --- a/scripts/fbt_tools/fbt_debugopts.py +++ b/scripts/fbt_tools/fbt_debugopts.py @@ -47,6 +47,7 @@ def generate(env, **kw): "source ${FBT_DEBUG_DIR}/gdbinit", ], GDBOPTS_BLACKMAGIC=[ + "-q", "-ex", "monitor swdp_scan", "-ex", From 71871949ec6999ab71fff5d25be8fb18a17a8186 Mon Sep 17 00:00:00 2001 From: Patrick Cunningham Date: Wed, 8 Feb 2023 19:47:16 -0800 Subject: [PATCH 134/231] Picopass: show elite key used from dictionary (#2119) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * show elite key used from dictionary * remove space so it fits on screen Co-authored-by: あく --- .../scenes/picopass_scene_read_card_success.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c index 0d1cc78c3..d89a5d89b 100644 --- a/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c +++ b/applications/plugins/picopass/scenes/picopass_scene_read_card_success.c @@ -69,6 +69,19 @@ void picopass_scene_read_card_success_on_enter(void* context) { furi_string_cat_printf(sio_str, "+SIO"); } + if(pacs->key) { + if(pacs->sio) { + furi_string_cat_printf(sio_str, " "); + } + furi_string_cat_printf(sio_str, "Key: "); + + uint8_t key[PICOPASS_BLOCK_LEN]; + memcpy(key, &pacs->key, PICOPASS_BLOCK_LEN); + for(uint8_t i = 0; i < PICOPASS_BLOCK_LEN; i++) { + furi_string_cat_printf(sio_str, "%02X", key[i]); + } + } + widget_add_button_element( widget, GuiButtonTypeLeft, From 163be139ebb7a56d495d97ca87b662dfcd8a0bb8 Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 9 Feb 2023 08:48:06 +0400 Subject: [PATCH 135/231] SubGhz: add protocol BinRAW (binarization of data quantized by the minimum correlated duration) (#2322) * SubGhz: add protocol DataRAW (binarization of data quantized by the minimum correlated duration) * SubGhz: fix name history * SubGhz: add encoder Data_RAW protocol * SubGhz: decreasing the size of the LevelDuration structure * SubGhz: history, added check that there is free RAM * SubGhz: checking for free memory, support to pass without gap * SubGhz: add running average to average the result, auto cut noise at the end of a burst * SubGhz: support for repeating sequences * SubGhz: fix secplus_v2 decoder * SubGhz: bin_RAW fix add history * SubGhz: add debug * SubGhz: debug refactoring * FURI_LOG: add FURI_LOG_RAW_x formatted string output like printf * SubGhz: fix new FURI_LOG metod * FURI_LOG: fix unit test * SubGhz: add enable/disable BinRAW protocol decoding * SubGhz: fix PVS * SubGhz: forcibly turn off the speaker when exiting SubGhz * SubGhz: adaptive adjustment to the noise level Co-authored-by: Aleksandr Kutuzov --- applications/debug/unit_tests/test_index.c | 2 +- applications/main/subghz/application.fam | 2 +- .../subghz/scenes/subghz_scene_read_raw.c | 2 +- .../subghz/scenes/subghz_scene_receiver.c | 13 + .../scenes/subghz_scene_receiver_config.c | 32 + applications/main/subghz/subghz.c | 5 +- applications/main/subghz/subghz_history.c | 40 +- applications/main/subghz/subghz_i.h | 1 + applications/main/subghz/views/receiver.c | 33 +- applications/main/subghz/views/receiver.h | 2 + .../subghz/views/subghz_frequency_analyzer.c | 1 - applications/main/subghz/views/transmitter.c | 7 +- firmware/targets/f7/api_symbols.csv | 3 +- furi/core/log.c | 31 +- furi/core/log.h | 51 +- lib/subghz/blocks/encoder.c | 23 +- lib/subghz/blocks/encoder.h | 11 +- lib/subghz/blocks/generic.c | 2 +- lib/subghz/blocks/generic.h | 2 +- lib/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ lib/subghz/protocols/bin_raw.h | 111 ++ lib/subghz/protocols/chamberlain_code.c | 5 +- lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + lib/subghz/protocols/secplus_v2.c | 8 +- lib/subghz/receiver.c | 2 +- lib/subghz/types.h | 1 + lib/toolbox/level_duration.h | 4 +- 28 files changed, 1451 insertions(+), 65 deletions(-) create mode 100644 lib/subghz/protocols/bin_raw.c create mode 100644 lib/subghz/protocols/bin_raw.h diff --git a/applications/debug/unit_tests/test_index.c b/applications/debug/unit_tests/test_index.c index 2bb9c423f..ac71ca397 100644 --- a/applications/debug/unit_tests/test_index.c +++ b/applications/debug/unit_tests/test_index.c @@ -70,7 +70,7 @@ void minunit_print_progress() { } void minunit_print_fail(const char* str) { - printf(FURI_LOG_CLR_E "%s\r\n" FURI_LOG_CLR_RESET, str); + printf(_FURI_LOG_CLR_E "%s\r\n" _FURI_LOG_CLR_RESET, str); } void unit_tests_cli(Cli* cli, FuriString* args, void* context) { diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 6df4b1a8a..f0dc66e89 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -12,7 +12,7 @@ App( ], provides=["subghz_start"], icon="A_Sub1ghz_14", - stack_size=2 * 1024, + stack_size=3 * 1024, order=10, ) diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index 6f95c4169..96acc90ee 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -411,5 +411,5 @@ void subghz_scene_read_raw_on_exit(void* context) { notification_message(subghz->notifications, &sequence_reset_rgb); //filter restoration - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); } diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index 2b01e2975..93c369092 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/receiver.h" #include +#include static const NotificationSequence subghs_sequence_rx = { &message_green_255, @@ -143,6 +144,11 @@ void subghz_scene_receiver_on_enter(void* context) { } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); + //to use a universal decoder, we are looking for a link to it + subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( + subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); + furi_assert(subghz->txrx->decoder_result); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } @@ -208,6 +214,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_hopper_update(subghz); subghz_scene_receiver_update_statusbar(subghz); } + + //get RSSI + float rssi = furi_hal_subghz_get_rssi(); + subghz_receiver_rssi(subghz->subghz_receiver, rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); + switch(subghz->state_notifications) { case SubGhzNotificationStateRx: notification_message(subghz->notifications, &sequence_blink_cyan_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index b49aac922..895e43342 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -5,6 +5,7 @@ enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, + SubGhzSettingIndexBinRAW, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, @@ -58,6 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = { SubGhzSpeakerStateShutdown, SubGhzSpeakerStateEnable, }; +#define BIN_RAW_COUNT 2 +const char* const bin_raw_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t bin_raw_value[BIN_RAW_COUNT] = { + SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, +}; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); @@ -186,6 +196,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { subghz->txrx->speaker_state = speaker_value[index]; } +static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, bin_raw_text[index]); + subghz->txrx->filter = bin_raw_value[index]; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -254,6 +273,19 @@ void subghz_scene_receiver_config_on_enter(void* context) { variable_item_set_current_value_text( item, subghz_setting_get_preset_name(subghz->setting, value_index)); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { + item = variable_item_list_add( + subghz->variable_item_list, + "Bin_RAW:", + BIN_RAW_COUNT, + subghz_scene_receiver_config_set_bin_raw, + subghz); + value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, bin_raw_text[value_index]); + } + item = variable_item_list_add( subghz->variable_item_list, "Sound:", diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 200be6262..25233fe21 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -194,7 +194,8 @@ SubGhz* subghz_alloc() { subghz_environment_set_protocol_registry( subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); + subghz->txrx->filter = SubGhzProtocolFlag_Decodable; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_worker_set_overrun_callback( subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); @@ -218,6 +219,8 @@ void subghz_free(SubGhz* subghz) { subghz->rpc_ctx = NULL; } + subghz_speaker_off(subghz); + // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); subghz_test_packet_free(subghz->subghz_test_packet); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index beecc6e8b..e6c93e05c 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -5,6 +5,7 @@ #include #define SUBGHZ_HISTORY_MAX 50 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" typedef struct { @@ -121,8 +122,12 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx } bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Free heap LOW"); + return true; + } if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, "Memory is FULL"); + if(output != NULL) furi_string_printf(output, " Memory is FULL"); return true; } if(output != NULL) @@ -142,6 +147,7 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; @@ -200,27 +206,31 @@ bool subghz_history_add_to_history( } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; + FURI_LOG_D(TAG, "No Key"); } uint64_t data = 0; for(uint8_t i = 0; i < sizeof(uint64_t); i++) { data = (data << 8) | key_data[i]; } - if(!(uint32_t)(data >> 32)) { - furi_string_printf( - item->item_str, - "%s %lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); + if(data != 0) { + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } } else { - furi_string_printf( - item->item_str, - "%s %lX%08lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); + furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string)); } + } while(false); furi_string_free(text); diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index cd33da447..65480c6fd 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -45,6 +45,7 @@ struct SubGhzTxRx { SubGhzEnvironment* environment; SubGhzReceiver* receiver; SubGhzTransmitter* transmitter; + SubGhzProtocolFlag filter; SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index aaec2adda..acc39e258 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -12,6 +12,8 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct { FuriString* item_str; uint8_t type; @@ -59,8 +61,24 @@ typedef struct { uint16_t list_offset; uint16_t history_item; SubGhzViewReceiverBarShow bar_show; + uint8_t u_rssi; } SubGhzViewReceiverModel; +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzViewReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -168,13 +186,22 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); bool scrollbar = model->history_item > 4; FuriString* str_buff; @@ -206,11 +233,11 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(model->history_item == 0) { canvas_draw_icon(canvas, 0, 0, &I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); + canvas_draw_str(canvas, 63, 44, "Scanning..."); canvas_set_font(canvas, FontSecondary); } + subghz_view_rssi_draw(canvas, model); switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index aab7a76c5..9b12ccfee 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -8,6 +8,8 @@ typedef struct SubGhzViewReceiver SubGhzViewReceiver; typedef void (*SubGhzViewReceiverCallback)(SubGhzCustomEvent event, void* context); +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_callback( diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 94419084b..325664f4a 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -79,7 +79,6 @@ void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, uint8_t rssi, uint8_t x void subghz_frequency_analyzer_draw_log_rssi(Canvas* canvas, uint8_t rssi, uint8_t x, uint8_t y) { uint8_t column_height = 6; if(rssi) { - //rssi = rssi if(rssi > 54) rssi = 54; for(uint8_t i = 1; i < rssi; i++) { if(i % 5) { diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index 833805ccb..4a13460a3 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -84,9 +84,10 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 0, 8, furi_string_get_cstr(model->key_str)); - canvas_draw_str(canvas, 78, 8, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 113, 8, furi_string_get_cstr(model->preset_str)); + elements_multiline_text_aligned( + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); + canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 846041c3e..906ab357a 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1409,6 +1409,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -2599,7 +2600,7 @@ Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8 Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" -Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" diff --git a/furi/core/log.c b/furi/core/log.c index a3967ed92..d910ecf21 100644 --- a/furi/core/log.c +++ b/furi/core/log.c @@ -28,27 +28,27 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form FuriString* string; string = furi_string_alloc(); - const char* color = FURI_LOG_CLR_RESET; + const char* color = _FURI_LOG_CLR_RESET; const char* log_letter = " "; switch(level) { case FuriLogLevelError: - color = FURI_LOG_CLR_E; + color = _FURI_LOG_CLR_E; log_letter = "E"; break; case FuriLogLevelWarn: - color = FURI_LOG_CLR_W; + color = _FURI_LOG_CLR_W; log_letter = "W"; break; case FuriLogLevelInfo: - color = FURI_LOG_CLR_I; + color = _FURI_LOG_CLR_I; log_letter = "I"; break; case FuriLogLevelDebug: - color = FURI_LOG_CLR_D; + color = _FURI_LOG_CLR_D; log_letter = "D"; break; case FuriLogLevelTrace: - color = FURI_LOG_CLR_T; + color = _FURI_LOG_CLR_T; log_letter = "T"; break; default: @@ -58,7 +58,7 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form // Timestamp furi_string_printf( string, - "%lu %s[%s][%s] " FURI_LOG_CLR_RESET, + "%lu %s[%s][%s] " _FURI_LOG_CLR_RESET, furi_log.timestamp(), color, log_letter, @@ -80,6 +80,23 @@ void furi_log_print_format(FuriLogLevel level, const char* tag, const char* form } } +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) { + if(level <= furi_log.log_level && + furi_mutex_acquire(furi_log.mutex, FuriWaitForever) == FuriStatusOk) { + FuriString* string; + string = furi_string_alloc(); + va_list args; + va_start(args, format); + furi_string_vprintf(string, format, args); + va_end(args); + + furi_log.puts(furi_string_get_cstr(string)); + furi_string_free(string); + + furi_mutex_release(furi_log.mutex); + } +} + void furi_log_set_level(FuriLogLevel level) { if(level == FuriLogLevelDefault) { level = FURI_LOG_LEVEL_DEFAULT; diff --git a/furi/core/log.h b/furi/core/log.h index cb8b3d9cc..46ae7f007 100644 --- a/furi/core/log.h +++ b/furi/core/log.h @@ -22,21 +22,21 @@ typedef enum { FuriLogLevelTrace = 6, } FuriLogLevel; -#define FURI_LOG_CLR(clr) "\033[0;" clr "m" -#define FURI_LOG_CLR_RESET "\033[0m" +#define _FURI_LOG_CLR(clr) "\033[0;" clr "m" +#define _FURI_LOG_CLR_RESET "\033[0m" -#define FURI_LOG_CLR_BLACK "30" -#define FURI_LOG_CLR_RED "31" -#define FURI_LOG_CLR_GREEN "32" -#define FURI_LOG_CLR_BROWN "33" -#define FURI_LOG_CLR_BLUE "34" -#define FURI_LOG_CLR_PURPLE "35" +#define _FURI_LOG_CLR_BLACK "30" +#define _FURI_LOG_CLR_RED "31" +#define _FURI_LOG_CLR_GREEN "32" +#define _FURI_LOG_CLR_BROWN "33" +#define _FURI_LOG_CLR_BLUE "34" +#define _FURI_LOG_CLR_PURPLE "35" -#define FURI_LOG_CLR_E FURI_LOG_CLR(FURI_LOG_CLR_RED) -#define FURI_LOG_CLR_W FURI_LOG_CLR(FURI_LOG_CLR_BROWN) -#define FURI_LOG_CLR_I FURI_LOG_CLR(FURI_LOG_CLR_GREEN) -#define FURI_LOG_CLR_D FURI_LOG_CLR(FURI_LOG_CLR_BLUE) -#define FURI_LOG_CLR_T FURI_LOG_CLR(FURI_LOG_CLR_PURPLE) +#define _FURI_LOG_CLR_E _FURI_LOG_CLR(_FURI_LOG_CLR_RED) +#define _FURI_LOG_CLR_W _FURI_LOG_CLR(_FURI_LOG_CLR_BROWN) +#define _FURI_LOG_CLR_I _FURI_LOG_CLR(_FURI_LOG_CLR_GREEN) +#define _FURI_LOG_CLR_D _FURI_LOG_CLR(_FURI_LOG_CLR_BLUE) +#define _FURI_LOG_CLR_T _FURI_LOG_CLR(_FURI_LOG_CLR_PURPLE) typedef void (*FuriLogPuts)(const char* data); typedef uint32_t (*FuriLogTimestamp)(void); @@ -54,6 +54,15 @@ void furi_log_init(); void furi_log_print_format(FuriLogLevel level, const char* tag, const char* format, ...) _ATTRIBUTE((__format__(__printf__, 3, 4))); +/** Print log record + * + * @param level + * @param format + * @param ... + */ +void furi_log_print_raw_format(FuriLogLevel level, const char* format, ...) + _ATTRIBUTE((__format__(__printf__, 2, 3))); + /** Set log level * * @param[in] level The level @@ -95,6 +104,22 @@ void furi_log_set_timestamp(FuriLogTimestamp timestamp); #define FURI_LOG_T(tag, format, ...) \ furi_log_print_format(FuriLogLevelTrace, tag, format, ##__VA_ARGS__) +/** Log methods + * + * @param format The raw format + * @param ... VA Args + */ +#define FURI_LOG_RAW_E(format, ...) \ + furi_log_print_raw_format(FuriLogLevelError, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_W(format, ...) \ + furi_log_print_raw_format(FuriLogLevelWarn, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_I(format, ...) \ + furi_log_print_raw_format(FuriLogLevelInfo, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_D(format, ...) \ + furi_log_print_raw_format(FuriLogLevelDebug, format, ##__VA_ARGS__) +#define FURI_LOG_RAW_T(format, ...) \ + furi_log_print_raw_format(FuriLogLevelTrace, format, ##__VA_ARGS__) + #ifdef __cplusplus } #endif diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index f3349b5fc..49ec4f177 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -2,6 +2,8 @@ #include "math.h" #include +#include "furi.h" + #define TAG "SubGhzBlockEncoder" void subghz_protocol_blocks_set_bit_array( @@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); } -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit) { - size_t index_bit = 0; + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; size_t size_upload = 0; uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1; i < count_bit_data_array; i++) { + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { duration += duration_bit; } else { - furi_assert(max_size_upload > size_upload); + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); last_bit = !last_bit; diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 1ff077726..aeaa2add0 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -19,6 +19,11 @@ typedef struct { } SubGhzProtocolBlockEncoder; +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + /** * Set data bit when encoding HEX array. * @param bit_value The value of the bit to be set @@ -47,13 +52,15 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde * @param upload Pointer to a LevelDuration * @param max_size_upload upload size, check not to overflow * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array */ -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit); + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); #ifdef __cplusplus } diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 94114676d..3d59adc82 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -100,7 +100,7 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma FURI_LOG_E(TAG, "Missing Bit"); break; } - instance->data_count_bit = (uint8_t)temp_data; + instance->data_count_bit = (uint16_t)temp_data; uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index d1c7dc356..284df51ae 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -19,7 +19,7 @@ struct SubGhzBlockGeneric { const char* protocol_name; uint64_t data; uint32_t serial; - uint8_t data_count_bit; + uint16_t data_count_bit; uint8_t btn; uint32_t cnt; }; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..c3f544110 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeStatic, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* 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_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 32f4e9520..9c8e5ee4a 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -196,12 +196,13 @@ static bool break; } - instance->encoder.size_upload = subghz_protocol_blocks_get_upload( + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( upload_hex_data, upload_hex_count_bit, instance->encoder.upload, instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short); + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); return true; } diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 050904eec..74244c5ff 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -42,6 +42,7 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 522931d22..4ca1c4679 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -42,5 +42,6 @@ #include "dooya.h" #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" +#include "bin_raw.h" extern const SubGhzProtocolRegistry subghz_protocol_registry; diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 7b79892b0..f7262bd17 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -261,16 +261,16 @@ static bool data = order << 4 | invert; int k = 0; for(int i = 6; i >= 0; i -= 2) { - roll_array[k++] = (data >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } } for(int i = 8; i >= 0; i -= 2) { - roll_array[k++] = (p[2] >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index fd6f1493e..698fe098e 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -64,7 +64,7 @@ void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t durat for M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - if((slot->base->protocol->flag & instance->filter) == instance->filter) { + if((slot->base->protocol->flag & instance->filter) != 0) { slot->base->protocol->decoder->feed(slot->base, level, duration); } } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 6c34dc728..1b8ef6a14 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -90,6 +90,7 @@ typedef enum { SubGhzProtocolFlag_Save = (1 << 7), SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; typedef struct { diff --git a/lib/toolbox/level_duration.h b/lib/toolbox/level_duration.h index 9406cef31..7618344ac 100644 --- a/lib/toolbox/level_duration.h +++ b/lib/toolbox/level_duration.h @@ -13,8 +13,8 @@ #define LEVEL_DURATION_RESERVED 0x800000U typedef struct { - uint32_t level; - uint32_t duration; + uint32_t duration : 30; + uint8_t level : 2; } LevelDuration; static inline LevelDuration level_duration_make(bool level, uint32_t duration) { From 4265057ee864a8d68a68e9a9ef4088b39d2b1e5c Mon Sep 17 00:00:00 2001 From: Petr Portnov | PROgrm_JARvis Date: Thu, 9 Feb 2023 07:58:01 +0300 Subject: [PATCH 136/231] feat: add missing `const` qualifiers (#2233) * feat: make `ViewPort` getters const * feat: make tx-buffers const * feat: make `canvas_get_buffer_size` const * feat: make `canvas` methods const * feat: make `icon_animation` methods const * feat: make `scene_manager` methods const * feat: make `loader` method const * feat: make `canvas_get_font_params` const Co-authored-by: Aleksandr Kutuzov --- applications/services/gui/canvas.c | 12 +++--- applications/services/gui/canvas.h | 8 ++-- applications/services/gui/canvas_i.h | 2 +- applications/services/gui/elements.c | 2 +- applications/services/gui/gui.c | 2 +- applications/services/gui/gui.h | 2 +- applications/services/gui/icon_animation.c | 8 ++-- applications/services/gui/icon_animation.h | 6 +-- applications/services/gui/icon_animation_i.h | 2 +- .../widget_element_text_scroll.c | 4 +- applications/services/gui/scene_manager.c | 4 +- applications/services/gui/scene_manager.h | 4 +- applications/services/gui/view_port.c | 6 +-- applications/services/gui/view_port.h | 6 +-- applications/services/loader/loader.c | 2 +- applications/services/loader/loader.h | 2 +- applications/services/locale/locale.h | 2 +- firmware/targets/f18/api_symbols.csv | 35 ++++++++--------- firmware/targets/f7/api_symbols.csv | 38 +++++++++---------- firmware/targets/f7/furi_hal/furi_hal_spi.c | 4 +- .../targets/furi_hal_include/furi_hal_spi.h | 4 +- 21 files changed, 78 insertions(+), 77 deletions(-) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 18636eced..d47bba6b2 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -57,7 +57,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas) { return u8g2_GetBufferPtr(&canvas->fb); } -size_t canvas_get_buffer_size(Canvas* canvas) { +size_t canvas_get_buffer_size(const Canvas* canvas) { furi_assert(canvas); return u8g2_GetBufferTileWidth(&canvas->fb) * u8g2_GetBufferTileHeight(&canvas->fb) * 8; } @@ -75,17 +75,17 @@ void canvas_frame_set( canvas->height = height; } -uint8_t canvas_width(Canvas* canvas) { +uint8_t canvas_width(const Canvas* canvas) { furi_assert(canvas); return canvas->width; } -uint8_t canvas_height(Canvas* canvas) { +uint8_t canvas_height(const Canvas* canvas) { furi_assert(canvas); return canvas->height; } -uint8_t canvas_current_font_height(Canvas* canvas) { +uint8_t canvas_current_font_height(const Canvas* canvas) { furi_assert(canvas); uint8_t font_height = u8g2_GetMaxCharHeight(&canvas->fb); @@ -96,10 +96,10 @@ uint8_t canvas_current_font_height(Canvas* canvas) { return font_height; } -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font) { +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font) { furi_assert(canvas); furi_assert(font < FontTotalNumber); - return (CanvasFontParameters*)&canvas_font_params[font]; + return &canvas_font_params[font]; } void canvas_clear(Canvas* canvas) { diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index b2a065ca7..f8fe2c1db 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -85,7 +85,7 @@ void canvas_commit(Canvas* canvas); * * @return width in pixels. */ -uint8_t canvas_width(Canvas* canvas); +uint8_t canvas_width(const Canvas* canvas); /** Get Canvas height * @@ -93,7 +93,7 @@ uint8_t canvas_width(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_height(Canvas* canvas); +uint8_t canvas_height(const Canvas* canvas); /** Get current font height * @@ -101,7 +101,7 @@ uint8_t canvas_height(Canvas* canvas); * * @return height in pixels. */ -uint8_t canvas_current_font_height(Canvas* canvas); +uint8_t canvas_current_font_height(const Canvas* canvas); /** Get font parameters * @@ -110,7 +110,7 @@ uint8_t canvas_current_font_height(Canvas* canvas); * * @return pointer to CanvasFontParameters structure */ -CanvasFontParameters* canvas_get_font_params(Canvas* canvas, Font font); +const CanvasFontParameters* canvas_get_font_params(const Canvas* canvas, Font font); /** Clear canvas * diff --git a/applications/services/gui/canvas_i.h b/applications/services/gui/canvas_i.h index 2b6849235..12cabfa7d 100644 --- a/applications/services/gui/canvas_i.h +++ b/applications/services/gui/canvas_i.h @@ -45,7 +45,7 @@ uint8_t* canvas_get_buffer(Canvas* canvas); * * @return size of canvas in bytes */ -size_t canvas_get_buffer_size(Canvas* canvas); +size_t canvas_get_buffer_size(const Canvas* canvas); /** Set drawing region relative to real screen buffer * diff --git a/applications/services/gui/elements.c b/applications/services/gui/elements.c index 54c36af76..9b7c84ece 100644 --- a/applications/services/gui/elements.c +++ b/applications/services/gui/elements.c @@ -639,7 +639,7 @@ void elements_text_box( bool inversed_present = false; Font current_font = FontSecondary; Font prev_font = FontSecondary; - CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); + const CanvasFontParameters* font_params = canvas_get_font_params(canvas, current_font); // Fill line parameters uint8_t line_leading_min = font_params->leading_min; diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index af5cf862d..94bab1402 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -467,7 +467,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, gui_unlock(gui); } -size_t gui_get_framebuffer_size(Gui* gui) { +size_t gui_get_framebuffer_size(const Gui* gui) { furi_assert(gui); return canvas_get_buffer_size(gui->canvas); } diff --git a/applications/services/gui/gui.h b/applications/services/gui/gui.h index 4e7fc2e4d..d7d73f27b 100644 --- a/applications/services/gui/gui.h +++ b/applications/services/gui/gui.h @@ -94,7 +94,7 @@ void gui_remove_framebuffer_callback(Gui* gui, GuiCanvasCommitCallback callback, * @param gui Gui instance * @return size_t size of frame buffer in bytes */ -size_t gui_get_framebuffer_size(Gui* gui); +size_t gui_get_framebuffer_size(const Gui* gui); /** Set lockdown mode * diff --git a/applications/services/gui/icon_animation.c b/applications/services/gui/icon_animation.c index 48c862208..b63d233f3 100644 --- a/applications/services/gui/icon_animation.c +++ b/applications/services/gui/icon_animation.c @@ -29,7 +29,7 @@ void icon_animation_set_update_callback( instance->callback_context = context; } -const uint8_t* icon_animation_get_data(IconAnimation* instance) { +const uint8_t* icon_animation_get_data(const IconAnimation* instance) { return instance->icon->frames[instance->frame]; } @@ -51,12 +51,12 @@ void icon_animation_timer_callback(void* context) { } } -uint8_t icon_animation_get_width(IconAnimation* instance) { +uint8_t icon_animation_get_width(const IconAnimation* instance) { furi_assert(instance); return instance->icon->width; } -uint8_t icon_animation_get_height(IconAnimation* instance) { +uint8_t icon_animation_get_height(const IconAnimation* instance) { furi_assert(instance); return instance->icon->height; } @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance) { } } -bool icon_animation_is_last_frame(IconAnimation* instance) { +bool icon_animation_is_last_frame(const IconAnimation* instance) { furi_assert(instance); return instance->icon->frame_count - instance->frame <= 1; } diff --git a/applications/services/gui/icon_animation.h b/applications/services/gui/icon_animation.h index 684790353..6e58df31d 100644 --- a/applications/services/gui/icon_animation.h +++ b/applications/services/gui/icon_animation.h @@ -55,7 +55,7 @@ void icon_animation_set_update_callback( * * @return width in pixels */ -uint8_t icon_animation_get_width(IconAnimation* instance); +uint8_t icon_animation_get_width(const IconAnimation* instance); /** Get icon animation height * @@ -63,7 +63,7 @@ uint8_t icon_animation_get_width(IconAnimation* instance); * * @return height in pixels */ -uint8_t icon_animation_get_height(IconAnimation* instance); +uint8_t icon_animation_get_height(const IconAnimation* instance); /** Start icon animation * @@ -83,7 +83,7 @@ void icon_animation_stop(IconAnimation* instance); * * @return true if last frame */ -bool icon_animation_is_last_frame(IconAnimation* instance); +bool icon_animation_is_last_frame(const IconAnimation* instance); #ifdef __cplusplus } diff --git a/applications/services/gui/icon_animation_i.h b/applications/services/gui/icon_animation_i.h index 4053a120d..62858d288 100644 --- a/applications/services/gui/icon_animation_i.h +++ b/applications/services/gui/icon_animation_i.h @@ -24,7 +24,7 @@ struct IconAnimation { * * @return pointer to current frame XBM bitmap data */ -const uint8_t* icon_animation_get_data(IconAnimation* instance); +const uint8_t* icon_animation_get_data(const IconAnimation* instance); /** Advance to next frame * diff --git a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c index d8fc11311..5d522c74b 100644 --- a/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c +++ b/applications/services/gui/modules/widget_elements/widget_element_text_scroll.c @@ -74,7 +74,7 @@ static void widget_element_text_scroll_fill_lines(Canvas* canvas, WidgetElement* } // Set canvas font canvas_set_font(canvas, line_tmp.font); - CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line_tmp.font); total_height += params->height; if(total_height > model->height) { model->scroll_pos_total++; @@ -138,7 +138,7 @@ static void widget_element_text_scroll_draw(Canvas* canvas, WidgetElement* eleme TextScrollLineArray_next(it), curr_line++) { if(curr_line < model->scroll_pos_current) continue; TextScrollLineArray* line = TextScrollLineArray_ref(it); - CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); + const CanvasFontParameters* params = canvas_get_font_params(canvas, line->font); if(y + params->descender > model->y + model->height) break; canvas_set_font(canvas, line->font); if(line->horizontal == AlignLeft) { diff --git a/applications/services/gui/scene_manager.c b/applications/services/gui/scene_manager.c index 590145e1e..4064dafb6 100644 --- a/applications/services/gui/scene_manager.c +++ b/applications/services/gui/scene_manager.c @@ -34,7 +34,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i scene_manager->scene[scene_id].state = state; } -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id) { +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); furi_assert(scene_id < scene_manager->scene_handlers->scene_num); @@ -184,7 +184,7 @@ bool scene_manager_search_and_switch_to_previous_scene_one_of( return scene_found; } -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id) { +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id) { furi_assert(scene_manager); bool scene_found = false; diff --git a/applications/services/gui/scene_manager.h b/applications/services/gui/scene_manager.h index 5b833e5de..c349a12ce 100644 --- a/applications/services/gui/scene_manager.h +++ b/applications/services/gui/scene_manager.h @@ -63,7 +63,7 @@ void scene_manager_set_scene_state(SceneManager* scene_manager, uint32_t scene_i * * @return Scene state */ -uint32_t scene_manager_get_scene_state(SceneManager* scene_manager, uint32_t scene_id); +uint32_t scene_manager_get_scene_state(const SceneManager* scene_manager, uint32_t scene_id); /** Scene Manager allocation and configuration * @@ -134,7 +134,7 @@ bool scene_manager_previous_scene(SceneManager* scene_manager); * * @return true if previous scene was found, false otherwise */ -bool scene_manager_has_previous_scene(SceneManager* scene_manager, uint32_t scene_id); +bool scene_manager_has_previous_scene(const SceneManager* scene_manager, uint32_t scene_id); /** Search and switch to previous Scene * diff --git a/applications/services/gui/view_port.c b/applications/services/gui/view_port.c index ffd01450b..5760ed577 100644 --- a/applications/services/gui/view_port.c +++ b/applications/services/gui/view_port.c @@ -89,7 +89,7 @@ void view_port_set_width(ViewPort* view_port, uint8_t width) { view_port->width = width; } -uint8_t view_port_get_width(ViewPort* view_port) { +uint8_t view_port_get_width(const ViewPort* view_port) { furi_assert(view_port); return view_port->width; } @@ -99,7 +99,7 @@ void view_port_set_height(ViewPort* view_port, uint8_t height) { view_port->height = height; } -uint8_t view_port_get_height(ViewPort* view_port) { +uint8_t view_port_get_height(const ViewPort* view_port) { furi_assert(view_port); return view_port->height; } @@ -112,7 +112,7 @@ void view_port_enabled_set(ViewPort* view_port, bool enabled) { } } -bool view_port_is_enabled(ViewPort* view_port) { +bool view_port_is_enabled(const ViewPort* view_port) { furi_assert(view_port); return view_port->is_enabled; } diff --git a/applications/services/gui/view_port.h b/applications/services/gui/view_port.h index 703e99248..752fc46ba 100644 --- a/applications/services/gui/view_port.h +++ b/applications/services/gui/view_port.h @@ -56,7 +56,7 @@ void view_port_free(ViewPort* view_port); * @param width wanted width, 0 - auto. */ void view_port_set_width(ViewPort* view_port, uint8_t width); -uint8_t view_port_get_width(ViewPort* view_port); +uint8_t view_port_get_width(const ViewPort* view_port); /** Set view_port height. * @@ -66,7 +66,7 @@ uint8_t view_port_get_width(ViewPort* view_port); * @param height wanted height, 0 - auto. */ void view_port_set_height(ViewPort* view_port, uint8_t height); -uint8_t view_port_get_height(ViewPort* view_port); +uint8_t view_port_get_height(const ViewPort* view_port); /** Enable or disable view_port rendering. * @@ -75,7 +75,7 @@ uint8_t view_port_get_height(ViewPort* view_port); * @warning automatically dispatches update event */ void view_port_enabled_set(ViewPort* view_port, bool enabled); -bool view_port_is_enabled(ViewPort* view_port); +bool view_port_is_enabled(const ViewPort* view_port); /** ViewPort event callbacks * diff --git a/applications/services/loader/loader.c b/applications/services/loader/loader.c index 97d1e6e4e..caaf9f11b 100644 --- a/applications/services/loader/loader.c +++ b/applications/services/loader/loader.c @@ -262,7 +262,7 @@ void loader_unlock(Loader* instance) { FURI_CRITICAL_EXIT(); } -bool loader_is_locked(Loader* instance) { +bool loader_is_locked(const Loader* instance) { return instance->lock_count > 0; } diff --git a/applications/services/loader/loader.h b/applications/services/loader/loader.h index 954e79c57..8dbc4fc35 100644 --- a/applications/services/loader/loader.h +++ b/applications/services/loader/loader.h @@ -43,7 +43,7 @@ bool loader_lock(Loader* instance); void loader_unlock(Loader* instance); /** Get loader lock status */ -bool loader_is_locked(Loader* instance); +bool loader_is_locked(const Loader* instance); /** Show primary loader */ void loader_show_menu(); diff --git a/applications/services/locale/locale.h b/applications/services/locale/locale.h index 5f2a4d87f..61fb4c605 100644 --- a/applications/services/locale/locale.h +++ b/applications/services/locale/locale.h @@ -81,7 +81,7 @@ void locale_format_time( * * @return The Locale DateFormat. */ -LocaleDateFormat locale_get_date_format(); +LocaleDateFormat locale_get_date_format(void); /** Set Locale DateFormat * diff --git a/firmware/targets/f18/api_symbols.csv b/firmware/targets/f18/api_symbols.csv index d29d696fc..462fbf739 100644 --- a/firmware/targets/f18/api_symbols.csv +++ b/firmware/targets/f18/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.1,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -522,7 +522,7 @@ Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* Function,+,canvas_commit,void,Canvas* -Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_current_font_height,uint8_t,const Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -539,9 +539,9 @@ Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const Canvas* Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" @@ -550,7 +550,7 @@ Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cfree,void,void* Function,-,clearerr,void,FILE* Function,-,clearerr_unlocked,void,FILE* @@ -1063,9 +1063,9 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -1123,6 +1123,7 @@ Function,+,furi_kernel_unlock,int32_t, Function,+,furi_log_get_level,FuriLogLevel, Function,-,furi_log_init,void, Function,+,furi_log_print_format,void,"FuriLogLevel, const char*, const char*, ..." +Function,+,furi_log_print_raw_format,void,"FuriLogLevel, const char*, ..." Function,+,furi_log_set_level,void,FuriLogLevel Function,-,furi_log_set_puts,void,FuriLogPuts Function,-,furi_log_set_timestamp,void,FuriLogTimestamp @@ -1287,7 +1288,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1301,9 +1302,9 @@ Function,+,hmac_sha256_init,void,"hmac_sha256_context*, const uint8_t*" Function,+,hmac_sha256_update,void,"const hmac_sha256_context*, const uint8_t*, unsigned" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1352,7 +1353,7 @@ Function,-,ldiv,ldiv_t,"long, long" Function,-,llabs,long long,long long Function,-,lldiv,lldiv_t,"long long, long long" Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -1570,11 +1571,11 @@ Function,+,saved_struct_save,_Bool,"const char*, void*, size_t, uint8_t, uint8_t Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -1946,11 +1947,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 906ab357a..33c443ae0 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.1,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -607,7 +607,7 @@ Function,-,bzero,void,"void*, size_t" Function,-,calloc,void*,"size_t, size_t" Function,+,canvas_clear,void,Canvas* Function,+,canvas_commit,void,Canvas* -Function,+,canvas_current_font_height,uint8_t,Canvas* +Function,+,canvas_current_font_height,uint8_t,const Canvas* Function,+,canvas_draw_bitmap,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" Function,+,canvas_draw_box,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t" Function,+,canvas_draw_circle,void,"Canvas*, uint8_t, uint8_t, uint8_t" @@ -624,18 +624,18 @@ Function,+,canvas_draw_str,void,"Canvas*, uint8_t, uint8_t, const char*" Function,+,canvas_draw_str_aligned,void,"Canvas*, uint8_t, uint8_t, Align, Align, const char*" Function,+,canvas_draw_triangle,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, CanvasDirection" Function,+,canvas_draw_xbm,void,"Canvas*, uint8_t, uint8_t, uint8_t, uint8_t, const uint8_t*" -Function,+,canvas_get_font_params,CanvasFontParameters*,"Canvas*, Font" +Function,+,canvas_get_font_params,const CanvasFontParameters*,"const Canvas*, Font" Function,+,canvas_glyph_width,uint8_t,"Canvas*, char" -Function,+,canvas_height,uint8_t,Canvas* +Function,+,canvas_height,uint8_t,const Canvas* Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" -Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" +Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,+,canvas_string_width,uint16_t,"Canvas*, const char*" -Function,+,canvas_width,uint8_t,Canvas* +Function,+,canvas_width,uint8_t,const Canvas* Function,-,cbrt,double,double Function,-,cbrtf,float,float Function,-,cbrtl,long double,long double @@ -1095,7 +1095,6 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* -Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" @@ -1249,6 +1248,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1319,9 +1319,9 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -1578,7 +1578,7 @@ Function,+,gui_add_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, voi Function,+,gui_add_view_port,void,"Gui*, ViewPort*, GuiLayer" Function,+,gui_direct_draw_acquire,Canvas*,Gui* Function,+,gui_direct_draw_release,void,Gui* -Function,+,gui_get_framebuffer_size,size_t,Gui* +Function,+,gui_get_framebuffer_size,size_t,const Gui* Function,+,gui_remove_framebuffer_callback,void,"Gui*, GuiCanvasCommitCallback, void*" Function,+,gui_remove_view_port,void,"Gui*, ViewPort*" Function,+,gui_set_lockdown,void,"Gui*, _Bool" @@ -1621,9 +1621,9 @@ Function,+,ibutton_worker_write_set_callback,void,"iButtonWorker*, iButtonWorker Function,+,ibutton_worker_write_start,void,"iButtonWorker*, iButtonKey*" Function,+,icon_animation_alloc,IconAnimation*,const Icon* Function,+,icon_animation_free,void,IconAnimation* -Function,+,icon_animation_get_height,uint8_t,IconAnimation* -Function,+,icon_animation_get_width,uint8_t,IconAnimation* -Function,+,icon_animation_is_last_frame,_Bool,IconAnimation* +Function,+,icon_animation_get_height,uint8_t,const IconAnimation* +Function,+,icon_animation_get_width,uint8_t,const IconAnimation* +Function,+,icon_animation_is_last_frame,_Bool,const IconAnimation* Function,+,icon_animation_set_update_callback,void,"IconAnimation*, IconAnimationCallback, void*" Function,+,icon_animation_start,void,IconAnimation* Function,+,icon_animation_stop,void,IconAnimation* @@ -1763,7 +1763,7 @@ Function,-,llround,long long int,double Function,-,llroundf,long long int,float Function,-,llroundl,long long int,long double Function,+,loader_get_pubsub,FuriPubSub*,Loader* -Function,+,loader_is_locked,_Bool,Loader* +Function,+,loader_is_locked,_Bool,const Loader* Function,+,loader_lock,_Bool,Loader* Function,+,loader_show_menu,void, Function,+,loader_start,LoaderStatus,"Loader*, const char*, const char*" @@ -2372,11 +2372,11 @@ Function,-,scalbnl,long double,"long double, int" Function,-,scanf,int,"const char*, ..." Function,+,scene_manager_alloc,SceneManager*,"const SceneManagerHandlers*, void*" Function,+,scene_manager_free,void,SceneManager* -Function,+,scene_manager_get_scene_state,uint32_t,"SceneManager*, uint32_t" +Function,+,scene_manager_get_scene_state,uint32_t,"const SceneManager*, uint32_t" Function,+,scene_manager_handle_back_event,_Bool,SceneManager* Function,+,scene_manager_handle_custom_event,_Bool,"SceneManager*, uint32_t" Function,+,scene_manager_handle_tick_event,void,SceneManager* -Function,+,scene_manager_has_previous_scene,_Bool,"SceneManager*, uint32_t" +Function,+,scene_manager_has_previous_scene,_Bool,"const SceneManager*, uint32_t" Function,+,scene_manager_next_scene,void,"SceneManager*, uint32_t" Function,+,scene_manager_previous_scene,_Bool,SceneManager* Function,+,scene_manager_search_and_switch_to_another_scene,_Bool,"SceneManager*, uint32_t" @@ -2895,11 +2895,11 @@ Function,+,view_port_alloc,ViewPort*, Function,+,view_port_draw_callback_set,void,"ViewPort*, ViewPortDrawCallback, void*" Function,+,view_port_enabled_set,void,"ViewPort*, _Bool" Function,+,view_port_free,void,ViewPort* -Function,+,view_port_get_height,uint8_t,ViewPort* +Function,+,view_port_get_height,uint8_t,const ViewPort* Function,+,view_port_get_orientation,ViewPortOrientation,const ViewPort* -Function,+,view_port_get_width,uint8_t,ViewPort* +Function,+,view_port_get_width,uint8_t,const ViewPort* Function,+,view_port_input_callback_set,void,"ViewPort*, ViewPortInputCallback, void*" -Function,+,view_port_is_enabled,_Bool,ViewPort* +Function,+,view_port_is_enabled,_Bool,const ViewPort* Function,+,view_port_set_height,void,"ViewPort*, uint8_t" Function,+,view_port_set_orientation,void,"ViewPort*, ViewPortOrientation" Function,+,view_port_set_width,void,"ViewPort*, uint8_t" diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 8dba8327f..42b854799 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -103,7 +103,7 @@ bool furi_hal_spi_bus_rx( bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout) { furi_assert(handle); @@ -128,7 +128,7 @@ bool furi_hal_spi_bus_tx( bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) { diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index 79e809317..af15a8838 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -85,7 +85,7 @@ bool furi_hal_spi_bus_rx( */ bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout); @@ -101,7 +101,7 @@ bool furi_hal_spi_bus_tx( */ bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout); From 82c730b6bec8095fea3aef1aaf2014212b90410a Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 9 Feb 2023 09:45:30 +0400 Subject: [PATCH 137/231] SubGhz: fix cc1101_read_fifo func (#2379) --- lib/drivers/cc1101.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/drivers/cc1101.c b/lib/drivers/cc1101.c index d563c30c3..d0feb0218 100644 --- a/lib/drivers/cc1101.c +++ b/lib/drivers/cc1101.c @@ -150,9 +150,8 @@ uint8_t cc1101_write_fifo(FuriHalSpiBusHandle* handle, const uint8_t* data, uint } uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* size) { - uint8_t buff_tx[64]; - buff_tx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; - uint8_t buff_rx[2]; + uint8_t buff_trx[2]; + buff_trx[0] = CC1101_FIFO | CC1101_READ | CC1101_BURST; // Start transaction // Wait IC to become ready @@ -160,15 +159,15 @@ uint8_t cc1101_read_fifo(FuriHalSpiBusHandle* handle, uint8_t* data, uint8_t* si ; // First byte - packet length - furi_hal_spi_bus_trx(handle, buff_tx, buff_rx, 2, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, buff_trx, buff_trx, 2, CC1101_TIMEOUT); // Check that the packet is placed in the receive buffer - if(buff_rx[1] > 64) { + if(buff_trx[1] > 64) { *size = 64; } else { - *size = buff_rx[1]; + *size = buff_trx[1]; } - furi_hal_spi_bus_trx(handle, &buff_tx[1], data, *size, CC1101_TIMEOUT); + furi_hal_spi_bus_trx(handle, NULL, data, *size, CC1101_TIMEOUT); return *size; -} +} \ No newline at end of file From 67c2d1cf6144da79e0077427b6ca09ee85f730ea Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Thu, 9 Feb 2023 13:42:41 +0300 Subject: [PATCH 138/231] Migrating CI/CD to Linode S3 (#2380) * Test PVS linode S3 * Migrating to Linode S3 * Disable PVS action debug * Fix pvs_studio.yml --- .github/workflows/build.yml | 11 +++++------ .github/workflows/pvs_studio.yml | 21 ++++++++++----------- scripts/merge_report_qa.py | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be9817684..689dd2037 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,14 +96,14 @@ jobs: - name: 'Upload map analyser files to storage' if: ${{ !github.event.pull_request.head.repo.fork }} - uses: keithweaver/aws-s3-github-action@v1.0.0 + uses: prewk/s3-cp-action@v2 with: - source: map_analyser_files/ - destination: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" + aws_s3_endpoint: "${{ secrets.MAP_REPORT_AWS_ENDPOINT }}" aws_access_key_id: "${{ secrets.MAP_REPORT_AWS_ACCESS_KEY }}" aws_secret_access_key: "${{ secrets.MAP_REPORT_AWS_SECRET_KEY }}" - aws_region: "${{ secrets.MAP_REPORT_AWS_REGION }}" - flags: --recursive + source: "./map_analyser_files/" + dest: "s3://${{ secrets.MAP_REPORT_AWS_BUCKET }}/${{steps.names.outputs.random_hash}}" + flags: "--recursive --acl public-read" - name: 'Trigger map file reporter' if: ${{ !github.event.pull_request.head.repo.fork }} @@ -114,7 +114,6 @@ jobs: event-type: map-file-analyse client-payload: '{"random_hash": "${{steps.names.outputs.random_hash}}", "event_type": "${{steps.names.outputs.event_type}}"}' - - name: 'Upload artifacts to update server' if: ${{ !github.event.pull_request.head.repo.fork }} run: | diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml index a4ac6e301..65a8b6150 100644 --- a/.github/workflows/pvs_studio.yml +++ b/.github/workflows/pvs_studio.yml @@ -54,17 +54,16 @@ jobs: ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT - - name: 'Upload artifacts to update server' + - name: 'Upload report' if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} - run: | - mkdir -p ~/.ssh - ssh-keyscan -p ${{ secrets.RSYNC_DEPLOY_PORT }} -H ${{ secrets.RSYNC_DEPLOY_HOST }} > ~/.ssh/known_hosts - echo "${{ secrets.RSYNC_DEPLOY_KEY }}" > deploy_key; - chmod 600 ./deploy_key; - rsync -avrzP --mkpath \ - -e 'ssh -p ${{ secrets.RSYNC_DEPLOY_PORT }} -i ./deploy_key' \ - build/f7-firmware-DC/pvsreport/ ${{ secrets.RSYNC_DEPLOY_USER }}@${{ secrets.RSYNC_DEPLOY_HOST }}:/home/data/firmware-pvs-studio-report/"${BRANCH_NAME}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/"; - rm ./deploy_key; + uses: prewk/s3-cp-action@v2 + with: + aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" + aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" + aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" + source: "./build/f7-firmware-DC/pvsreport" + dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" + flags: "--recursive --acl public-read" - name: 'Find Previous Comment' if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} @@ -83,7 +82,7 @@ jobs: issue-number: ${{ github.event.pull_request.number }} body: | **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** - - [Report](https://update.flipperzero.one/builds/firmware-pvs-studio-report/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) + - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) edit-mode: replace - name: 'Raise exception' diff --git a/scripts/merge_report_qa.py b/scripts/merge_report_qa.py index c0707848e..caa742408 100755 --- a/scripts/merge_report_qa.py +++ b/scripts/merge_report_qa.py @@ -17,7 +17,7 @@ def parse_args(): def checkCommitMessage(msg): - regex = re.compile(r"^'?\[FL-\d+\]") + regex = re.compile(r"^'?\[(FL-\d+,?\s?)+\]") if regex.match(msg): return True return False From af869ef8d2f62d878a8571f29e7bfb2230aa6db7 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Thu, 9 Feb 2023 18:34:56 +0100 Subject: [PATCH 139/231] Merging OFW | Suffering | Part 1 | Wont build as is I honestly dont even know anymore... --- .github/workflows/pvs_studio.yml | 93 ++ .../debug/example_custom_font/application.fam | 9 + .../example_custom_font/example_custom_font.c | 98 ++ .../examples/example_thermo/README.md | 44 + .../examples/example_thermo/application.fam | 10 + .../examples/example_thermo/example_thermo.c | 356 ++++++ .../example_thermo/example_thermo_10px.png | Bin 0 -> 7293 bytes .../resources/badusb/assets/layouts/ba-BA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/cz_CS.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/da-DA.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/de-DE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/dvorak.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-UK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/en-US.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/es-ES.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-BE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-CH.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/fr-FR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hr-HR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/hu-HU.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/it-IT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nb-NO.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/nl-NL.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-BR.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/pt-PT.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/si-SI.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sk-SK.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/sv-SE.kl | Bin 0 -> 256 bytes .../resources/badusb/assets/layouts/tr-TR.kl | Bin 0 -> 256 bytes assets/resources/infrared/assets/projector.ir | 829 ++++++++++++ assets/resources/subghz/assets/alutech_at_4n | 6 + .../unit_tests/subghz/alutech_at_4n_raw.sub | 10 + assets/unit_tests/subghz/dooya.sub | 7 + assets/unit_tests/subghz/dooya_raw.sub | 8 + .../subghz/kinggates_stylo4k_raw.sub | 11 + assets/unit_tests/subghz/linear_delta3.sub | 7 + .../unit_tests/subghz/linear_delta3_raw.sub | 8 + assets/unit_tests/subghz/nice_one_raw.sub | 12 + documentation/UniversalRemotes.md | 76 ++ firmware/targets/f7/api_symbols.csv | 75 +- firmware/targets/f7/fatfs/sd_spi_io.c | 877 +++++++++++++ firmware/targets/f7/fatfs/sd_spi_io.h | 157 +++ firmware/targets/f7/furi_hal/furi_hal_spi.c | 235 +++- .../targets/furi_hal_include/furi_hal_spi.h | 25 +- lib/subghz/blocks/encoder.c | 25 +- lib/subghz/blocks/encoder.h | 11 +- lib/subghz/environment.c | 16 + lib/subghz/environment.h | 17 + lib/subghz/protocols/alutech_at_4n.c | 455 +++++++ lib/subghz/protocols/alutech_at_4n.h | 74 ++ lib/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ lib/subghz/protocols/bin_raw.h | 111 ++ lib/subghz/protocols/chamberlain_code.c | 5 +- lib/subghz/protocols/dooya.c | 447 +++++++ lib/subghz/protocols/dooya.h | 107 ++ lib/subghz/protocols/keeloq.c | 13 +- lib/subghz/protocols/kinggates_stylo_4k.c | 336 +++++ lib/subghz/protocols/kinggates_stylo_4k.h | 74 ++ lib/subghz/protocols/linear_delta3.c | 359 ++++++ lib/subghz/protocols/linear_delta3.h | 111 ++ lib/subghz/protocols/nice_flor_s.c | 167 ++- lib/subghz/protocols/protocol_items.c | 57 +- lib/subghz/protocols/protocol_items.h | 4 + lib/subghz/protocols/secplus_v1.c | 4 +- lib/subghz/protocols/somfy_keytis.c | 366 +++++- lib/subghz/protocols/somfy_keytis.h | 52 + lib/subghz/protocols/somfy_telis.c | 298 ++++- lib/subghz/protocols/somfy_telis.h | 52 + lib/subghz/subghz_setting.c | 5 +- lib/subghz/subghz_tx_rx_worker.c | 10 +- lib/subghz/types.h | 1 + 72 files changed, 7144 insertions(+), 106 deletions(-) create mode 100644 .github/workflows/pvs_studio.yml create mode 100644 applications/debug/example_custom_font/application.fam create mode 100644 applications/debug/example_custom_font/example_custom_font.c create mode 100644 applications/examples/example_thermo/README.md create mode 100644 applications/examples/example_thermo/application.fam create mode 100644 applications/examples/example_thermo/example_thermo.c create mode 100644 applications/examples/example_thermo/example_thermo_10px.png create mode 100644 assets/resources/badusb/assets/layouts/ba-BA.kl create mode 100644 assets/resources/badusb/assets/layouts/cz_CS.kl create mode 100644 assets/resources/badusb/assets/layouts/da-DA.kl create mode 100644 assets/resources/badusb/assets/layouts/de-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/de-DE.kl create mode 100644 assets/resources/badusb/assets/layouts/dvorak.kl create mode 100644 assets/resources/badusb/assets/layouts/en-UK.kl create mode 100644 assets/resources/badusb/assets/layouts/en-US.kl create mode 100644 assets/resources/badusb/assets/layouts/es-ES.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-BE.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-CH.kl create mode 100644 assets/resources/badusb/assets/layouts/fr-FR.kl create mode 100644 assets/resources/badusb/assets/layouts/hr-HR.kl create mode 100644 assets/resources/badusb/assets/layouts/hu-HU.kl create mode 100644 assets/resources/badusb/assets/layouts/it-IT.kl create mode 100644 assets/resources/badusb/assets/layouts/nb-NO.kl create mode 100644 assets/resources/badusb/assets/layouts/nl-NL.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-BR.kl create mode 100644 assets/resources/badusb/assets/layouts/pt-PT.kl create mode 100644 assets/resources/badusb/assets/layouts/si-SI.kl create mode 100644 assets/resources/badusb/assets/layouts/sk-SK.kl create mode 100644 assets/resources/badusb/assets/layouts/sv-SE.kl create mode 100644 assets/resources/badusb/assets/layouts/tr-TR.kl create mode 100644 assets/resources/infrared/assets/projector.ir create mode 100644 assets/resources/subghz/assets/alutech_at_4n create mode 100644 assets/unit_tests/subghz/alutech_at_4n_raw.sub create mode 100644 assets/unit_tests/subghz/dooya.sub create mode 100644 assets/unit_tests/subghz/dooya_raw.sub create mode 100644 assets/unit_tests/subghz/kinggates_stylo4k_raw.sub create mode 100644 assets/unit_tests/subghz/linear_delta3.sub create mode 100644 assets/unit_tests/subghz/linear_delta3_raw.sub create mode 100644 assets/unit_tests/subghz/nice_one_raw.sub create mode 100644 documentation/UniversalRemotes.md create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.c create mode 100644 firmware/targets/f7/fatfs/sd_spi_io.h create mode 100644 lib/subghz/protocols/alutech_at_4n.c create mode 100644 lib/subghz/protocols/alutech_at_4n.h create mode 100644 lib/subghz/protocols/bin_raw.c create mode 100644 lib/subghz/protocols/bin_raw.h create mode 100644 lib/subghz/protocols/dooya.c create mode 100644 lib/subghz/protocols/dooya.h create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.c create mode 100644 lib/subghz/protocols/kinggates_stylo_4k.h create mode 100644 lib/subghz/protocols/linear_delta3.c create mode 100644 lib/subghz/protocols/linear_delta3.h diff --git a/.github/workflows/pvs_studio.yml b/.github/workflows/pvs_studio.yml new file mode 100644 index 000000000..65a8b6150 --- /dev/null +++ b/.github/workflows/pvs_studio.yml @@ -0,0 +1,93 @@ +name: 'Static C/C++ analysis with PVS-Studio' + +on: + push: + branches: + - dev + - "release*" + tags: + - '*' + pull_request: + +env: + TARGETS: f7 + DEFAULT_TARGET: f7 + FBT_TOOLCHAIN_PATH: /runner/_work + +jobs: + analyse_c_cpp: + if: ${{ !github.event.pull_request.head.repo.fork }} + runs-on: [self-hosted, FlipperZeroShell] + steps: + - name: 'Decontaminate previous build leftovers' + run: | + if [ -d .git ]; then + git submodule status || git checkout "$(git rev-list --max-parents=0 HEAD | tail -n 1)" + fi + + - name: 'Checkout code' + uses: actions/checkout@v3 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: 'Get commit details' + id: names + run: | + if [[ ${{ github.event_name }} == 'pull_request' ]]; then + TYPE="pull" + elif [[ "${{ github.ref }}" == "refs/tags/"* ]]; then + TYPE="tag" + else + TYPE="other" + fi + python3 scripts/get_env.py "--event_file=${{ github.event_path }}" "--type=$TYPE" + + - name: 'Supply PVS credentials' + run: | + pvs-studio-analyzer credentials ${{ secrets.PVS_STUDIO_CREDENTIALS }} + + - name: 'Convert PVS-Studio output to html and detect warnings' + id: pvs-warn + run: | + WARNINGS=0 + ./fbt COMPACT=1 PVSNOBROWSER=1 firmware_pvs || WARNINGS=1 + echo "warnings=${WARNINGS}" >> $GITHUB_OUTPUT + + - name: 'Upload report' + if: ${{ !github.event.pull_request.head.repo.fork && (steps.pvs-warn.outputs.warnings != 0) }} + uses: prewk/s3-cp-action@v2 + with: + aws_s3_endpoint: "${{ secrets.PVS_AWS_ENDPOINT }}" + aws_access_key_id: "${{ secrets.PVS_AWS_ACCESS_KEY }}" + aws_secret_access_key: "${{ secrets.PVS_AWS_SECRET_KEY }}" + source: "./build/f7-firmware-DC/pvsreport" + dest: "s3://${{ secrets.PVS_AWS_BUCKET }}/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/" + flags: "--recursive --acl public-read" + + - name: 'Find Previous Comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: 'github-actions[bot]' + body-includes: 'PVS-Studio report for commit' + + - name: 'Create or update comment' + if: ${{ !github.event.pull_request.head.repo.fork && github.event.pull_request && (steps.pvs-warn.outputs.warnings != 0) }} + uses: peter-evans/create-or-update-comment@v1 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + issue-number: ${{ github.event.pull_request.number }} + body: | + **PVS-Studio report for commit `${{steps.names.outputs.commit_sha}}`:** + - [Report](https://pvs.flipp.dev/${{steps.names.outputs.branch_name}}/${{steps.names.outputs.default_target}}-${{steps.names.outputs.suffix}}/index.html) + edit-mode: replace + + - name: 'Raise exception' + if: ${{ steps.pvs-warn.outputs.warnings != 0 }} + run: | + echo "Please fix all PVS warnings before merge" + exit 1 + diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam new file mode 100644 index 000000000..02285b8a0 --- /dev/null +++ b/applications/debug/example_custom_font/application.fam @@ -0,0 +1,9 @@ +App( + appid="example_custom_font", + name="Example: custom font", + apptype=FlipperAppType.EXTERNAL, + entry_point="example_custom_font_main", + requires=["gui"], + stack_size=1 * 1024, + fap_category="Debug", +) diff --git a/applications/debug/example_custom_font/example_custom_font.c b/applications/debug/example_custom_font/example_custom_font.c new file mode 100644 index 000000000..15eeb5f02 --- /dev/null +++ b/applications/debug/example_custom_font/example_custom_font.c @@ -0,0 +1,98 @@ +#include +#include + +#include +#include + +//This arrays contains the font itself. You can use any u8g2 font you want + +/* +Fontname: -Raccoon-Fixed4x6-Medium-R-Normal--6-60-75-75-P-40-ISO10646-1 +Copyright: +Glyphs: 95/203 +BBX Build Mode: 0 +*/ +const uint8_t u8g2_font_tom_thumb_4x6_tr[725] = + "_\0\2\2\2\3\3\4\4\3\6\0\377\5\377\5\0\0\352\1\330\2\270 \5\340\315\0!\6\265\310" + "\254\0\42\6\213\313$\25#\10\227\310\244\241\206\12$\10\227\310\215\70b\2%\10\227\310d\324F\1" + "&\10\227\310(\65R\22'\5\251\313\10(\6\266\310\251\62)\10\226\310\304\224\24\0*\6\217\312\244" + "\16+\7\217\311\245\225\0,\6\212\310)\0-\5\207\312\14.\5\245\310\4/\7\227\310Ve\4\60" + "\7\227\310-k\1\61\6\226\310\255\6\62\10\227\310h\220\312\1\63\11\227\310h\220\62X\0\64\10\227" + "\310$\65b\1\65\10\227\310\214\250\301\2\66\10\227\310\315\221F\0\67\10\227\310\314TF\0\70\10\227" + "\310\214\64\324\10\71\10\227\310\214\64\342\2:\6\255\311\244\0;\7\222\310e\240\0<\10\227\310\246\32" + "d\20=\6\217\311l\60>\11\227\310d\220A*\1\77\10\227\310\314\224a\2@\10\227\310UC\3" + "\1A\10\227\310UC\251\0B\10\227\310\250\264\322\2C\7\227\310\315\32\10D\10\227\310\250d-\0" + "E\10\227\310\214\70\342\0F\10\227\310\214\70b\4G\10\227\310\315\221\222\0H\10\227\310$\65\224\12" + "I\7\227\310\254X\15J\7\227\310\226\252\2K\10\227\310$\265\222\12L\7\227\310\304\346\0M\10\227" + "\310\244\61\224\12N\10\227\310\244q\250\0O\7\227\310UV\5P\10\227\310\250\264b\4Q\10\227\310" + "Uj$\1R\10\227\310\250\64V\1S\10\227\310m\220\301\2T\7\227\310\254\330\2U\7\227\310$" + "W\22V\10\227\310$\253L\0W\10\227\310$\65\206\12X\10\227\310$\325R\1Y\10\227\310$U" + "V\0Z\7\227\310\314T\16[\7\227\310\214X\16\134\10\217\311d\220A\0]\7\227\310\314r\4^" + "\5\213\313\65_\5\207\310\14`\6\212\313\304\0a\7\223\310\310\65\2b\10\227\310D\225\324\2c\7" + "\223\310\315\14\4d\10\227\310\246\245\222\0e\6\223\310\235\2f\10\227\310\246\264b\2g\10\227\307\35" + "\61%\0h\10\227\310D\225\254\0i\6\265\310\244\1j\10\233\307f\30U\5k\10\227\310\304\264T" + "\1l\7\227\310\310\326\0m\7\223\310 +#include + +#include +#include + +#include + +#include +#include + +#define UPDATE_PERIOD_MS 1000UL +#define TEXT_STORE_SIZE 64U + +#define DS18B20_CMD_CONVERT 0x44U +#define DS18B20_CMD_READ_SCRATCHPAD 0xbeU + +#define DS18B20_CFG_RESOLUTION_POS 5U +#define DS18B20_CFG_RESOLUTION_MASK 0x03U +#define DS18B20_DECIMAL_PART_MASK 0x0fU + +#define DS18B20_SIGN_MASK 0xf0U + +/* Possible GPIO pin choices: + - gpio_ext_pc0 + - gpio_ext_pc1 + - gpio_ext_pc3 + - gpio_ext_pb2 + - gpio_ext_pb3 + - gpio_ext_pa4 + - gpio_ext_pa6 + - gpio_ext_pa7 + - ibutton_gpio +*/ + +#define THERMO_GPIO_PIN (ibutton_gpio) + +/* Flags which the reader thread responds to */ +typedef enum { + ReaderThreadFlagExit = 1, +} ReaderThreadFlag; + +typedef union { + struct { + uint8_t temp_lsb; /* Least significant byte of the temperature */ + uint8_t temp_msb; /* Most significant byte of the temperature */ + uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ + uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ + uint8_t config; /* Configuration register */ + uint8_t reserved[3]; /* Not used */ + uint8_t crc; /* CRC checksum for error detection */ + } fields; + uint8_t bytes[9]; +} DS18B20Scratchpad; + +/* Application context structure */ +typedef struct { + Gui* gui; + ViewPort* view_port; + FuriThread* reader_thread; + FuriMessageQueue* event_queue; + OneWireHost* onewire; + float temp_celsius; + bool has_device; +} ExampleThermoContext; + +/*************** 1-Wire Communication and Processing *****************/ + +/* Commands the thermometer to begin measuring the temperature. */ +static void example_thermo_request_temperature(ExampleThermoContext* context) { + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) break; + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + /* After the ROM operation, a device-specific command is issued. + In this case, it's a request to start measuring the temperature. */ + onewire_host_write(onewire, DS18B20_CMD_CONVERT); + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Reads the measured temperature from the thermometer. */ +static void example_thermo_read_temperature(ExampleThermoContext* context) { + /* If there was no device detected, don't try to read the temperature */ + if(!context->has_device) { + return; + } + + OneWireHost* onewire = context->onewire; + + /* All 1-wire transactions must happen in a critical section, i.e + not interrupted by other threads. */ + FURI_CRITICAL_ENTER(); + + bool success = false; + + do { + DS18B20Scratchpad buf; + + /* Attempt reading the temperature 10 times before giving up */ + size_t attempts_left = 10; + do { + /* Each communication with a 1-wire device starts by a reset. + The functon will return true if a device responded with a presence pulse. */ + if(!onewire_host_reset(onewire)) continue; + + /* After the reset, a ROM operation must follow. + If there is only one device connected, the "Skip ROM" command is most appropriate + (it can also be used to address all of the connected devices in some cases).*/ + onewire_host_skip(onewire); + + /* After the ROM operation, a device-specific command is issued. + This time, it will be the "Read Scratchpad" command which will + prepare the device's internal buffer memory for reading. */ + onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); + + /* The actual reading happens here. A total of 9 bytes is read. */ + onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); + + /* Calculate the checksum and compare it with one provided by the device. */ + const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); + + /* Checksums match, exit the loop */ + if(crc == buf.fields.crc) break; + + } while(--attempts_left); + + if(attempts_left == 0) break; + + /* Get the measurement resolution from the configuration register. (See [1] page 9) */ + const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & + DS18B20_CFG_RESOLUTION_MASK; + + /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ + const uint8_t decimal_mask = + (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & + DS18B20_DECIMAL_PART_MASK; + + /* Get the integer and decimal part of the temperature (See [1] page 6) */ + const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); + const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; + + /* Calculate the sign of the temperature (See [1] page 6) */ + const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; + + /* Combine the integer and decimal part together */ + const float temp_celsius_abs = integer_part + decimal_part / 16.f; + + /* Set the appropriate sign */ + context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; + + success = true; + } while(false); + + context->has_device = success; + + FURI_CRITICAL_EXIT(); +} + +/* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ +static int32_t example_thermo_reader_thread_callback(void* ctx) { + ExampleThermoContext* context = ctx; + + for(;;) { + /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ + example_thermo_request_temperature(context); + + /* Wait for the measurement to finish. At the same time wait for an exit signal. */ + const uint32_t flags = + furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); + + /* If an exit signal was received, return from this thread. */ + if(flags != (unsigned)FuriFlagErrorTimeout) break; + + /* The measurement is now ready, read it from the termometer. */ + example_thermo_read_temperature(context); + } + + return 0; +} + +/*************** GUI, Input and Main Loop *****************/ + +/* Draw the GUI of the application. The screen is completely redrawn during each call. */ +static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { + ExampleThermoContext* context = ctx; + char text_store[TEXT_STORE_SIZE]; + const size_t middle_x = canvas_width(canvas) / 2U; + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); + canvas_draw_line(canvas, 0, 16, 128, 16); + + canvas_set_font(canvas, FontSecondary); + canvas_draw_str_aligned( + canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer"); + + snprintf( + text_store, + TEXT_STORE_SIZE, + "to GPIO pin %ld", + furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); + canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); + + canvas_set_font(canvas, FontKeyboard); + + if(context->has_device) { + float temp; + char temp_units; + + /* The applicaton is locale-aware. + Change Settings->System->Units to check it out. */ + switch(locale_get_measurement_unit()) { + case LocaleMeasurementUnitsMetric: + temp = context->temp_celsius; + temp_units = 'C'; + break; + case LocaleMeasurementUnitsImperial: + temp = locale_celsius_to_fahrenheit(context->temp_celsius); + temp_units = 'F'; + break; + default: + furi_crash("Illegal measurement units"); + } + /* If a reading is available, display it */ + snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); + } else { + /* Or show a message that no data is available */ + strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); + } + + canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); +} + +/* This function is called from the GUI thread. All it does is put the event + into the application's queue so it can be processed later. */ +static void example_thermo_input_callback(InputEvent* event, void* ctx) { + ExampleThermoContext* context = ctx; + furi_message_queue_put(context->event_queue, event, FuriWaitForever); +} + +/* Starts the reader thread and handles the input */ +static void example_thermo_run(ExampleThermoContext* context) { + /* Configure the hardware in host mode */ + onewire_host_start(context->onewire); + + /* Start the reader thread. It will talk to the thermometer in the background. */ + furi_thread_start(context->reader_thread); + + /* An endless loop which handles the input*/ + for(bool is_running = true; is_running;) { + InputEvent event; + /* Wait for an input event. Input events come from the GUI thread via a callback. */ + const FuriStatus status = + furi_message_queue_get(context->event_queue, &event, FuriWaitForever); + + /* This application is only interested in short button presses. */ + if((status != FuriStatusOk) || (event.type != InputTypeShort)) { + continue; + } + + /* When the user presses the "Back" button, break the loop and exit the application. */ + if(event.key == InputKeyBack) { + is_running = false; + } + } + + /* Signal the reader thread to cease operation and exit */ + furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); + + /* Wait for the reader thread to finish */ + furi_thread_join(context->reader_thread); + + /* Reset the hardware */ + onewire_host_stop(context->onewire); +} + +/******************** Initialisation & startup *****************************/ + +/* Allocate the memory and initialise the variables */ +static ExampleThermoContext* example_thermo_context_alloc() { + ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); + + context->view_port = view_port_alloc(); + view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); + view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); + + context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); + + context->reader_thread = furi_thread_alloc(); + furi_thread_set_stack_size(context->reader_thread, 1024U); + furi_thread_set_context(context->reader_thread, context); + furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); + + context->gui = furi_record_open(RECORD_GUI); + gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); + + context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); + + return context; +} + +/* Release the unused resources and deallocate memory */ +static void example_thermo_context_free(ExampleThermoContext* context) { + view_port_enabled_set(context->view_port, false); + gui_remove_view_port(context->gui, context->view_port); + + onewire_host_free(context->onewire); + furi_thread_free(context->reader_thread); + furi_message_queue_free(context->event_queue); + view_port_free(context->view_port); + + furi_record_close(RECORD_GUI); +} + +/* The application's entry point. Execution starts from here. */ +int32_t example_thermo_main(void* p) { + UNUSED(p); + + /* Allocate all of the necessary structures */ + ExampleThermoContext* context = example_thermo_context_alloc(); + + /* Start the applicaton's main loop. It won't return until the application was requested to exit. */ + example_thermo_run(context); + + /* Release all unneeded resources */ + example_thermo_context_free(context); + + return 0; +} diff --git a/applications/examples/example_thermo/example_thermo_10px.png b/applications/examples/example_thermo/example_thermo_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3d527f306c2d325fc72380856f14860f51b3742c GIT binary patch literal 7293 zcmeHMcTkhr*A7x6NLK_ALQoVWh4chN3sne3kSb^z5F`mCp^7v?QHrvl6hTCBL5hNu zr78#tyMiuNk+Rqj6ahuC!}o%&>(2L^`DSOn-+!B#G&lf!DOee-p$;^GBJ^qYwG zu0k1IO#B$Gb(B+3AoDV=`A*lv!-15DO|QA$l_Ms@Xll-MW5R=b!OPJNaUtyR1q|np zl$|khVskbfHgkL54QYnI8?OL|f0;``)KARM?tEzZLbc#alyTGpbR5O!!}Ma7!G%KS zbqHQ$%Fu^juNm>qURXbR?WQr2IF^REyJ^!tD^}wYia+t?SMu57Kz!QS<}1sc@*093 zcMCB1vi(7)XS*l2jk*~m$o5D$+e$u+NdV_wNl_6kS?82-Ve|SczSSX8_bS_t!$R*H zo#?^Y8Ps#t=}+?x+_{^Pa-Vgt`4(d8wyc%WK~wd79(ty&X=Lw=I!?2vyLY2@^394n zH?nU$e3A8GQIg@W=YDX$$1OWc@I26tqFp>!zfPfwVTiStEV%N_IJL}5>_Vi6^Op9i zkerg<@toGXCI?412JfAw+ti8FOHr)sZbh1doT`l;Fy;7Z<%!y=B6``43vGMMTjT4* zGK6iP67E;_7rs)uy5Op^*?v==WR8|%?>2`d>Y{ktTCFw4${RQ8^;(_WZ-~d{L2mmorjXS3$w~=No55t?cc(^BS00WNY+frmo;lgK~<;{)UNr;`q$n-fyu3 z{sE;f4U?W~6a8k%Fl@jmf5)Q^>GzbMPF6B?$3jgm9o0Dae4yc(kHk4#vns74U1FU^ zM5fn3sC!KFwWQoJCjokQr1sePBh}``;4+~i)>mp{X_7^U4FzOkm^$2s6Dv;d&)z(& zwc~vImJ7G+$}MFSWX2i^GpdGnb&&lB zmU7H8L<;4bOzuqR7pezkl4c4gNyQD_r@gl-D;42>(vhtSu0Jf1eQdnPl@u{9f zLQCWGSgGlB^mETpTD_R{e0@CS^?KD!D)YY`lgZqbbgQ@R_f}K1BgWAf+%Rst84S+w zc6oK7(Clo3?2?I@lpShl_ISBeL9a&@+c#07DpE5~Rc*AeO|}2biK?7*1BSR|XLeki z_CtHZ`r?3#vpUKUk;xm#ksA7Gl5y;?*0Q-Rto3{>m+QoLXDJedTbD})Kk(}RlFgfOL}r|8GL)3T^cq^bl8wwcwp}nx0CxLJG+u7rjg)I<;k(_ zpCpodaa77hDN{rA{R8$*?wQp(JK1rVJ4}+=+9xQVv^>4-R{0$4b)EJEj)U{-nqryg z>XOB}T8^zq6Q^w$>E}k6TjX2#Ug+HMo45SJnCWS-={-4uZj;Z%tMQ3|C!DJJ47SYl z30FqKlZ$S_G3A`e_$n_QPD*~x8&Fjkm}9hmMuh(Us%hxqXQEx*pQ3#7Uh8idXNyT>!wdzf!94d2>*le-8)<2dA-KX~{cdO9a2vx7ahP#xHn&n33 zqY1<&ZU3YB1}$1}lc&^5SLa+AQ@?uz6meUe4gg zqtq=$R9GUju}J|~%h?uTCY0*kIfcC#ba>mEEpYW07wwu4A-)(XJh0g|`G?{IFm8bd zekqn4(IoT1?t}CK{A@PflOP1|G`%j}IVU~y(jP9qSg^B5e33&xlO?|B7jbC%!T}C; z?)AsQW-}Dt;qg@KUULNWWV~a%=S_)d4~QDoawH+Rd@ZrBg(&B)*E5_hUeV)v#X0Cw zv=_!_=Ji^eJ+SH;;KWdi6E(v)bCuTr6cbJ%CGELgEw*m zOB0=bilQcqw`!r;ruxCjzuh0a#1HsQy*Q}N4grC^Z{4-1bK);N8;|Fwhck!lcPQ1xII?EOIU2PA;+dFNpzugYovG_`D`i}mj+=maZ{g>cLn&g1AnBHz5;_c5zYNFdcCZ+wN zp6D-Z>iZ~CreY!Wxpm{lQz3h`WrymNWv43J zj~`31)D2x4o;3<>IzD?&bn&JodN`!kaCqI-{IEL7#y1lxi@iGPqle>!hc>Of`=vg@ zziX~rP%w0MfUdEn>$YB^azkfcy5{dQ@-`=58f9&mc|P!Eyib0*sxS^tJTY)ksJExk zq*I~(MMiFs!zO6QP0cjY!Z7^Ir6*~Ze=~l&m3iLceOu+hh4Z@0zA91Ejj80|0fAJ} zSopwEjevnX&1emq%J&T+MfW3HB3wpnwpe=2-!p$5o#bY<@Y*cUzsG4Va9X~ovE5Zn zQ_v<}Z!EK|-my$6I9k_v3#V@C%+DC{l3Hw+z^dF#!g+aYD`-l!ex~GkZOR_VUGCh>Ti@`k^A=Z*N-f9d z9NApWTs~~z4a+pEPWG%&x}m#8ueq=90VOZ5hj&S4N1m5G_%kw=zM%l+DXy#Qc17pR zi7CYkVi}b|w=ZT!Z!xWM^++{0Hx->feU%se7JNX|Kj^aaeI*3z_h&At0iSDoi_~I% zRq_8^gN+uWo*lc>>OjsMY8kQVp-76{O1R}PlJ8kc_21{#)DEU6`~K3O2}3>_XReEW zdMp*~AeMvLRC%4tR{z*)k_B$9n1x*_F}(~cP25{{uV;KmJ-()P&SzqF+VhWYKPOr2 zE&pQgk}iDIthQ7xM_GN;nPJgs?NZZ#oClxuNju^x{`NQ&T0?l&P>K>WqM=Jv+jKMo)yj(+Hab6*|#!2eGpLjhvq|zy@c$)*tjy zxPiQpq@{-fm6T3rBA8@XA}@GI4?EPj zErz!J$`yS<{-o4T?j4F-k{lf=s_XA3^_OPQwWy!Kn#(6&WOZFUStetdChk&b8v11- zX1>X*3`u;rHBr&zUGn3oj@Yx_=RCsl<`|vqv!FJ=iCw%;W$X4&X1s~?O4bm0$eDQ5 zSfP8c^{FiKJ+tfQnO&y2W`&^Uf?oX7*f_6qyq*^owI})|Ar$Y6+}OjE{WaFZFO249 zU(USz16{Ec4JxnaUmMB7Z3cm4z1YCD%bjXRV(>U{8k0w7!NWO0z_knn+G!jfL}MIa z2_bZrADc^t&NW?!LfA|))D25TQiCWgf3|fbpXD5B@4|>Yz#uZA#zt#*hLZpS4ogUb zgmVJ90#Y~`y2?ue+AC%R6tW5t9w0;Asg4i|kI#Z&;21a(W)aQ~ML~_$LU!_*zNFpe zmft9V9vSK{6b6wHh_J9Qco-Va|Ed z`R34nd_&*@++Gp8Spr@NpTV*SWpRbtKT=rPP#wQBtc2vp<^-(<1&H^z$(W4qxS$Yz z;3|g6K(GQ?9Dq~+_-F7pypZktmso%EZDnLNoF5$lru)wSH}s#euYv&-l}a+_F+x@* zwJ|3{SNtV0c?>p_wAw^6XapjjhJoRIfmC4VEHsRO!eU`49F2fOV(Azbo%REj4Obwf zaT%-?Du5i$26$LZ100cwVZazTA|8feFz_%M4TXj=eDOFI3x&ZTnZ7?zIPlp(Rnh`~ z^lF8Q2~Zgja5N$Yg@Iu)3?vLg#Q4I97z_%ACE(~N7K@1?5Ll~JD|sN9I@*w-C^+&j zi(?>7=*#1C$WS{rHzfS80T(uhU%P?q)ujZ1tS$-?jqx=E0WFlpT=f&+`Z~n$r*ZvQ z!0z#_T>rGQ|3fLD(ReH#V}OS-Ff=R-gJYs$GymMobkHG&{*S~W8BL)5u z_}}XKf0JwNUpGb;7dYsJ0rx?cL&P=UE-B$?=VAdomzS3>4i}68EfKn{l{qL$e6uys z0S8%o2!KBUl~%qYpxnHTfKgItL$#2cl-VSwBf)ExO#w`rHs+=-Qui3|BVr#Wi?+LM n{LmKn@Uh4d70Hbn>L3*8aiyH^d~eG#00r4t*qdKA+aLcw^(CDL literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/ba-BA.kl b/assets/resources/badusb/assets/layouts/ba-BA.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHkcVK4j(2-*&PMujecVX$$ zm1{Rv)^6RqGmA3vIK<6U^hikQGaw^p$cQl$rpzdqv!GYm`cD?ePm*T4GZVB*m| zTln^pGi$gvZr&YZh&>B_Ym yx9;4VM-@dp{CKxUod!)>wCT{LN1p*hMvR#-WyYKZOIEDeuw}=dEbdXpBj69fFAeVi literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-CH.kl b/assets/resources/badusb/assets/layouts/de-CH.kl new file mode 100644 index 0000000000000000000000000000000000000000..1704bc9dbaae9fcc90213cb33b0132bca1b71866 GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5i<8!$oa;st31l>ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/de-DE.kl b/assets/resources/badusb/assets/layouts/de-DE.kl new file mode 100644 index 0000000000000000000000000000000000000000..67b05c042f1649ab6aa17c4d369b096c88ec2785 GIT binary patch literal 256 zcmaLLHx9y30KiboqW2Pd4WuBN!ay1SVRHX7Ftgz88Gbu+;q<%y>dMxg4`L4uK0?Afwu&6#5(8&;hd+qL7+fvJ5TwhjIGHgM$E s3wH`~pT4NjqeYV%GsZ+Ts4^j@ONSu?Qu;h-6Y}9rz=$sw?-ss^@Y|jPyWjO!M`q5wk-2p3gN<8r z&%QYE=EIg5a~5PQS+OQ(!`(YC&%FKkuw>baRci*;Z5Z0LW!sLCv58$%d-feTbmZ8H tQ!f-;S$XnFi82+c)Cj24Af!o)HXR~j61t@H=rdr*h%pnUTr9kL6TT^X3*7(! literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/en-US.kl b/assets/resources/badusb/assets/layouts/en-US.kl new file mode 100644 index 0000000000000000000000000000000000000000..8089d8257881765fe67691a206b6bf6e9cb2c97d GIT binary patch literal 256 zcmaLL#}dH+007aQ9YGK+QKLn)QG!wb|I3_nvA4HS?#PKldHvOyg-=&zuHE=#=iaS_ zZ!UcJvS-1P6&Y)CHf-7P@WI=Hk6)Ko46ItSZfL`%EhF1@?Ao($Y~sM7BgamhnmTjt sl{+_bFTSV{P^CtlkOoa!M6~J9rAMEbgaJcFjF~VcWyYMVcW=Ig9}}MoYybcN literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/es-ES.kl b/assets/resources/badusb/assets/layouts/es-ES.kl new file mode 100644 index 0000000000000000000000000000000000000000..15e9d7997c3f5178982fe212b5341f09469486bc GIT binary patch literal 256 zcmaLLH;%#p6adlDh@5lA#$@e+Y@}VF{DLgOx;B0)}2=l z9xW`m^W?$a6A39BGPdm4bKuCsyDv_@yqZSIvK6b=tlO|@%eEc6_Ut=w=*Y1Xr_P+a zaOujmdHhEaxleOSl&MgqMx6#tTD0lVrAMCuLq?35FlEM^1xr?}`HLiOkp;W~w>u5X literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-BE.kl b/assets/resources/badusb/assets/layouts/fr-BE.kl new file mode 100644 index 0000000000000000000000000000000000000000..ea9e553e894a470639ee48648a386898ed5cfa67 GIT binary patch literal 256 zcmaLLNm9Z96adk#F6Mcj2}uy4P%#!3Wcd%1`=7xz8+-Q}Uc2(a<#qk5PfnzyQSfU* zM$Q+7r6X=Ue0nzVU}|RW(Kk=N`{Y4IN#)2x%&gn6Y0I`9yY}omaOmB!6Q|akyKw2s zwR^Yj+?YodMSOGfCuY=X(4ack{~4%hnC=aHcHxKf=lZH&ZYHL2XXoB4 z2al$1eDLB)=7|jnDO+~z$vAN2>BEAPg;$d(S+-);nspmCZP~VC*PeX`4jnmm;?$XQ z7cO19@-K?GMb69I5@jk>sZpmvlNN0{bm`G&z>pDRCQO+zXTg#+EB+&kd*lIc##;^x literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/fr-FR.kl b/assets/resources/badusb/assets/layouts/fr-FR.kl new file mode 100644 index 0000000000000000000000000000000000000000..f9193297e58722fd4f1547cb9ef62474beb2487b GIT binary patch literal 256 zcmaLLH*Nv}6adlDh@5i{8(2U#EVi(~}3$%7XqF9)7tV%3^;8#Zm(wqw_xeeVt(Ikw`|nR6E| z-MV(;$}Ebw#*KnQOsG<$PJ<>b+H~mBqtBfoBgRyiGGoqyC0o{PSdm5^W$Xj~0L-fm A_W%F@ literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/hr-HR.kl b/assets/resources/badusb/assets/layouts/hr-HR.kl new file mode 100644 index 0000000000000000000000000000000000000000..379f6c649c98ef7f38df9c6d1c2b1588167ee46a GIT binary patch literal 256 zcmaLL%XYy47y#ipa|DTNh+7ar(9@P>V)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk)x=fVv{{L5{w#jy9;k{43_;_Fc>YIyakMR>% z$I*KC;Silq2439!rR7Y=g)29D?mT$;j68aqcv{8C#IX~nre@BZyKw2s+`_dROSkUa zd$6+hXyeH=%E+RN!9GSLjF~VcC1b{%1xr@s6s*}$vSr7f0~Iw#8crlpM-gqnA2aL@ AGynhq literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/it-IT.kl b/assets/resources/badusb/assets/layouts/it-IT.kl new file mode 100644 index 0000000000000000000000000000000000000000..059e428808a07b26d0da37d7f37d5d8d4614365b GIT binary patch literal 256 zcmaKnw++Go0KhCO)P&wbCv;IGQOAf|`2PmP$bvU}GyJk+&-OR{wF48UUbDox3y-W^ zo0@Xx$%CaECQO-;FlWJ%6>A>eeY5fH(MuMXGjGA7CCi3JR;*gHZo{T6+ji{QGq&%* yp(Dqhvm0r4@+k!5$Wx$5i83J(6{^&z)1XO96ECB#K@^L=Pq0t xyK-$Ft`y%!)Nxcub+H~j=&?BVJfFUEsOqeobPMlv%@{{ldJ?{*0 literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/pt-BR.kl b/assets/resources/badusb/assets/layouts/pt-BR.kl new file mode 100644 index 0000000000000000000000000000000000000000..d36421cfc45747687dba3cabe05ff84a4550050a GIT binary patch literal 256 zcmaKnM-ssR004J(1W9z!q7!8hWkx%CjQ_uZtBbwu-WGn@v1j|6{@Q_wH)qaWn0aIE z#+66!wV)?)SQ*~|A*?j|_ee=WD=lWN_RGzFod-3X% zn|EKnXnE7o_@LrM&6x{V8gAU_Y;EIV7f*N5GqLZ$p{bc8$4;C&b8ha!rG+ckZrr-F ybnnrFX%vw~9dDcHk17S!45fq0~DYekR@*l?gpTRX7d-ob{d*zLn+x4&B`SxrR z=Qx?hB0l}{*Orbe7e0Kp_0GKq?|ty-qfcJ^p^Fo{=&Yh=Vrt)knYlwpj-5DlX5rk0 zrAt??-B?-MxN~b3W#qAstHmjD0& literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/sv-SE.kl b/assets/resources/badusb/assets/layouts/sv-SE.kl new file mode 100644 index 0000000000000000000000000000000000000000..5c55bb9ef1df3785fb143eb463a5375be10ce96f GIT binary patch literal 256 zcmaLLH*x|26u?l8hMaSTC2U}9uLl~S{D;~5pMjbNbx-iw55HVL*H`^JB+m2m(_8mBMwCT{LN1p*hMvR#-WyYKZOIEDeuw}=dG^!}#7VrkLXASNE literal 0 HcmV?d00001 diff --git a/assets/resources/badusb/assets/layouts/tr-TR.kl b/assets/resources/badusb/assets/layouts/tr-TR.kl new file mode 100644 index 0000000000000000000000000000000000000000..6377b770365ed544824108765ce2587f315bc1da GIT binary patch literal 256 zcmaLLM{dFZ6adlDh~A6oU~F)Sk{HDcB$od$x&IlmW|P^yf%iW7;^Te&t8Y@DWRb_s zyH`sq8**D7yeOY}m46&w(Q+&Ri(sH|qEa_ygc8 B4s!qi literal 0 HcmV?d00001 diff --git a/assets/resources/infrared/assets/projector.ir b/assets/resources/infrared/assets/projector.ir new file mode 100644 index 000000000..e9861de21 --- /dev/null +++ b/assets/resources/infrared/assets/projector.ir @@ -0,0 +1,829 @@ +Filetype: IR library file +Version: 1 +# +# Model: Smart +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8A 00 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +# Model: Epson +name: Power +type: parsed +protocol: NECext +address: 81 03 00 00 +command: F0 0F 00 00 +# +# Model: Hitatchi +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 310 27591 171 27662 241 27731 307 27575 107 27749 306 27551 130 55520 243 27614 217 55584 129 27743 119 27756 115 27747 163 27712 308 27502 243 27650 217 27732 175 27693 167 27698 166 27689 171 27622 215 27712 133 27658 216 27716 129 27732 162 27698 305 27571 131 27753 310 27570 170 27707 162 27707 175 10960 9194 4518 618 542 618 543 725 434 672 1623 671 1647 646 514 592 568 592 568 592 1702 592 568 592 567 593 1702 592 568 618 1676 618 1676 618 1676 618 543 617 543 617 543 617 1677 617 544 616 544 616 544 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1679 615 1678 616 1678 616 40239 9196 2250 617 +# +name: Vol_up +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 49 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 14 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 08 00 00 00 +command: 0B 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 40 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 48 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 44 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 83 7C 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 82 7D 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 13 00 00 +command: 87 78 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9055 4338 672 1551 669 1553 618 1603 619 481 617 482 616 481 617 507 591 1605 645 479 619 1577 645 1578 644 1578 644 479 619 480 618 1581 641 480 617 1605 617 1606 616 1606 615 483 615 1608 614 484 614 484 614 484 614 484 614 484 614 484 614 1609 614 484 614 1609 614 1609 613 1609 613 40058 9000 2068 614 95467 9022 2068 614 +# +name: Mute +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 29 D6 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 08 F7 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 04 FB 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 93 6C 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4462 598 501 627 1604 627 530 598 531 677 423 706 422 706 421 707 451 677 1554 677 451 598 1633 598 1634 597 1634 598 1634 598 1634 625 1606 681 1550 626 502 598 530 599 529 600 1632 600 528 600 528 601 528 601 528 601 1631 600 1631 625 1607 625 504 625 1607 624 1608 624 1608 623 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 1D 00 00 00 +# +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9096 4436 620 505 647 478 648 501 623 1599 647 1624 623 502 623 503 621 504 619 1628 618 507 617 507 617 1630 617 508 616 1630 617 1630 617 1631 616 508 616 508 617 508 616 1631 616 508 617 508 617 508 616 508 616 1630 616 1630 616 1631 616 508 616 1630 617 1630 617 1630 617 1631 617 509 616 508 616 509 616 509 616 509 616 509 615 509 616 508 617 1631 616 1631 615 1631 616 1631 616 1631 616 1631 616 1631 615 1631 616 14435 9093 2186 615 96359 9095 2184 617 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9091 4465 594 530 595 530 594 530 594 1651 595 1652 595 529 621 504 620 504 619 1628 618 507 617 508 616 1631 616 509 615 1631 616 1631 616 1632 615 509 616 509 616 509 615 1631 616 509 616 508 616 1631 616 509 616 1631 615 1631 616 1631 617 508 616 1631 616 1631 616 508 616 1631 617 508 617 509 616 509 616 509 616 509 616 509 616 509 616 509 616 1631 616 1631 616 1631 616 1631 616 1631 615 1631 615 1631 615 1631 616 14435 9090 2190 615 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 619 506 618 530 593 1627 620 1630 643 504 620 505 618 506 617 1630 617 508 616 508 616 1632 616 508 617 1631 616 1631 616 1631 616 1631 616 509 616 508 616 1631 616 509 616 509 615 1632 616 509 616 508 616 1631 616 1631 616 508 616 1631 615 1631 616 509 615 1632 615 509 616 509 616 509 616 509 616 509 616 510 615 509 616 509 616 1631 616 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 615 14434 9088 2191 615 96339 9115 2189 616 96343 9117 2189 616 96343 9114 2189 616 +# AV-Mute +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9092 4439 620 506 618 506 618 530 594 1627 619 1629 643 505 619 505 619 506 617 1629 617 508 616 508 616 1631 616 508 616 1630 616 1630 616 1630 617 1630 616 1630 616 1631 616 508 616 508 616 508 616 1631 616 508 617 508 616 508 616 508 616 1630 616 1631 615 1631 616 508 616 1631 616 508 617 508 616 509 615 509 616 508 616 509 615 509 616 508 616 1631 615 1631 615 1631 616 1631 615 1631 615 1631 615 1631 615 1631 616 14433 9088 2191 615 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9014 4332 661 1570 661 471 660 473 658 474 657 476 655 498 633 498 634 502 633 499 633 1599 632 1599 632 1599 632 1599 632 1599 632 1600 631 1603 632 500 632 501 631 501 631 501 631 501 631 501 631 1601 631 504 631 1601 631 1601 631 1601 631 1601 631 1601 630 1601 630 501 631 1601 631 38177 8983 2149 630 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 4C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9042 4306 690 1541 665 468 664 468 664 469 663 470 662 471 660 495 636 499 636 497 634 1597 634 1598 633 1598 633 1599 633 1599 632 1599 633 1603 632 1599 633 499 633 499 633 500 632 499 633 500 632 1600 632 503 633 500 632 1600 632 1600 632 1600 633 1600 632 1600 632 500 632 1600 632 37912 8986 2145 633 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3522 1701 472 426 444 1269 472 426 444 426 443 427 443 427 443 426 444 427 443 426 444 427 442 428 441 429 440 431 438 1304 437 433 437 433 438 433 437 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1305 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 459 411 459 411 459 411 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 459 411 459 411 1330 411 1330 411 460 410 1330 411 1330 411 1331 410 1330 411 74392 3516 1736 436 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 435 435 1305 436 435 435 435 435 1306 435 435 435 435 435 435 435 436 434 436 434 436 434 435 435 436 434 436 434 436 434 1330 411 1331 410 1330 411 1330 411 1330 411 459 411 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 435 436 434 436 1306 435 435 435 435 435 1306 435 435 435 435 435 435 435 435 435 435 435 436 434 436 434 435 435 436 434 435 435 1306 435 1330 411 1307 434 1331 410 1308 433 436 434 436 434 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 435 435 434 436 434 436 434 436 434 436 434 436 1306 435 435 435 435 435 435 435 1306 435 435 435 436 434 1306 435 435 435 436 434 436 434 435 435 436 434 436 434 460 410 460 410 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74392 3515 1736 437 433 437 1304 437 433 437 434 436 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 434 436 434 436 435 435 435 435 434 436 1306 435 434 436 435 435 435 435 1306 435 436 434 435 435 1306 435 435 435 436 434 436 434 436 434 436 434 460 410 437 433 459 411 460 410 460 410 1331 410 1331 410 1331 410 1331 410 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3514 1736 437 434 436 1304 437 433 437 434 436 433 437 434 436 433 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 434 436 435 435 434 436 434 436 435 435 434 436 1305 436 435 435 435 435 435 435 1306 435 435 435 435 435 1306 435 435 435 436 434 435 435 459 411 436 434 435 435 459 411 459 411 459 411 459 411 1330 411 1306 435 1330 411 1330 411 1331 410 460 410 460 410 460 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# ON +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 529 7218 126 6585 219 703 180 5362 427 18618 177 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9069 4362 622 486 621 487 621 491 622 1608 623 1603 622 487 621 487 621 491 622 1604 621 487 622 491 622 1604 621 491 622 1608 622 1609 621 1604 622 486 622 487 621 491 621 1605 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1604 622 491 621 1609 622 1604 621 491 621 1604 622 487 621 487 622 486 622 487 621 488 621 487 621 488 620 491 621 1609 622 1609 620 1609 621 1609 621 1609 621 1609 621 1609 621 1618 621 14330 9047 2137 620 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9047 4362 621 486 622 463 645 490 622 1609 622 1604 622 487 620 487 621 491 622 1604 622 484 625 490 621 1605 649 463 621 1609 620 1611 621 1608 622 1605 621 486 622 491 622 1604 621 487 621 492 620 1604 621 488 621 492 620 1609 622 1604 621 492 622 1609 620 1605 621 491 622 1603 622 488 621 488 620 488 620 488 621 488 620 487 622 485 621 492 596 1635 621 1609 622 1585 643 1611 620 1608 621 1610 619 1611 620 1619 619 14332 9074 2109 647 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9073 4336 648 461 647 484 624 489 623 1607 623 1603 622 486 622 486 622 491 622 1604 621 487 621 491 622 1604 622 491 621 1609 621 1609 621 1609 621 1608 622 1609 621 1604 621 486 622 486 622 491 622 1604 622 486 622 487 621 487 621 491 622 1608 622 1609 621 1604 622 491 621 1604 621 487 621 486 622 487 621 487 621 487 621 487 621 487 621 491 622 1608 622 1608 622 1609 621 1608 622 1608 622 1608 622 1609 621 1617 622 14330 9047 2137 620 +# ON +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4F B0 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 10 EF 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 80 19 00 00 +command: 46 B9 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 80 00 00 00 +command: 51 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 40 40 00 00 +command: 0A F5 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4E B1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0E F1 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 0D F2 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 4F B0 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 00 30 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 08 16 00 00 +command: 87 78 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 08 16 00 00 +command: C8 37 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 01 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 02 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 28 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 01 00 00 00 +command: 29 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 84 F4 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 00 FF 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 1D E2 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 33 00 00 00 +command: 0B F4 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 90 6F 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 99 66 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 55 00 00 +command: 98 67 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 1C E3 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4F B0 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 00 DF 00 00 +command: 4B B4 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 02 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 2E 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 52 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 41 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 51 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 56 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 20 00 00 00 +command: 5A 00 00 00 +# +name: Power +type: parsed +protocol: SIRC15 +address: 54 00 00 00 +command: 15 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 82 7D 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 83 7C 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 14 EB 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 91 00 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 90 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 31 00 00 00 +command: D0 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 89 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 00 00 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 30 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 31 00 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: 86 00 00 00 +command: 32 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 30 00 00 00 +command: 00 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 4E 00 00 +command: 0D 00 00 00 +# +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9032 4479 597 560 572 558 564 566 566 1666 589 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 1669 596 560 562 1671 594 1666 588 1671 594 562 570 560 562 568 564 1669 596 560 562 568 564 566 566 563 569 1664 591 1669 596 1664 590 565 567 1667 598 1661 593 1666 588 1671 594 562 570 560 562 568 564 565 567 563 569 560 562 568 564 565 567 1666 588 1671 594 1665 589 1670 595 1665 590 1669 596 1664 590 1668 597 13983 9029 2222 599 96237 9030 2221 589 96244 9034 2217 594 96244 9033 2218 592 96249 9038 2213 597 96239 9037 2214 596 96238 9028 2223 598 96221 9032 2215 595 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4482 593 563 569 561 571 559 563 1698 566 1694 570 559 563 568 564 566 566 1695 569 560 572 559 563 1671 593 563 569 1692 562 1671 593 1693 571 558 564 567 565 565 567 1693 571 532 590 567 565 1695 569 560 562 1698 566 1694 570 1663 591 539 593 1693 571 1688 566 564 568 1691 563 567 565 565 567 563 569 561 571 559 563 567 565 565 567 563 569 1690 564 1695 569 1691 563 1696 568 1691 563 1697 567 1692 562 1697 567 13988 9030 2223 597 96250 9035 2219 591 96245 9032 2221 589 96240 9038 2215 595 96235 9033 2220 590 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9028 4482 593 563 569 561 571 558 564 1696 568 1690 564 566 566 563 569 561 571 1688 566 563 569 561 571 1688 566 563 569 1690 564 1695 569 1689 565 1668 596 560 562 568 564 1695 569 560 562 568 564 1695 569 560 562 568 564 1695 569 1690 564 566 566 1692 572 1687 567 563 569 1690 564 566 566 564 568 562 570 559 563 567 565 565 567 562 570 560 562 1696 568 1665 589 1670 594 1665 589 1670 594 1664 590 1669 647 1612 590 13987 9031 2220 590 96223 9033 2217 593 96223 9034 2218 592 96225 9032 2219 591 96221 9087 2164 595 +# +name: Mute +type: raw +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_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 14 00 00 00 +# OFF +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 4E B1 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 1D 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 11 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 03 00 00 00 +command: 15 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9075 4307 677 433 675 456 651 461 651 1579 650 1576 649 459 649 460 648 465 648 1578 647 461 622 491 622 1604 647 465 647 1583 622 1608 647 1579 647 461 647 466 622 1604 647 465 647 1579 647 461 645 463 648 465 648 1583 646 1580 646 466 647 1579 622 491 647 1583 622 1608 647 1579 647 461 647 461 622 486 622 486 647 461 647 462 646 462 622 491 646 1584 622 1608 647 1584 621 1608 647 1583 646 1584 647 1584 646 1592 622 14330 9047 2137 621 +# +name: Power +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: E6 00 00 00 +# +name: Vol_up +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 07 00 00 00 +# +name: Vol_dn +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0B 00 00 00 +# +name: Mute +type: parsed +protocol: Samsung32 +address: 07 00 00 00 +command: 0F 00 00 00 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 3523 1701 472 426 444 1269 472 426 444 426 442 429 443 427 443 426 444 426 444 426 443 427 442 429 440 430 439 432 438 1304 437 433 437 432 438 432 438 433 437 433 437 433 437 433 437 433 437 433 437 1304 437 433 437 433 437 433 437 1304 437 433 437 433 437 1304 437 433 437 434 436 433 437 434 436 434 436 434 436 433 437 433 437 434 436 1304 437 1305 436 1305 436 1305 436 1305 436 1305 436 434 436 434 436 1305 436 1305 436 1305 436 434 436 1305 436 1305 436 1306 435 1306 435 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 434 436 434 436 1304 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 436 434 435 435 1307 434 1331 410 1307 434 1307 434 1330 411 1307 434 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 434 436 433 437 433 437 1304 437 434 436 434 436 434 437 434 436 434 436 434 436 434 436 434 436 434 436 1305 436 434 436 434 436 434 436 1305 436 435 435 434 436 1305 436 434 436 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1306 435 1307 434 1307 434 1307 434 1331 410 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 74393 3515 1736 437 433 437 1304 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 433 437 434 436 433 437 1304 437 433 437 434 436 434 436 434 436 434 436 434 436 434 436 434 436 434 437 1305 436 434 436 434 436 434 436 1305 436 434 436 434 436 1306 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 435 1307 434 1330 411 1330 411 1330 411 1330 411 1330 411 460 410 460 410 1331 410 1331 410 1331 410 460 410 1331 410 1331 410 1331 410 1331 410 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9093 4441 620 507 618 530 594 531 593 1652 595 1653 620 505 620 505 619 506 617 1630 616 508 616 508 616 1632 615 509 615 1631 616 1632 615 1632 615 510 615 509 615 1632 615 509 615 1632 615 510 615 510 614 509 615 1632 614 1633 614 509 615 1633 614 509 615 1632 615 1632 614 1633 614 510 614 510 615 510 615 510 614 510 614 510 615 510 615 510 614 1632 615 1632 614 1632 615 1632 615 1632 615 1632 615 1632 615 1633 614 14439 9088 2192 614 96349 9112 2190 616 +# OFF +name: Power +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 243 27700 170 27632 246 27694 282 27595 307 27497 241 27696 177 27710 164 27644 245 27629 246 27712 174 27638 211 27736 131 27741 306 27504 214 27727 135 27749 132 27761 126 27744 131 27753 127 27764 121 27767 132 27773 307 27577 131 27706 213 27761 129 27759 128 27770 125 27694 213 27751 307 27578 131 27737 131 27745 304 27575 335 27540 124 27752 132 27749 132 27747 134 27757 134 27758 127 27762 131 27748 131 27750 122 27749 130 27748 125 27772 131 27774 136 27762 135 27686 215 27742 131 27749 132 27756 133 27764 126 24073 9255 4460 672 488 618 541 619 541 619 1675 619 1676 618 542 618 542 618 542 618 1676 618 542 618 543 617 1678 616 568 592 1702 592 1702 592 1703 617 543 617 543 617 1677 617 543 617 1678 615 544 616 544 616 544 616 1678 616 1679 615 544 616 1679 615 545 615 1679 615 1679 615 1679 615 40240 9173 2273 591 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 219 27658 217 27663 216 27658 216 27634 216 27642 215 27646 217 27662 217 27637 216 27649 216 27649 218 27656 217 27658 215 27640 214 27636 217 27649 216 27644 218 27635 217 27630 215 27645 216 27631 215 27632 216 27650 216 27628 217 27630 214 27627 217 27623 215 27632 215 27641 216 27634 214 27633 215 27648 215 27648 217 27651 215 27635 216 27629 216 27630 216 2021 9254 4461 618 542 618 542 618 542 618 1675 619 1676 618 541 619 541 619 542 618 1677 617 543 617 543 617 1678 616 568 592 1702 592 1702 618 1676 618 542 618 542 618 543 617 1677 617 543 617 544 616 1678 616 544 616 1678 616 1678 616 1678 616 544 616 1678 616 1678 616 544 616 1678 616 40239 9200 2247 617 99930 110 27739 119 27738 123 27750 126 27738 175 27617 214 27716 203 27604 213 27639 217 27631 214 27722 136 27753 119 27736 175 27618 246 27683 177 27619 245 27685 171 55486 244 27693 158 27635 241 27695 170 27693 129 27717 340 27530 113 27757 106 27751 124 27728 172 27707 126 27666 215 27708 123 27733 123 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 18 E9 00 00 +command: 49 B6 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 14 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 48 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 40 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 02 00 00 00 +command: 18 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0C F3 00 00 +# +name: Mute +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 0D F2 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1E E1 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: B8 57 00 00 +command: 1F E0 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 81 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8F 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 32 00 00 00 +command: 8C 00 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9066 4428 608 507 609 1622 609 507 609 507 609 1623 608 1623 609 507 609 506 610 1623 609 507 609 1622 610 1623 608 507 609 506 610 1622 609 1623 609 506 610 1622 610 506 610 1623 637 478 690 425 638 478 637 1594 637 1594 664 451 636 1594 610 506 610 1621 611 1621 610 1621 610 505 611 40183 9065 2156 637 95953 9037 2185 608 +# +name: Power +type: parsed +protocol: NEC +address: 00 00 00 00 +command: A8 00 00 00 +# +name: Mute +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 88 00 00 00 +# +name: Vol_dn +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 9C 00 00 00 +# +name: Vol_up +type: parsed +protocol: NEC +address: 00 00 00 00 +command: 8C 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9064 4354 666 1559 666 1562 662 1586 638 475 636 477 635 477 635 478 635 1590 635 1591 634 478 635 1591 634 478 634 478 635 478 634 1591 635 478 634 1591 634 478 635 478 634 478 635 1591 634 478 634 1591 635 478 634 478 634 1591 634 1591 635 1591 634 478 635 1591 634 478 634 1591 635 40957 9035 2144 634 95483 9047 2155 632 95484 9048 2153 633 +# +name: Vol_dn +type: parsed +protocol: NECext +address: 87 45 00 00 +command: 50 AF 00 00 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.330000 +data: 9034 4385 638 1587 664 1562 663 1587 637 476 635 478 634 478 635 478 635 1591 634 1591 634 478 635 1591 635 478 634 478 635 478 635 1591 635 478 634 478 634 1591 634 478 635 479 634 1591 635 478 634 1591 635 478 634 1592 634 478 634 1591 635 1591 635 478 634 1592 634 478 634 1591 634 40958 9033 2144 635 +# +name: Power +type: parsed +protocol: NECext +address: FF FF 00 00 +command: E8 17 00 00 +# +name: Vol_up +type: parsed +protocol: NECext +address: FF FF 00 00 +command: BD 42 00 00 +# +name: Vol_dn +type: parsed +protocol: NECext +address: FF FF 00 00 +command: F2 0D 00 00 +# +name: Power +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 05 00 00 00 +# +name: Vol_up +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 70 01 00 00 +# +name: Vol_dn +type: parsed +protocol: Kaseikyo +address: 41 54 32 00 +command: 71 01 00 00 +# +name: Power +type: parsed +protocol: NEC +address: 31 00 00 00 +command: 81 00 00 00 +# +name: Power +type: parsed +protocol: NECext +address: 83 F4 00 00 +command: 17 E8 00 00 +# +name: Vol_up +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9010 4413 532 1617 532 1617 533 489 533 489 533 489 558 464 558 465 557 1593 557 465 557 466 556 1594 555 467 555 1595 529 1621 554 1595 581 1569 581 441 581 1569 581 441 581 441 581 441 581 441 581 441 581 1569 581 1569 581 441 581 1569 580 1569 580 1570 580 1595 554 1595 555 468 554 42156 8983 2135 556 +# +name: Vol_dn +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9032 4390 556 1592 559 1591 559 463 559 463 558 464 557 465 556 465 557 1593 583 440 581 441 580 1569 581 441 581 1569 580 1569 581 1569 581 1570 580 1596 554 1596 554 468 554 468 554 468 554 442 580 442 580 1596 554 469 553 469 553 1596 554 1596 553 1597 550 1598 553 1598 552 469 551 42155 9008 2107 531 95218 9006 2108 582 +# +name: Mute +type: raw +frequency: 38000 +duty_cycle: 0.33 +data: 9011 4388 557 1617 532 1617 532 489 533 489 558 464 558 440 582 440 582 1593 556 466 556 466 556 1594 556 467 555 1595 555 1595 529 1620 554 1596 554 467 554 468 555 1595 579 443 581 1569 581 441 581 441 580 442 581 1569 581 1569 581 441 581 1569 580 441 581 1569 581 1569 581 1570 579 42152 8957 2159 556 \ No newline at end of file diff --git a/assets/resources/subghz/assets/alutech_at_4n b/assets/resources/subghz/assets/alutech_at_4n new file mode 100644 index 000000000..5d7beacec --- /dev/null +++ b/assets/resources/subghz/assets/alutech_at_4n @@ -0,0 +1,6 @@ +Filetype: Flipper SubGhz Keystore RAW File +Version: 0 +Encryption: 1 +IV: 88 64 A6 A6 44 47 67 8A D6 32 36 F6 B9 06 57 31 +Encrypt_data: RAW +E811BD4F0955D217AE6677906E799D45D8DAAFD1F7923E1660B5E24574631B60 \ No newline at end of file diff --git a/assets/unit_tests/subghz/alutech_at_4n_raw.sub b/assets/unit_tests/subghz/alutech_at_4n_raw.sub new file mode 100644 index 000000000..ae5db9715 --- /dev/null +++ b/assets/unit_tests/subghz/alutech_at_4n_raw.sub @@ -0,0 +1,10 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -854 811 -454 811 -444 409 -838 811 -454 823 -432 385 -842 811 -454 389 -854 821 -418 837 -444 401 -850 417 -854 395 -830 417 -846 819 -432 811 -448 789 -444 839 -454 401 -856 381 -850 825 -410 841 -418 417 -834 411 -840 827 -442 417 -844 799 -472 809 -420 411 -842 419 -848 397 -822 413 -850 799 -486 381 -848 415 -854 423 -16394 449 -358 437 -386 411 -384 449 -382 417 -384 419 -386 417 -386 419 -388 419 -388 419 -388 419 -390 437 -4036 421 -810 425 -820 393 -866 813 -422 415 -856 397 -858 811 -456 427 -820 815 -416 419 -850 401 -854 805 -420 835 -444 409 -842 809 -454 433 -820 421 -838 813 -420 417 -822 435 -820 419 -834 431 -852 411 -866 805 -420 815 -444 805 -454 403 -824 809 -448 819 -448 413 -844 811 -446 811 -456 409 -816 809 -456 389 -866 387 -842 809 -454 827 -432 413 -850 829 -428 809 -452 381 -852 799 -452 413 -852 807 -450 801 -444 409 -872 411 -840 413 -812 413 -832 807 -450 815 -442 801 -454 809 -454 429 -820 419 -838 811 -456 785 -428 409 -842 439 -824 813 -448 415 -858 819 -418 831 -426 449 -808 427 -820 393 -866 421 -808 825 -436 413 -852 403 -884 421 -16394 407 -478 257 -574 229 -576 229 -564 231 -592 267 -490 305 -520 307 -486 309 -522 307 -458 341 -486 337 -4096 343 -882 389 -880 341 -874 807 -476 351 -882 397 -860 807 -450 431 -824 811 -450 399 -824 417 -844 817 -432 807 -448 411 -872 801 -460 417 -810 425 -836 809 -420 411 -838 409 -852 417 -844 425 -852 385 -872 801 -426 841 -420 811 -422 409 -844 809 -454 823 -432 415 -842 835 -450 805 -454 403 -822 809 -450 399 -826 417 -844 821 -434 807 -448 411 -876 801 -440 807 -450 383 -850 833 -416 415 -852 807 -456 811 -444 411 -838 419 -848 401 -852 377 -850 819 -454 795 -436 809 -448 821 -448 411 -846 417 -842 817 -432 811 -412 411 -864 417 -844 791 -438 415 -876 793 -458 809 -450 383 -832 413 -840 407 -866 387 -844 821 -434 413 -874 377 -868 419 -16408 411 -392 421 -390 421 -388 421 -376 427 -394 433 -388 409 -386 411 -384 449 -382 419 -384 419 -384 419 -4018 343 -89684 97 -430 65 -166 163 -66 231 -100 161 -392 161 -64 229 -1056 97 -198 97 -198 259 -166 691 -66 395 -98 131 -100 99 -66 199 -198 1657 -406 365 -462 361 -436 357 -438 387 -444 353 -444 385 -414 387 -448 355 -448 355 -444 395 -394 399 -4050 819 -434 811 -414 819 -450 409 -852 809 -450 805 -448 805 -446 831 -428 409 -846 389 -854 395 -862 811 +RAW_Data: -452 783 -462 811 -446 411 -874 383 -852 403 -852 777 -450 411 -838 805 -452 397 -858 805 -484 387 -872 803 -442 805 -448 383 -846 797 -484 777 -474 381 -846 831 -430 807 -482 387 -852 817 -418 805 -452 823 -430 385 -878 377 -876 411 -846 391 -884 811 -444 805 -420 415 -846 399 -852 807 -452 805 -444 803 -450 803 -454 807 -452 397 -850 395 -862 385 -844 427 -840 809 -456 379 -876 407 -880 383 -846 819 -432 387 -872 375 -854 413 -846 387 -886 779 -476 803 -450 801 -444 415 -846 793 -438 415 -846 417 -822 407 -852 417 -852 817 -444 409 -16426 443 -358 431 -388 409 -386 447 -350 449 -382 419 -386 419 -386 417 -386 419 -388 453 -354 453 -356 445 -4018 813 -416 839 -420 817 -418 451 -816 835 -444 809 -450 811 -440 803 -444 407 -830 419 -844 419 -836 805 -420 835 -444 803 -446 409 -868 421 -814 431 -822 807 -452 397 -828 819 -452 415 -856 787 -454 419 -848 837 -410 839 -416 395 -866 787 -450 811 -454 407 -850 805 -454 805 -470 381 -850 833 -418 805 -438 809 -448 397 -860 421 -810 431 -856 381 -888 791 -424 841 -422 415 -820 437 -818 813 -450 813 -454 803 -446 817 -448 829 -410 451 -816 403 -818 413 -850 409 -846 811 -448 415 -834 413 -884 399 -822 815 -452 381 -852 407 -846 415 -846 385 -866 807 -456 809 -446 835 -412 407 -834 815 -450 415 -822 405 -848 419 -844 427 -852 809 -442 407 -16420 425 -422 335 -486 309 -486 309 -522 307 -492 337 -454 351 -466 329 -498 327 -468 323 -472 355 -442 355 -4120 757 -456 773 -478 781 -458 381 -874 771 -482 801 -470 777 -482 805 -450 399 -826 415 -846 387 -866 805 -420 813 -446 831 -458 417 -846 401 -852 381 -840 835 -420 415 -846 795 -440 413 -842 835 -450 407 -860 811 -418 815 -424 413 -842 807 -454 823 -428 411 -842 801 -454 807 -488 401 -822 805 -448 803 -446 803 -426 447 -844 397 -856 381 -870 411 -870 777 -452 829 -432 385 -840 419 -848 797 -438 809 -450 815 -448 833 -440 803 -452 411 -820 415 -848 409 -844 411 -846 779 -462 409 -848 409 -864 421 -844 793 -438 385 -846 419 -850 399 -838 415 -872 777 -478 803 -448 799 -442 417 -848 799 -438 415 -842 409 -826 417 -844 427 -854 807 -452 409 -16402 461 -352 421 -386 421 -386 419 -388 453 -354 453 -354 447 -378 409 -386 439 -376 421 -410 385 -414 415 -4024 831 -418 809 -438 807 -444 409 -838 809 -456 821 -432 841 -414 255 -87638 131 -66 97 -296 97 -264 131 -196 65 -132 231 -632 197 -664 131 +RAW_Data: -500 395 -132 461 -132 689 -98 2685 -100 997 -1508 99 -2186 231 -166 231 -134 133 -932 65 -268 99 -132 65 -200 97 -68 163 -234 65 -68 99 -930 331 -98 763 -100 2025 -418 353 -446 385 -414 385 -416 387 -448 355 -442 383 -412 397 -424 405 -388 409 -418 379 -418 415 -4014 443 -814 413 -822 817 -454 801 -436 409 -842 409 -866 415 -838 441 -836 811 -432 387 -842 419 -846 793 -440 807 -448 837 -446 803 -448 835 -420 807 -448 383 -868 379 -850 409 -866 387 -844 825 -468 381 -884 793 -426 415 -842 427 -818 817 -440 407 -830 419 -844 429 -852 387 -872 409 -826 811 -450 813 -418 837 -412 409 -864 417 -844 397 -852 809 -454 805 -448 409 -840 809 -420 813 -458 409 -844 407 -860 385 -878 793 -470 809 -420 817 -416 417 -850 403 -852 381 -852 827 -428 447 -844 401 -854 813 -424 421 -840 419 -812 823 -438 415 -846 409 -844 415 -846 389 -868 809 -458 803 -416 409 -866 813 -418 417 -854 397 -862 419 -842 401 -854 415 -16404 435 -376 409 -410 407 -384 439 -384 409 -410 417 -368 421 -410 407 -378 447 -376 415 -378 447 -380 407 -4022 421 -844 423 -822 821 -418 807 -454 429 -820 421 -836 439 -854 421 -810 821 -436 385 -840 441 -822 813 -448 811 -452 803 -444 835 -444 801 -446 801 -426 447 -808 423 -834 413 -852 407 -840 819 -452 389 -856 813 -444 409 -848 415 -812 809 -458 409 -848 411 -842 415 -844 421 -834 415 -834 835 -418 819 -418 807 -456 393 -856 393 -866 421 -846 799 -474 809 -420 421 -836 811 -420 813 -458 407 -850 413 -842 415 -846 819 -428 835 -416 835 -412 407 -832 421 -842 423 -822 813 -446 407 -864 419 -846 799 -440 413 -850 419 -816 797 -442 413 -850 409 -844 417 -846 423 -834 841 -428 805 -414 435 -822 813 -450 413 -822 437 -818 421 -844 429 -854 411 -16406 427 -418 309 -522 307 -488 303 -520 289 -530 295 -500 323 -470 325 -504 321 -476 321 -476 355 -444 357 -4080 355 -906 339 -882 771 -476 777 -486 381 -874 383 -884 375 -884 387 -852 819 -418 417 -846 399 -854 809 -418 815 -446 837 -420 839 -454 801 -436 807 -452 399 -826 417 -844 391 -852 423 -838 809 -452 431 -852 811 -414 409 -836 417 -844 821 -432 385 -876 385 -850 409 -848 415 -854 421 -840 817 -420 815 -424 817 -448 409 -848 413 -844 389 -854 815 -446 829 -426 413 -842 819 -434 809 -446 409 -838 419 -846 401 -852 811 -456 811 -444 803 -418 417 -848 403 -850 381 -864 805 -450 395 -866 419 -848 801 -474 381 -848 411 +RAW_Data: -842 807 -446 381 -872 377 -866 421 -846 401 -854 813 -458 779 -446 407 -832 811 -450 415 -856 399 -856 385 -876 399 -854 411 -16398 435 -392 395 -400 421 -412 385 -412 417 -384 415 -386 415 -418 385 -420 385 -420 417 -390 417 -388 419 -4020 421 -838 421 -812 819 -434 809 -448 397 -864 421 -844 401 -850 413 -858 789 -426 413 -844 419 -836 807 -424 843 -410 829 -442 835 -446 801 -454 809 -420 417 -832 411 -848 249 -88020 133 -896 231 -466 67 -1062 131 -728 163 -98 621 -98 1051 -100 680933 -452 269 -522 273 -554 273 -558 239 -558 271 -490 337 -488 321 -498 295 -500 325 -470 323 -474 353 -4082 757 -492 375 -880 357 -872 777 -486 773 -480 807 -450 805 -444 805 -476 407 -812 413 -834 411 -848 407 -828 813 -450 811 -458 803 -448 835 -446 791 -424 447 -808 427 -818 423 -840 419 -848 401 -854 811 -458 809 -446 801 -416 439 -826 415 -848 813 -430 809 -450 395 -866 419 -846 403 -850 413 -820 407 -848 415 -846 781 -460 805 -446 803 -474 803 -448 835 -420 805 -454 389 -836 409 -842 407 -866 419 -842 399 -854 809 -456 809 -446 409 -840 385 -844 819 -434 809 -450 395 -860 811 -452 393 -886 779 -446 409 -830 419 -842 423 -818 423 -838 419 -844 799 -472 809 -454 385 -844 807 -454 391 -854 395 -860 385 -844 429 -852 809 -454 385 -874 409 -16402 427 -368 455 -358 433 -380 443 -378 415 -378 447 -380 411 -384 409 -406 421 -408 387 -412 415 -386 415 -4026 831 -398 441 -810 417 -832 837 -418 833 -444 803 -446 833 -448 801 -424 449 -810 427 -820 423 -838 419 -812 825 -438 841 -416 845 -446 825 -418 809 -422 419 -822 433 -822 419 -844 425 -820 421 -840 841 -458 797 -436 809 -414 435 -822 419 -844 819 -432 809 -448 395 -864 421 -846 407 -850 411 -808 433 -824 419 -844 819 -432 809 -446 823 -416 837 -454 807 -440 809 -414 435 -828 417 -844 425 -828 415 -848 419 -818 839 -446 807 -422 411 -844 419 -846 795 -438 807 -450 395 -866 811 -454 391 -854 845 -412 407 -832 421 -842 419 -832 411 -824 435 -820 815 -450 811 -460 409 -850 799 -454 407 -824 413 -848 411 -842 415 -844 815 -432 415 -848 405 -16400 441 -432 327 -492 301 -516 307 -484 309 -520 307 -492 339 -454 337 -490 331 -464 327 -500 325 -472 325 -4110 763 -480 373 -852 385 -878 759 -482 775 -474 813 -458 781 -482 789 -454 415 -846 397 -820 411 -840 405 -852 809 -450 811 -458 809 -450 817 -448 803 -426 411 -844 391 -854 393 -866 419 -848 399 -854 811 +RAW_Data: -454 811 -444 803 -418 417 -846 403 -850 809 -452 805 -444 411 -840 419 -846 407 -850 415 -836 385 -842 419 -850 797 -438 807 -452 817 -446 801 -486 813 -444 775 -450 409 -838 419 -810 431 -854 379 -848 405 -884 809 -450 817 -430 385 -874 375 -856 811 -446 809 -422 421 -836 835 -452 419 -848 783 -460 409 -814 407 -856 415 -846 383 -870 381 -848 819 -450 811 -472 383 -850 803 -454 415 -838 399 -854 379 -850 407 -848 811 -448 415 -872 387 -16400 451 -374 445 -374 415 -378 415 -412 411 -384 405 -422 409 -410 387 -410 417 -382 417 -384 415 -420 383 -4030 827 -428 411 -842 425 -820 817 -418 833 -426 845 -452 815 -428 837 -416 409 -842 421 -810 431 -820 421 -89106 265 -662 99 -532 131 -598 97 -668 65 -300 761 -198 231 -132 265 -100 233 -100 197 diff --git a/assets/unit_tests/subghz/dooya.sub b/assets/unit_tests/subghz/dooya.sub new file mode 100644 index 000000000..0767a1a73 --- /dev/null +++ b/assets/unit_tests/subghz/dooya.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: Dooya +Bit: 40 +Key: 00 00 00 E1 DC 03 05 11 diff --git a/assets/unit_tests/subghz/dooya_raw.sub b/assets/unit_tests/subghz/dooya_raw.sub new file mode 100644 index 000000000..6c3ca1627 --- /dev/null +++ b/assets/unit_tests/subghz/dooya_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 4046 -17306 65 -298 97 -100 133 -268 265 -330 133 -132 1723 -16806 165 -132 99 -920 65 -622 789 -130 99 -66 361 -98 295 -166 73573 -17510 97 -492 129 -728 529 -100 1063 -164 295 -66 1119 -14962 627 -166 363 -264 427 -132 593 -100 633 -132 39555 -16938 99 -2024 65 -100 97 -164 99 -66 399 -100 123891 -16736 163 -200 97 -200 165 -264 65 -828 427 -132 871 -5132 591 -490 595 -486 605 -454 275 -822 241 -824 273 -784 321 -782 649 -444 653 -408 657 -428 321 -744 693 -388 699 -388 707 -392 313 -752 345 -750 317 -744 351 -730 355 -738 323 -774 327 -748 329 -750 695 -386 701 -354 381 -722 351 -720 385 -718 351 -718 345 -738 705 -382 329 -736 713 -360 387 -718 369 -718 367 -706 735 -352 375 -726 351 -722 351 -720 719 -7808 4845 -1474 743 -332 741 -370 705 -370 349 -718 383 -716 345 -712 381 -704 747 -326 747 -350 737 -352 351 -718 719 -360 741 -366 687 -362 375 -704 381 -724 351 -740 353 -712 357 -718 359 -744 363 -688 365 -722 727 -354 727 -354 379 -724 351 -722 353 -720 387 -718 353 -718 703 -374 351 -716 735 -354 365 -708 353 -734 351 -746 717 -356 359 -720 371 -704 371 -720 731 -7786 4847 -1482 711 -386 711 -358 743 -330 373 -708 359 -748 349 -740 351 -716 719 -356 727 -354 739 -354 351 -718 719 -362 743 -364 721 -330 373 -706 381 -722 351 -740 353 -712 359 -720 361 -722 361 -720 361 -720 725 -354 731 -354 381 -720 353 -722 385 -720 351 -720 349 -716 735 -354 361 -748 711 -364 347 -740 365 -722 365 -720 695 -384 371 -704 381 -702 377 -710 709 -7804 4853 -1468 743 -336 735 -358 719 -352 379 -724 353 -722 353 -720 387 -686 721 -360 721 -362 743 -332 387 -718 721 -366 701 -382 701 -350 377 -720 351 -740 353 -714 357 -710 397 -710 365 -702 385 -688 377 -724 731 -352 703 -354 379 -736 343 -740 357 -720 349 -706 385 -718 719 -354 365 -724 735 -352 377 -724 355 -720 353 -720 721 -358 387 -686 387 -718 353 -718 733 -7796 4821 -1492 739 -350 719 -334 737 -350 365 -722 373 -722 367 -708 371 -702 747 -352 711 -358 743 -364 343 -706 749 -352 717 -350 717 -384 327 -736 351 -746 355 -716 357 -720 359 -710 365 -742 365 -708 367 -704 711 -354 743 -356 387 -684 373 -706 381 -722 351 -740 353 -714 721 -356 361 -720 733 -352 375 -694 385 -724 353 -722 719 -356 385 -686 385 -718 351 -716 731 -7792 4843 -1480 717 -354 719 -386 717 -354 359 -720 351 -708 387 -712 355 -718 721 -356 727 -354 739 -356 351 -718 741 -364 +RAW_Data: 705 -370 703 -372 351 -718 383 -720 347 -720 347 -714 381 -704 353 -744 357 -718 355 -720 723 -356 725 -354 379 -722 351 -722 353 -722 385 -718 351 -718 721 -372 351 -716 719 -372 351 -718 383 -716 345 -714 743 -346 361 -740 353 -712 357 -710 725 -7818 4837 -1498 713 -356 709 -360 741 -332 375 -706 359 -750 351 -706 353 -748 719 -356 723 -352 739 -354 351 -718 709 -364 719 -362 721 -364 385 -718 353 -718 383 -682 377 -712 349 -734 353 -742 355 -712 359 -722 723 -354 729 -352 381 -722 353 -722 351 -720 387 -718 353 -716 701 -388 345 -722 737 -354 357 -722 351 -708 387 -712 717 -350 731 -354 741 -356 743 -330 375 -8180 4829 -1468 739 -364 707 -354 729 -352 379 -722 353 -720 387 -686 387 -718 707 -368 721 -366 707 -368 351 -718 735 -354 719 -354 719 -388 329 -746 349 -738 351 -712 359 -718 361 -742 365 -708 371 -706 373 -720 733 -320 733 -354 383 -720 353 -720 387 -718 351 -716 385 -714 703 -388 327 -746 705 -348 387 -702 385 -690 385 -724 713 -358 709 -362 743 -364 709 -370 351 -8162 4837 -1482 715 -388 715 -352 715 -384 325 -730 353 -744 353 -712 359 -720 723 -354 733 -354 745 -356 351 -720 719 -362 741 -330 737 -382 349 -722 345 -724 361 -744 349 -704 383 -716 357 -718 357 -720 361 -720 723 -354 733 -354 383 -720 387 -686 387 -718 353 -718 349 -716 731 -384 347 -724 721 -352 365 -706 353 -732 353 -746 717 -356 723 -352 739 -354 711 -360 385 -8146 4841 -1470 737 -344 739 -326 751 -352 377 -690 387 -724 353 -724 353 -722 711 -360 743 -364 721 -330 387 -716 703 -386 721 -356 721 -354 363 -706 349 -734 351 -746 355 -718 355 -712 363 -744 365 -708 369 -722 695 -352 731 -354 381 -722 353 -722 351 -734 351 -716 383 -720 723 -354 333 -736 739 -348 361 -708 351 -748 355 -712 725 -354 727 -352 741 -352 713 -358 385 -8134 4855 -1474 719 -358 709 -362 721 -364 387 -716 351 -718 385 -712 347 -712 739 -334 739 -354 729 -352 379 -722 717 -354 711 -360 743 -332 387 -718 351 -716 377 -708 349 -730 353 -742 355 -710 359 -720 359 -720 723 -354 729 -352 381 -720 353 -722 351 -722 387 -684 387 -716 703 -384 349 -722 737 -354 329 -750 349 -738 353 -712 719 -356 725 -354 741 -354 717 -358 385 -8126 4861 -1470 735 -344 731 -346 729 -348 383 -718 347 -712 353 -734 353 -746 715 -356 725 -350 741 -352 351 -718 741 -366 721 -366 705 -370 353 -718 385 -682 377 -710 349 -734 353 -744 355 -710 359 -710 397 -688 +RAW_Data: 727 -354 729 -352 379 -724 353 -722 353 -718 387 -716 353 -716 735 -348 383 -682 727 -386 347 -722 347 -712 381 -706 747 -326 747 -350 737 -352 711 -358 diff --git a/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub new file mode 100644 index 000000000..49b190002 --- /dev/null +++ b/assets/unit_tests/subghz/kinggates_stylo4k_raw.sub @@ -0,0 +1,11 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 377 -386 1117 -410 1121 -352 1141 -384 1151 -378 1119 -350 1139 -386 1115 -1134 389 -1114 395 -1122 363 -1136 389 -358 1167 -356 1145 -1120 389 -1110 391 -356 1139 -1126 389 -1114 391 -1122 363 -1146 389 -1110 395 -1122 363 -1138 389 -1110 393 -1122 363 -1140 389 -1112 393 -1120 389 -1118 389 -1112 397 -1124 363 -1142 389 -1112 359 -1154 367 -1134 389 -1144 365 -1138 355 -394 1119 -380 1107 -1152 353 -398 1113 -384 1139 -1118 385 -376 1141 -386 1129 -350 1143 -388 1109 -1132 389 -1112 393 -390 1107 -1128 389 -1112 397 -388 1111 -1132 389 -358 1127 -1118 417 -1116 383 -1120 353 -1158 389 -1108 375 -384 1121 -408 1123 -350 1139 -386 1111 -1130 389 -1114 395 -1122 395 -1114 389 -1116 395 -1122 363 -1138 387 -9444 373 -374 379 -374 381 -346 403 -346 389 -376 389 -390 353 -376 359 -382 383 -360 419 -360 359 -386 359 -2264 777 -356 1127 -390 1143 -362 1131 -1138 365 -1122 359 -386 1153 -1106 377 -1152 385 -372 1113 -1140 385 -1118 381 -1114 383 -1150 383 -1120 355 -1122 389 -358 1165 -386 1113 -1128 389 -360 1125 -384 1131 -368 1157 -350 1139 -386 1115 -406 1099 -384 1141 -1122 383 -1110 373 -1130 385 -1128 393 -380 1131 -380 1129 -1112 383 -1132 391 -356 1143 -1124 383 -1130 367 -1136 385 -1136 387 -1112 371 -1120 389 -1118 383 -1130 371 -1130 383 -1110 383 -1120 413 -1118 383 -1144 347 -1144 389 -1110 393 -1122 363 -1140 389 -1112 359 -1154 363 -1146 389 -1110 393 -374 1115 -384 1115 -1144 385 -368 1141 -388 1111 -1110 421 -360 1125 -388 1109 -392 1137 -358 1125 -1144 365 -1138 389 -358 1123 -1118 401 -1138 389 -360 1121 -1120 417 -358 1109 -1154 355 -1120 375 -1138 385 -1130 391 -1136 355 -398 1115 -380 1141 -384 1121 -382 1119 -1104 413 -1118 355 -1156 387 -1112 377 -1122 389 -1118 387 -1112 397 -9422 417 -352 363 -416 355 -388 345 -382 377 -380 375 -380 375 -380 375 -380 385 -356 379 -366 385 -374 387 -2246 745 -380 1141 -386 1113 -370 1125 -1140 373 -1152 355 -394 1117 -1140 381 -1120 385 -374 1145 -1112 385 -1122 381 -1116 383 -1120 375 -1120 389 -1120 373 -380 1171 -358 1121 -1142 377 -356 1127 -384 1137 -378 1155 -390 1105 -366 1125 -386 1135 -386 1111 -1132 389 -1112 393 -1120 365 -1138 387 -360 1163 -356 1143 -1126 387 -1114 357 -386 1141 -1126 383 -1130 365 -1132 381 -1140 377 -1116 383 -1130 371 -1128 381 -1140 347 -1148 385 -1128 369 -1128 381 -1142 377 -1114 389 -1112 395 -1124 361 -1142 389 -1114 393 -1122 365 -1138 389 -1114 397 -1108 389 -392 1119 -350 1139 -1152 355 -396 1115 -382 1109 -1156 385 -374 1111 -384 1139 -368 1147 -388 1109 -1112 389 -1120 383 -388 1107 -1150 389 -1112 +RAW_Data: 393 -390 1109 -1128 389 -360 1125 -1120 381 -1152 383 -1118 353 -1158 387 -1112 375 -386 1117 -408 1121 -350 1143 -388 1109 -1132 389 -1114 391 -1122 395 -1120 389 -1112 393 -1122 365 -1136 389 -9442 373 -376 389 -354 377 -366 387 -384 357 -378 361 -418 347 -394 385 -358 363 -382 361 -414 357 -392 333 -2290 751 -384 1113 -406 1121 -350 1137 -1136 353 -1160 385 -356 1135 -1120 361 -1146 385 -388 1137 -1108 361 -1150 387 -1112 395 -1136 349 -1154 353 -1142 371 -384 1145 -378 1117 -1138 381 -382 1087 -410 1121 -382 1143 -380 1121 -380 1115 -384 1107 -418 1115 -1106 385 -1148 365 -1118 359 -1146 387 -388 1135 -388 1113 -1126 383 -1130 367 -376 1113 -1142 383 -1114 375 -1154 355 -1160 385 -1110 371 -1152 357 -1118 385 -1146 365 -1122 361 -1146 387 -1114 395 -1134 355 -1160 351 -1146 369 -1154 355 -1120 387 -1114 397 -1136 357 -1118 407 -1144 351 -1134 359 -420 1111 -366 1131 -1142 379 -384 1089 -410 1119 -1142 379 -366 1141 -386 1109 -388 1131 -350 1141 -1118 391 -1114 375 -378 1153 -1116 385 -1136 383 -358 1139 -1120 359 -420 1099 -1142 383 -1118 383 -1138 347 -1144 385 -1144 369 -386 1113 -404 1089 -386 1141 -382 1099 -1136 381 -1128 375 -1130 383 -1140 359 -1146 387 -1114 395 -1138 357 -9430 383 -378 359 -418 347 -392 387 -360 363 -384 361 -414 357 -386 347 -384 375 -382 385 -358 383 -364 387 -2252 773 -354 1131 -384 1137 -386 1111 -1132 387 -1112 397 -388 1113 -1124 389 -1116 387 -388 1115 -1132 389 -1114 393 -1120 365 -1140 389 -1114 357 -1154 365 -378 1151 -358 1127 -1156 367 -376 1135 -358 1125 -388 1141 -368 1125 -386 1133 -388 1109 -370 1155 -1106 375 -1122 389 -1118 389 -1114 395 -386 1117 -410 1123 -1106 377 -1130 383 -388 1107 -1152 353 -1148 353 -1150 367 -1142 389 -1110 397 -1120 365 -1138 389 -1110 391 -1122 363 -1142 387 -1116 389 -1120 391 -1116 389 -1116 395 -1122 365 -1140 389 -1114 357 -1154 363 -1138 389 -1142 365 -1138 355 -396 1115 -382 1141 -1118 353 -400 1111 -384 1139 -1120 381 -386 1139 -366 1119 -392 1121 -388 1107 -1152 389 -1114 355 -388 1139 -1126 387 -1114 397 -376 1111 -1144 375 -380 1129 -1138 375 -1098 385 -1140 377 -1118 387 -1144 371 -386 1115 -404 1121 -348 1137 -386 1113 -1134 389 -1112 395 -1124 395 -1118 389 -1110 395 -1122 363 -1138 389 -9418 391 -360 407 -384 361 -388 355 -390 367 -376 373 -380 387 -356 377 -366 385 -388 355 -378 359 -384 377 -2266 777 -346 1149 -388 1107 -390 1129 -1104 415 -1118 353 -398 1113 -1138 383 -1122 381 -388 1141 -1118 389 -1112 357 -1154 365 -1138 389 -1114 357 -1154 363 -378 1155 -358 1123 -1156 381 -360 1107 -384 +RAW_Data: 1153 -378 1119 -382 1143 -382 1121 -382 1117 -382 1113 -1120 389 -1120 387 -1112 399 -1120 393 -392 1119 -350 1141 -1154 357 -1116 389 -360 1163 -1120 365 -1138 387 -1114 389 -1122 389 -1116 387 -1114 397 -1104 379 -1156 353 -1148 367 -1118 377 -1122 423 -1110 373 -1122 389 -1118 383 -1130 373 -1128 383 -1140 345 -1146 383 -1130 399 -1130 353 -1142 377 -358 1127 -384 1143 -1118 385 -372 1111 -386 1137 -1120 381 -388 1141 -364 1127 -384 1133 -374 1111 -1148 383 -1114 373 -384 1115 -1136 387 -1144 371 -386 1115 -1132 387 -360 1123 -1150 345 -1148 383 -1128 371 -1132 381 -1140 379 -390 1123 -350 1139 -388 1113 -406 1089 -1142 373 -1120 389 -1118 423 -1110 371 -1120 379 -1122 407 -1104 417 -9440 389 -356 379 -366 385 -388 355 -378 359 -384 381 -394 387 -358 361 -386 359 -416 355 -388 345 -384 377 -2262 747 -384 1149 -380 1115 -382 1113 -1120 389 -1120 389 -360 1129 -1152 367 -1136 389 -358 1131 -1152 367 -1138 389 -1116 355 -1152 367 -1134 389 -1116 353 -388 1141 -368 1123 -1138 375 -386 1113 -408 1121 -350 1175 -372 1105 -386 1145 -352 1141 -366 1145 -1114 385 -1116 377 -1122 389 -1120 421 -354 1139 -388 1109 -1132 383 -1130 369 -374 1113 -1144 385 -1114 377 -1120 391 -1128 373 -1138 385 -1130 359 -1138 377 -1120 373 -1138 383 -1130 359 -1138 379 -1160 375 -1106 385 -1130 393 -1120 377 -1118 389 -1112 393 -1140 355 -1120 421 -1114 371 -1122 391 -390 1123 -350 1139 -1134 353 -402 1113 -384 1141 -1118 385 -376 1143 -352 1161 -352 1135 -386 1113 -1132 387 -1114 395 -388 1111 -1128 387 -1114 399 -374 1115 -1142 375 -380 1117 -1118 387 -1144 363 -1136 385 -1130 367 -1130 383 -388 1107 -392 1129 -380 1115 -384 1113 -1136 389 -1114 393 -1124 393 -1120 389 -1114 393 -1124 363 -1140 389 -9416 391 -360 405 -386 329 -416 357 -392 365 -374 377 -380 343 -412 341 -412 353 -390 375 -366 385 -386 355 -2264 743 -394 1123 -388 1111 -392 1133 -1110 395 -1120 363 -382 1133 -1142 381 -1118 383 -376 1111 -1146 383 -1122 383 -1146 347 -1150 381 -1116 353 -1158 343 -410 1133 -382 1111 -1152 355 -394 1119 -382 1109 -382 1153 -378 1131 -354 1137 -396 1119 -388 1111 -1150 351 -1152 351 -1150 365 -1136 387 -356 1131 -386 1143 -1122 387 -1112 357 -420 1107 -1128 387 -1114 359 -1152 363 -1148 387 -1114 395 -1122 361 -1140 387 -1110 395 -1120 361 -1140 387 -1114 393 -1154 355 -1120 387 -1146 365 -1118 361 -1146 387 -1112 395 -1120 363 -1140 385 -1144 367 -1120 359 -420 1099 -384 1139 -1118 383 -384 1109 -392 1129 -1138 379 -366 1147 -388 1109 -386 1099 -384 1139 -1132 349 -1158 375 -380 1131 -1104 411 -1122 351 -416 1111 -1148 +RAW_Data: 353 -396 1121 -1142 347 -1150 381 -1116 355 -1156 375 -1144 387 -360 1119 -388 1107 -394 1131 -386 1101 -1152 363 -1138 387 -1112 391 -1152 357 -1116 375 -1136 383 -1122 383 -9448 357 -392 357 -398 363 -378 385 -358 383 -364 389 -386 357 -380 389 -386 347 -382 375 -384 375 -380 373 -2262 747 -376 1145 -390 1107 -386 1129 -1104 413 -1120 353 -396 1115 -1140 381 -1122 383 -376 1143 -1110 385 -1118 383 -1114 417 -1114 383 -1120 353 -1156 389 -356 1135 -386 1113 -1134 389 -358 1129 -390 1107 -392 1153 -358 1127 -388 1143 -362 1131 -356 1131 -1154 365 -1136 389 -1114 357 -1150 363 -386 1153 -358 1125 -1118 417 -1120 381 -350 1123 -1134 391 -1112 395 -1124 395 -1116 389 -1112 393 -1124 363 -1140 389 -1114 359 -1154 363 -1138 389 -1114 391 -1124 361 -1144 389 -1112 393 -1122 363 -1138 389 -1112 391 -1122 363 -1138 389 -1144 365 -1124 361 -382 1155 -350 1137 -1120 391 -386 1131 -350 1151 -1120 383 -378 1141 -352 1137 -394 1117 -390 1107 -1150 389 -1114 355 -388 1143 -1120 387 -1112 397 -388 1113 -1130 385 -344 1163 -1104 379 -1122 373 -1140 383 -1130 389 -1124 359 -386 1127 -386 1139 -368 1141 -390 1107 -1112 387 -1116 385 -1150 367 -1140 389 -1112 393 -1124 363 -1136 389 -9444 379 -340 417 -360 359 -386 359 -416 355 -386 347 -384 375 -382 375 -380 375 -378 375 -380 385 -356 379 -2278 745 -354 1151 -368 1141 -390 1105 -1114 387 -1116 385 -386 1141 -1120 389 -1114 389 -388 1113 -1130 389 -1112 393 -1124 363 -1138 389 -1112 389 -1122 363 -380 1159 -350 1137 -1122 391 -388 1097 -384 1139 -382 1125 -386 1145 -352 1141 -366 1145 -390 1107 -1110 387 -1150 353 -1150 367 -1138 387 -360 1125 -390 1109 -1152 389 -1112 357 -388 1141 -1122 389 -1110 391 -1122 395 -1112 389 -1110 397 -1120 363 -1144 389 -1114 391 -1122 365 -1138 389 -1116 389 -1142 355 -1120 389 -1112 397 -1122 363 -1140 389 -1110 393 -1130 349 -1140 405 -1134 389 -1112 357 -388 1141 -364 1129 -1142 367 -388 1111 -370 1131 -1140 383 -364 1149 -388 1109 -388 1137 -356 1127 -1118 383 -1120 413 -350 1121 -1132 407 -1140 355 -364 1149 -1112 371 -406 1129 -1104 409 -1098 383 -1116 417 -1118 381 -1118 385 -388 1111 -390 1129 -350 1137 -386 1113 -1138 389 -1114 395 -1122 393 -1120 389 -1112 393 -1124 363 -1138 389 -9444 379 -340 417 -360 359 -386 361 -414 357 -386 347 -384 375 -382 375 -380 375 -380 375 -378 373 -380 373 -2262 749 -386 1111 -408 1117 -348 1143 -1120 391 -1116 389 -358 1127 -1154 365 -1136 387 -360 1129 -1154 365 -1136 389 -1114 357 -1154 365 -1134 389 -1112 357 -390 1137 -406 1119 -1106 377 -386 1117 -406 1123 -350 1143 -384 +RAW_Data: 1149 -378 1121 -350 1145 -380 1133 -1104 375 -1136 385 -1130 359 -1138 379 -400 1129 -354 1139 -1148 355 -1150 365 -378 1119 -1146 355 -1152 365 -1134 389 -1110 397 -1122 363 -1140 389 -1110 395 -1122 363 -1142 389 -1116 357 -1152 365 -1146 389 -1112 393 -1130 349 -1138 383 -1116 413 -1120 353 -1122 387 -1114 397 -1154 355 -1124 387 -360 1133 -384 1131 -1134 383 -376 1133 -352 1133 -1132 383 -376 1139 -378 1135 -380 1115 -382 1141 -1118 353 -1160 387 -356 1133 -1118 379 -1158 353 -392 1133 -1104 379 -398 1117 -1138 383 -1118 353 -1164 353 -1146 403 -1120 353 -398 1115 -382 1143 -384 1089 -412 1121 -1106 377 -1154 355 -1118 407 -1146 351 -1130 395 -1138 355 -1118 407 -9434 353 -396 363 -380 383 -360 383 -364 389 -386 357 -414 355 -386 345 -386 375 -382 375 -380 375 -378 375 -2256 769 -384 1119 -382 1117 -380 1113 -1122 389 -1118 389 -360 1131 -1140 377 -1118 421 -354 1139 -1140 355 -1118 405 -1104 387 -1128 391 -1122 363 -1138 387 -360 1157 -354 1143 -1128 387 -360 1121 -388 1141 -362 1129 -384 1139 -388 1111 -370 1127 -384 1135 -1122 363 -1140 389 -1116 393 -1122 395 -356 1129 -384 1141 -1120 383 -1134 387 -356 1133 -1130 349 -1140 383 -1116 413 -1118 381 -1110 377 -1146 389 -1114 391 -1124 365 -1138 391 -1112 357 -1150 365 -1144 387 -1112 395 -1122 361 -1140 387 -1112 393 -1104 379 -1158 353 -1144 403 -1118 353 -1158 353 -390 1133 -390 1107 -1130 389 -358 1125 -388 1111 -1154 389 -358 1129 -386 1131 -368 1129 -382 1139 -1118 353 -1164 387 -356 1135 -1120 393 -1118 389 -358 1131 -1154 365 -376 1135 -1108 395 -1136 347 -1126 387 -1144 403 -1120 353 -398 1115 -384 1141 -372 1103 -386 1145 -1108 387 -1120 383 -1116 403 -1140 389 -1116 353 -1146 361 -1144 389 -9420 393 -362 401 -338 377 -388 385 -374 361 -382 375 -382 375 -380 373 -380 373 -380 373 -380 389 -354 379 -2272 743 -388 1137 -360 1121 -386 1111 -1152 351 -1148 359 -384 1143 -1126 387 -1114 391 -384 1117 -1130 383 -1132 369 -1134 351 -1138 377 -1142 385 -1112 359 -420 1107 -406 1121 -1104 377 -384 1119 -406 1119 -352 1171 -382 1123 -378 1119 -384 1107 -384 1119 -1136 385 -1116 393 -1120 361 -1142 387 -388 1137 -386 1113 -1128 385 -1116 357 -420 1113 -1124 387 -1114 359 -1148 395 -1116 385 -1146 363 -1116 361 -1144 387 -1144 363 -1120 361 -1144 385 -1112 393 -1152 355 -1154 353 -1144 367 -1136 355 -1158 351 -1146 369 -1120 361 -1144 385 -1148 369 -1152 357 -392 1117 -380 1107 -1152 355 -398 1119 -382 1109 -1152 385 -374 1113 -386 1139 -366 1145 -388 1111 -1110 385 -1148 353 -386 1139 -1124 387 -1142 365 -386 1113 -1132 385 -360 1125 -1144 +RAW_Data: 363 -1140 387 -1114 357 -1152 363 -1146 387 -358 1129 -386 1139 -366 1125 -384 1139 -1120 363 -1140 387 -1112 393 -1130 381 -1136 383 -1114 371 -1132 383 -116626 65 -934 133 -1954 131 -102 133 -136 97 -332 65 -430 299 -296 129 -100 265 -168 367 -100 65 -66 231 -336 9643 -7766 529 -68 467 -166 65 -134 99 -500 331 -132 65 -130 329 -98 497 -100 1195 -100 1959 -66 4163 -7346 97 -392 165 -194 97 -2978 433 -298 531 -298 65 -200 131 -132 261 -98 229 -68 12837 -340 99 -268 165 -134 65 -898 67 -100 265 -66 165 -100 597 -166 199 -298 199 -200 99 -132 233 -132 299 -132 233 -166 65 -66 4021 -168 133 -68 231 -168 4647 -130 1399 -7750 133 -1714 197 -2480 131 -200 65 -100 265 -890 63 -1152 197 -98 293 -134 65 -300 361 -100 1035 -100 231 -132 299 -100 3399 -66 6287 -4506 99 -100 65 -130 99 -196 461 -98 331 -164 97 -162 227 -64 197 -98 229 -130 195 -100 425 -526 165 -130 95 -522 457 -560 233 -98 261 -66 1155 -100 259 -130 1407 -98 553 -66 7793 -494 65 -232 65 -3652 229 -2716 361 -266 333 -200 133 -166 99 -132 267 -66 133 -132 199 -166 331 -132 331 -166 197 -950 229 -198 303 -298 365 -100 4839 -3816 165 -130 229 -696 131 -130 261 -262 97 -166 263 -894 165 -230 365 -566 129 -560 197 -324 99 -98 261 -134 131 -100 67 -334 67 -232 199 -132 165 -302 67 -100 1467 -98 459 -100 1081 -130 131 -66 8927 -232 165 -3104 99 -2812 65 -982 131 -98 195 -98 263 -264 231 -66 195 -132 193 -164 65 -100 365 -132 1629 -66 1009 -132 8383 -632 131 -3060 131 -492 425 -100 763 -166 371 -132 1197 -134 229 -694 461 -366 365 -98 329 -198 267 -168 399 -68 131 -332 493 -132 231 -132 569 -66 7765 -7568 99 -532 65 -634 133 -3540 65 -100 263 -592 261 -1484 299 -302 265 -234 1129 -304 99 -436 163 -360 97 -556 231 -166 265 -1164 165 -134 235 -100 163 -332 297 -100 197 -132 99 -566 133 -234 133 -328 295 -98 985 -98 163 -396 399 -134 1557 -134 297 -266 6875 -68 1759 -7194 133 -166 99 -266 65 -432 67 -432 393 -5086 99 -66 199 -68 263 -866 429 -100 359 -130 261 -132 267 -134 533 -134 9251 -4184 65 -1156 165 -198 65 -426 297 -492 67 -164 131 -198 259 -164 199 -100 733 -134 865 -100 397 -132 65 -100 197 -66 327 -164 227 -98 231 -132 97 -262 99 -130 229 -66 589 -96 1119 -98 1905 -7486 599 -66 561 -66 359 -98 757 -162 261 -66 323 -130 5573 -8538 99 -894 131 -594 229 -364 63 -1378 197 -1682 331 -100 199 -166 diff --git a/assets/unit_tests/subghz/linear_delta3.sub b/assets/unit_tests/subghz/linear_delta3.sub new file mode 100644 index 000000000..f00507428 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3.sub @@ -0,0 +1,7 @@ +Filetype: Flipper SubGhz Key File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: LinearDelta3 +Bit: 8 +Key: 00 00 00 00 00 00 00 D0 diff --git a/assets/unit_tests/subghz/linear_delta3_raw.sub b/assets/unit_tests/subghz/linear_delta3_raw.sub new file mode 100644 index 000000000..1973622a5 --- /dev/null +++ b/assets/unit_tests/subghz/linear_delta3_raw.sub @@ -0,0 +1,8 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: -66 11813 -100 14655 -98 40111 -66 1625 -2116 1933 -34732 501 -11730 235 -3728 1887 -2106 1933 -2092 1971 -2072 1959 -34712 511 -3554 445 -3556 1997 -2036 455 -3594 1963 -2046 1979 -2076 1961 -2070 1989 -34690 483 -7724 1739 -2226 355 -3684 1857 -2138 1929 -2078 1965 -2074 1947 -34750 487 -3538 473 -3544 1993 -2042 485 -3548 1961 -2070 1965 -2070 1969 -2042 1997 -34716 443 -7734 1753 -2236 323 -3676 1903 -2098 1945 -2102 1927 -2070 1989 -34710 521 -3532 473 -3544 1991 -2032 481 -3556 1969 -2076 1967 -2036 1991 -2066 1969 -34718 467 -7756 1739 -2192 363 -3654 1889 -2132 1929 -2096 1935 -2070 1987 -34716 511 -3522 471 -3554 2009 -2036 459 -3550 2003 -2038 1979 -2042 1999 -2042 1999 -34704 471 -11774 225 -3710 1879 -2162 1885 -2112 1925 -2110 1939 -34738 459 -3636 403 -3612 1939 -2062 451 -3566 1985 -2044 1995 -2040 2009 -2032 2003 -34684 495 -3680 295 -3648 1935 -2098 423 -3562 2001 -2038 1989 -2044 2003 -2036 1977 -34718 461 -3678 295 -3684 1901 -2098 429 -3596 1967 -2036 1981 -2048 1993 -2042 2013 -34686 521 -3530 457 -3568 1999 -2036 455 -3552 1999 -2032 2019 -2024 1995 -2022 1997 -34716 441 -15774 1809 -2192 1905 -2100 1919 -2112 1961 -34720 417 -3830 167 -3710 1863 -2144 357 -3674 1909 -2100 1955 -2062 1977 -2072 1965 -34710 487 -3562 453 -3554 1985 -2052 481 -3536 2019 -2010 2001 -2042 1997 -2038 2005 -34716 451 -3602 433 -3584 1959 -2070 451 -3560 2001 -2038 1993 -2042 1967 -2072 1973 -34712 459 -3622 393 -3624 1933 -2068 457 -3584 1965 -2064 1979 -2052 1967 -2044 1981 -34722 477 -3608 397 -3588 1961 -2096 413 -3596 1971 -2040 1979 -2072 1963 -2070 1959 -34714 495 -3558 483 -3538 1985 -2042 479 -3562 1985 -2046 1967 -2070 1973 -2054 1995 -34688 493 -3578 413 -3614 1939 -2074 465 -3560 1971 -2038 2017 -2018 1995 -2042 2013 -34726 479 -3528 475 -3556 1999 -2036 455 -3570 1999 -2040 1973 -2054 2001 -2032 1987 -34720 477 -3562 445 -3602 1949 -2054 481 -3562 1975 -2060 1963 -2064 1977 -2038 2005 -34702 485 -3570 447 -3550 2015 -2020 479 -3564 1983 -2048 1999 -2034 1971 -2064 1993 -34688 517 -3516 497 -3532 1999 -2038 481 -3558 1997 -2004 2027 -2042 1963 -2038 1997 -34716 491 -3562 461 -3548 1995 -2032 491 -3524 2005 -2036 1989 -2038 1995 -2046 1979 -34714 465 -3682 293 -3680 1905 -2096 431 -3592 1969 -2070 1977 -2052 1965 -2044 1981 -34734 479 -3564 463 -3556 1999 -2032 457 -3550 1995 -2044 2011 -2042 1997 -2006 2027 -34680 531 -3524 483 -3538 1987 -2044 479 -3534 2013 -2048 1965 -2062 1987 -2030 1997 -34712 473 -3592 445 -3562 1975 -2072 451 -3566 1965 -2042 2013 -2046 1963 -2064 1993 -34700 459 -3632 371 -3638 1915 -2084 449 -3568 1987 -2046 1971 -2070 1983 -2022 1997 -34726 487 -3524 477 -3562 1985 -2044 481 -3542 2005 -2040 1995 -2038 1967 -2046 1993 -34710 511 -3528 471 -3560 1967 -2070 459 -3558 1971 +RAW_Data: -2072 1971 -2056 1971 -2074 1973 -34714 455 -3634 373 -3634 1901 -2110 419 -3620 1941 -2070 1991 -2040 1999 -2038 1965 -34740 467 -3562 481 -3534 1983 -2070 449 -3546 1999 -2044 1993 -2042 2003 -2036 1975 -34702 521 -3560 443 -3586 1969 -2044 449 -3562 1997 -2046 1987 -2042 2007 -2034 1973 -34732 487 -3562 443 -3582 1979 -2058 445 -3560 1995 -2044 1997 -2028 1987 -2034 2003 -34710 515 -3518 485 -3566 1977 -2036 483 -3536 1999 -2044 2009 -2024 1995 -2068 1973 -34710 487 -3564 471 -3558 1977 -2054 447 -3564 1991 -2042 1997 -2036 2007 -2034 2001 -34684 529 -3526 469 -3548 1989 -2038 483 -3562 1997 -2038 1973 -2034 1999 -2036 1997 -34728 487 -3536 479 -3534 2013 -2044 449 -3570 1985 -2042 1993 -2044 2005 -2014 1995 -34710 473 -3594 439 -3562 1995 -2040 457 -3564 2001 -2040 1975 -2046 1995 -2046 1999 -34704 491 -3548 451 -3570 1991 -2042 447 -3578 1967 -2046 1995 -2042 1999 -2034 2001 -34712 491 -3562 443 -3584 1981 -2018 479 -3562 1985 -2044 1997 -2030 1989 -2040 1997 -34722 489 -3554 459 -3560 1969 -2068 453 -3554 1999 -2034 1987 -2058 1997 -2046 1983 -34702 487 -3534 479 -3564 1983 -2040 483 -3538 1981 -2048 1993 -2048 2007 -2044 1995 -34696 489 -3550 453 -3570 1995 -2050 447 -3564 1983 -2040 1999 -2034 2003 -2034 1995 -34690 495 -3580 433 -3586 1969 -2064 453 -3552 1995 -2036 1991 -2056 1997 -2046 1987 -34706 441 -3636 373 -3626 1959 -2074 419 -3592 1963 -2074 1989 -2044 1971 -2070 1981 -34698 509 -3526 503 -3528 2005 -2034 481 -3528 1993 -2042 1999 -2066 1989 -2034 2003 -34678 495 -3540 481 -3546 1997 -2046 473 -3554 1999 -2034 2001 -2036 1995 -2046 1983 -34720 475 -3560 469 -3548 1997 -2030 485 -3566 1963 -2066 1983 -2046 1999 -2034 1973 -34734 487 -3560 443 -3584 1981 -2052 445 -3568 1987 -2044 1999 -2032 1993 -2034 2007 -34702 491 -3560 459 -3558 1967 -2070 455 -3556 2003 -2036 1977 -2042 2005 -2028 1997 -34730 461 -3564 473 -3536 2011 -2046 449 -3566 1989 -2044 1997 -2042 1971 -2054 2001 -34708 475 -3560 479 -3528 1999 -2040 485 -3566 1963 -2040 2013 -2042 1995 -2034 1987 -34694 519 -3554 441 -3582 1981 -2052 449 -3564 1985 -2040 1993 -2034 1991 -2062 1975 -34714 529 -3534 463 -3558 1969 -2068 451 -3560 2003 -2038 1993 -2042 1969 -2070 1975 -34720 493 -3582 383 -3616 1937 -2072 469 -3558 1995 -2036 1975 -2066 1995 -2042 1989 -34678 531 -3560 391 -3622 1937 -2094 429 -3588 1967 -2070 1981 -2054 1965 -2038 2021 -34682 525 -3524 481 -3564 1989 -2040 445 -3554 1997 -2040 2005 -2034 2001 -2024 1991 -34706 517 -3586 409 -3610 1927 -2076 451 -3558 1967 -2074 1993 -2038 2001 -2040 1975 -34714 495 -3588 409 -3602 1933 -2088 447 -3584 1965 -2044 1999 -2036 2007 -2030 1995 -34692 525 -3538 447 -3580 1981 -2042 487 -3542 1995 -2040 1969 -2072 1969 -2044 1991 -34714 443 -3636 399 -3630 1899 -2106 413 -3584 1997 +RAW_Data: -2034 2007 -2038 1969 -2076 1965 -34708 493 -3564 451 -3570 1965 -2074 449 -3548 2003 -2044 1987 -2038 1999 -2030 1991 -34710 493 -3602 403 -3612 1943 -2092 419 -3596 1963 -2062 1963 -2042 2001 -2064 1967 -34716 497 -3616 357 -3648 1903 -2132 399 -3596 1963 -2068 1977 -2052 1967 -2046 2019 -34684 497 -3614 359 -3650 1909 -2100 405 -3630 1925 -2098 1965 -2066 1965 -2056 1971 -34712 477 -3634 371 -3628 1931 -2104 391 -3624 1939 -2066 1975 -2052 2005 -2036 1985 -34714 449 -3668 337 -3664 1901 -2124 417 -3594 1963 -2048 1995 -2028 1993 -2066 1971 -34698 463 -3642 353 -3650 1943 -2066 433 -3594 1963 -2066 1995 -2034 1997 -2046 1981 -34730 479 -3560 445 -3562 1997 -2032 485 -3560 1965 -2062 1989 -2044 1999 -2032 1971 -34724 463 -3608 399 -3620 1943 -2096 421 -3592 1961 -2074 1979 -2036 2011 -2032 1971 -34734 469 -3558 485 -3552 1999 -2028 473 -3552 2003 -2032 2003 -2032 1997 -2044 1993 -34704 443 -3602 431 -3596 1967 -2076 447 -3556 1975 -2058 1997 -2040 1991 -2048 1971 -161100 97 -428 165 -200 395 -428 97 -100 559 -130 97 -164 129 -98 391 -98 295 -166 52395 -66 16239 -66 42541 -66 755 -132 14015 -98 2885 -68 10385 -98 40045 -100 987 -68 25539 -66 19799 -98 136101 -100 5141 -66 5709 -68 23177 -66 11097 -66 329 -100 261 -66 15755 -98 20575 -66 3645 -100 51411 -66 14441 -132 4467 -66 3965 -132 3707 -66 33107 -66 10373 -66 1775 -66 4185 -132 1429 -68 4675 -100 13419 -66 33985 diff --git a/assets/unit_tests/subghz/nice_one_raw.sub b/assets/unit_tests/subghz/nice_one_raw.sub new file mode 100644 index 000000000..169b3f088 --- /dev/null +++ b/assets/unit_tests/subghz/nice_one_raw.sub @@ -0,0 +1,12 @@ +Filetype: Flipper SubGhz RAW File +Version: 1 +Frequency: 433920000 +Preset: FuriHalSubGhzPresetOok650Async +Protocol: RAW +RAW_Data: 7855 -12784 1413 -1544 469 -1040 465 -1010 479 -1020 967 -548 445 -1046 973 -524 967 -520 981 -516 483 -1042 449 -1034 949 -528 495 -1008 479 -1016 985 -518 453 -1042 449 -1052 949 -514 483 -1012 985 -512 477 -1042 445 -1050 951 -548 971 -512 975 -520 967 -554 949 -548 451 -1040 967 -520 987 -518 455 -1038 475 -1016 977 -518 983 -514 473 -1018 975 -518 487 -1002 475 -1020 965 -516 477 -1012 1007 -522 445 -1034 491 -1008 973 -524 +RAW_Data: 481 -992 481 -1010 483 -1030 977 -520 487 -1008 973 -522 987 -518 983 -514 965 -522 987 -520 489 -1004 473 -1018 471 -1016 1005 -476 511 -1012 457 -1018 1001 -510 975 -520 471 -1022 483 -1016 969 -536 1003 -454 981 -480 479 -986 981 -486 479 -946 989 -492 973 -484 473 -976 1503 -23606 1433 -1542 493 -1006 473 -1032 441 -1048 971 -514 483 -1012 985 -518 479 -1014 481 -1012 457 -1050 443 -1044 977 -520 473 -1004 495 -1004 969 -556 453 -1036 451 -1038 973 -520 485 -994 981 -520 457 -1050 477 -1014 977 -494 985 -538 961 -512 1005 -518 951 -526 491 -1006 969 -520 985 -524 455 -1044 447 -1048 983 -518 983 -514 441 -1050 981 -518 453 -1042 447 -1050 981 -518 451 -1046 975 -520 451 -1022 483 -1008 1001 -522 447 -1020 485 -1008 473 -1016 981 -550 449 -1044 977 -520 949 -550 979 -516 967 -520 983 -522 455 -1042 447 -1050 451 -1024 981 -520 483 -1018 963 -546 479 -1010 967 -520 483 -1022 975 -522 967 -552 487 -960 481 -990 451 -994 481 -980 479 -986 449 -984 969 -480 983 -510 1465 -23612 1473 -1520 479 -1026 453 -1044 451 -1036 943 -552 453 -1044 949 -518 481 -1018 977 -524 459 -1046 439 -1046 973 -528 463 -1012 471 -1046 943 -552 443 -1034 457 -1042 977 -518 479 -1028 949 -554 451 -1014 481 -1018 981 -524 985 -518 971 -514 979 -522 987 -512 477 -1016 977 -522 969 -552 449 -1016 483 -1014 985 -518 973 -516 481 -1012 967 -552 449 -1020 483 -1010 969 -554 447 -1022 977 -520 475 -1018 479 -1018 975 -522 457 -1036 479 -1016 479 -1002 969 -552 447 -1054 943 -548 969 -520 983 -520 983 -516 969 -518 479 -1030 453 -1044 449 -1048 943 -548 451 -1044 945 -552 975 -518 947 -552 449 -1034 975 -524 455 -1040 969 -520 449 -982 969 -518 945 -484 481 -984 481 -994 447 -986 477 -998 1435 -23658 1441 -1530 483 -1008 483 -1034 449 -1022 977 -520 485 -1018 479 -1018 975 -506 473 -1036 469 -1042 463 -1010 977 -520 487 -1030 451 -1010 981 -520 481 -1018 481 -1014 983 -518 479 -1016 975 -492 497 -1014 467 -1014 977 -520 975 -526 985 -516 979 -506 1005 -496 493 -1008 975 -522 983 -518 453 -1040 475 -1016 975 -524 987 -514 471 -1038 955 -514 473 -1046 445 -1044 967 -514 477 -1016 975 -520 457 -1050 477 -1010 973 -522 473 -1000 479 -1030 453 -1038 969 -506 473 -1050 971 -512 979 -524 955 -548 973 -512 975 -518 475 -1036 473 -1006 493 -1008 975 -520 973 -526 487 -1004 475 -1018 965 -516 1005 -512 481 -1014 985 -518 483 -986 975 -488 977 -480 977 -486 975 -482 481 -982 975 -480 977 -488 1477 -23618 1389 -1634 369 -1114 383 -1078 431 -1072 +RAW_Data: 931 -550 451 -1046 447 -1042 967 -552 945 -522 459 -1042 445 -1050 943 -552 439 -1036 459 -1046 977 -508 477 -1030 455 -1044 945 -552 451 -1020 979 -524 459 -1046 443 -1048 979 -518 967 -534 957 -516 977 -518 973 -528 455 -1042 973 -520 975 -526 459 -1040 481 -1020 969 -510 967 -546 447 -1050 955 -544 441 -1044 449 -1048 953 -550 443 -1046 975 -518 485 -1010 455 -1044 943 -554 447 -1054 449 -1010 475 -1048 943 -550 453 -1040 969 -520 973 -522 985 -514 969 -554 949 -524 459 -1040 477 -1014 483 -1034 947 -520 981 -554 447 -1016 977 -524 983 -516 973 -516 483 -1016 455 -1046 973 -484 977 -518 449 -986 447 -1016 971 -482 449 -1018 443 -1014 449 -984 1461 -129764 65 -3200 133 -464 133 -298 429 -132 265 -98 231 -134 265 -164 3439 -132 727 -132 199 -2058 133 -1644 361 -166 65 -492 165 -264 591 -428 197 -198 201 -98 831 -68 2313 -100 5839 -10922 65 -1320 425 -262 297 -428 97 -362 2463 -98 1025 -66 5263 -5030 99 -6924 461 -1092 133 -98 333 -166 2739 -132 3131 -66 10535 -2008 131 -434 297 -1058 65 -132 99 -198 529 -198 97 -526 97 -66 493 -664 99 -232 2613 -132 5371 -11166 229 -198 163 -394 199 -398 365 -132 99 -166 2121 -100 1195 -68 1821 -100 10635 -468 67 -1256 65 -2144 229 -100 163 -394 593 -98 67 -166 1677 -66 791 -66 335 -98 11033 -566 65 -1460 165 -1520 497 -1254 491 -564 99 -330 99 -232 1227 -132 2973 -66 3661 -11964 131 -132 99 -398 131 -328 97 -232 363 -396 1379 -98 99 -166 1591 -66 12171 -4136 65 -298 265 -298 199 -462 99 -330 65 -166 163 -66 1591 -66 165 -166 12079 -1002 65 -366 465 -530 97 -134 561 -66 497 -494 99 -64 131 -134 1095 -66 6537 -5066 65 -5458 397 -724 165 -466 131 -166 14293 -436 65 -1590 65 -1462 459 -332 65 -396 563 -794 197 -300 1255 -12100 99 -130 495 -166 97 -296 97 -658 757 -98 959 -66 1029 -1346 165 -2620 395 -494 197 -166 163 -198 65 -98 195 -394 821 -98 3063 -100 4469 -12120 497 -166 65 -462 195 -164 295 -66 4361 -100 1755 -100 131 -66 9415 -3840 99 -530 197 -364 463 -330 365 -332 133 -100 165 -166 2113 -100 1461 -132 4175 -3772 97 -7124 231 -1258 165 -100 429 -1326 995 -200 1755 -66 1519 -100 6437 -7198 133 -300 527 -398 165 -232 131 -166 67 -164 16443 -3270 131 -658 131 -726 97 -858 97 -300 331 -100 629 -10288 67 -164 133 -1458 297 -364 65 -98 163 -758 1189 -66 199 -68 1791 -66 897 -132 165 -3410 163 -364 99 -98 99 -66 365 -232 789 -494 65 -328 629 -66 1259 -66 365 -11422 7923 -12864 1405 -1562 +RAW_Data: 451 -1040 441 -1052 449 -1050 945 -554 449 -1052 451 -1020 481 -1010 473 -1050 449 -1052 451 -1040 969 -520 977 -520 455 -1042 977 -522 447 -1056 947 -518 979 -546 447 -1052 451 -1040 441 -1048 983 -518 455 -1044 449 -1018 979 -548 947 -554 449 -1032 481 -992 483 -1012 985 -514 999 -512 479 -1012 485 -1014 961 -544 477 -1010 965 -522 981 -512 483 -1012 487 -1020 477 -1014 479 -1016 459 -1014 471 -1012 1003 -492 997 -522 483 -1016 979 -522 985 -520 975 -512 975 -520 999 -488 985 -514 481 -1006 1001 -522 483 -990 483 -1008 483 -1020 977 -516 975 -518 999 -524 451 -1018 1009 -482 999 -506 983 -524 487 -1004 473 -980 501 -952 517 -940 497 -982 489 -974 987 -452 495 -974 487 -954 1485 -23662 1457 -1556 445 -1026 483 -1010 475 -1016 975 -518 483 -1014 487 -1034 447 -1022 977 -522 457 -1046 475 -1018 975 -524 985 -518 477 -1016 977 -524 459 -1048 969 -514 977 -522 457 -1038 479 -1018 481 -1002 1001 -520 447 -1054 449 -1008 1001 -520 977 -520 451 -1040 475 -1014 479 -1028 949 -518 983 -542 447 -1058 449 -1044 947 -552 447 -1024 977 -520 967 -542 479 -1024 451 -1040 441 -1050 451 -1028 481 -1014 483 -1010 965 -548 973 -518 485 -1010 981 -516 967 -520 983 -524 981 -514 969 -538 967 -518 481 -1016 973 -524 485 -1016 465 -1012 479 -1020 983 -532 959 -514 975 -554 949 -526 985 -512 969 -554 967 -534 461 -1042 443 -1014 967 -478 455 -1006 969 -486 967 -480 983 -486 969 -514 451 -982 1461 -23692 563 -4014 291 -1220 263 -1228 829 -620 883 -626 851 -608 903 -622 387 -1082 391 -1102 409 -1084 913 -588 941 -548 443 -1056 945 -522 445 -1046 971 -552 977 -516 441 -1048 481 -992 483 -1010 979 -554 451 -1018 481 -1014 983 -518 977 -514 479 -1040 447 -1034 485 -996 975 -520 979 -520 483 -1016 481 -1008 999 -506 471 -1050 971 -514 975 -520 473 -1000 483 -1020 481 -1008 473 -1018 481 -1020 481 -1008 967 -554 945 -518 481 -1038 967 -520 985 -520 981 -514 967 -520 985 -520 981 -508 479 -1016 1003 -518 479 -1010 479 -1010 473 -1018 975 -516 979 -520 983 -520 975 -514 977 -518 999 -520 979 -518 451 -1040 479 -986 479 -962 1007 -486 451 -986 975 -486 977 -482 483 -980 477 -982 1473 -23656 1453 -1548 447 -1016 485 -1012 491 -1012 973 -520 981 -526 983 -514 971 -554 947 -526 491 -1008 475 -1020 983 -498 989 -516 483 -1014 977 -524 453 -1044 979 -518 979 -520 453 -1042 449 -1048 447 -1022 975 -518 475 -1050 447 -1020 977 -522 983 -518 481 -1016 481 -1012 473 -1002 973 -550 945 -552 449 -1050 447 -1020 975 -522 487 -1034 973 -520 +RAW_Data: 979 -514 443 -1046 479 -1028 451 -1042 451 -1048 447 -1022 485 -1014 983 -520 973 -516 483 -1012 983 -518 973 -516 977 -520 1003 -520 975 -520 981 -514 475 -1034 969 -516 479 -1016 447 -1046 475 -1018 975 -516 975 -522 983 -510 469 -1010 1007 -518 951 -530 989 -516 973 -556 951 -494 481 -978 487 -978 975 -460 1005 -466 979 -486 969 -508 981 -450 1489 -23666 571 -4036 269 -1224 257 -1250 787 -642 867 -622 883 -622 359 -1136 373 -1086 421 -1080 417 -1074 935 -550 947 -552 445 -1048 939 -552 451 -1046 947 -552 947 -550 451 -1040 443 -1048 453 -1024 977 -522 471 -1034 449 -1020 973 -540 975 -508 479 -1032 453 -1042 449 -1050 977 -518 979 -518 449 -1018 481 -1018 975 -518 473 -1034 963 -542 961 -544 447 -1044 473 -1020 479 -1014 481 -1010 473 -1032 471 -1010 959 -546 973 -492 499 -1006 997 -510 977 -524 953 -552 971 -512 973 -508 979 -554 451 -1016 977 -518 471 -1038 485 -1010 457 -1036 969 -506 999 -520 481 -1014 975 -522 967 -520 975 -548 451 -1038 475 -1022 965 -518 463 -978 985 -486 465 -978 457 -1016 463 -978 985 -486 963 -480 1477 -129906 495 -726 197 -328 295 -132 2547 -66 233 -98 11033 -1856 233 -1458 65 -198 165 -134 199 -168 101 -694 463 -530 165 -300 99 -232 2479 -98 1745 -98 3029 -132 163 -1460 65 -500 65 -400 99 -664 895 -398 65 -564 331 -166 97 -66 197 -98 3813 -98 10097 -3848 165 -232 67 -266 397 -596 165 -66 199 -166 99 -66 199 -398 165 -166 1721 -232 429 -166 133 -330 133 -698 493 -200 197 -428 11029 -12118 65 -198 199 -68 231 -230 101 -166 99 -664 131 -132 3163 -4238 331 -298 531 -398 299 -98 199 -166 563 -100 131 -98 893 -66 3141 -1556 133 -1722 131 -830 197 -262 195 -66 163 -462 195 -396 195 -134 499 -132 265 -66 1717 -166 3175 -11366 199 -164 131 -66 163 -98 525 -98 363 -264 4495 -100 229 -66 131 -66 593 -3002 97 -394 131 -426 99 -462 597 -692 295 -298 431 -230 4231 -66 9711 -3246 131 -100 99 -400 263 -498 65 -100 297 -98 99 -132 65 -862 131 -66 365 -396 99 -166 1991 -98 1611 -132 10333 -790 65 -1984 99 -896 165 -332 365 -232 131 -830 65 -66 397 -166 197 -66 65 -496 199 -100 9975 -1728 67 -5008 727 -98 131 -100 2873 -66 12011 -3150 67 -960 99 -234 99 -298 231 -232 195 -266 165 -296 261 -166 757 -66 629 -196 657 -100 197 -134 297 -364 11237 -1684 65 -2076 165 -462 491 -100 663 -630 329 -264 263 -100 1357 -66 461 -1676 99 -1782 295 -296 65 -296 163 -230 99 -132 295 -66 163 -362 197 -724 757 -66 +RAW_Data: 3785 -66 13551 -1808 97 -730 65 -100 231 -132 131 -1230 593 -232 1579 -66 2667 -200 101 -3480 165 -692 133 -396 427 -1524 363 -66 431 -132 10305 -8288 461 -628 67 -430 725 -66 1053 -66 4501 -230 165 -66 331 -66 355 -266 263 -132 63 -562 459 -462 197 -66 129 -132 65 -100 2643 -132 2107 -66 9651 -3692 99 -100 195 -294 97 -660 759 -328 165 -560 891 -66 1953 -66 11305 -362 263 -662 131 -432 65 -134 563 -430 131 -132 1819 -100 165 -166 1061 -98 10089 -2476 65 -854 395 -198 99 -492 131 -164 229 -466 199 -428 299 -100 927 -200 1557 -134 4269 -10464 133 -1624 65 -198 265 -398 131 -430 729 -134 6189 -66 5421 -2082 165 -3342 19967 -12808 1439 -1536 453 -1046 449 -1032 449 -1056 947 -552 977 -522 977 -518 453 -1038 977 -522 977 -520 457 -1038 977 -506 1005 -496 495 -1008 975 -538 973 -530 465 -1008 975 -554 453 -1036 947 -518 487 -1008 475 -1042 443 -1050 461 -1008 1005 -510 447 -1048 985 -510 469 -1006 1005 -494 997 -514 975 -514 975 -504 999 -506 479 -1034 491 -1010 975 -508 973 -524 491 -1004 473 -1018 997 -520 975 -512 975 -518 473 -1030 983 -516 981 -514 471 -998 997 -522 481 -1012 481 -1012 457 -1050 973 -512 977 -524 459 -1016 1003 -512 479 -1014 459 -1016 475 -1012 1007 -522 969 -502 495 -1008 477 -1030 965 -522 975 -514 479 -1000 471 -1062 471 -964 483 -982 471 -1000 471 -980 979 -448 503 -988 465 -976 487 -974 1459 -23696 1407 -1616 401 -1068 429 -1080 419 -1058 935 -566 923 -584 417 -1078 939 -524 457 -1042 973 -550 443 -1028 949 -554 945 -552 447 -1022 979 -518 971 -542 479 -1024 947 -550 441 -1048 979 -518 453 -1044 449 -1050 449 -1020 485 -1014 981 -518 479 -1014 975 -524 459 -1036 973 -516 979 -518 971 -552 945 -550 945 -552 449 -1030 479 -1026 947 -554 949 -552 449 -1018 479 -1008 981 -518 975 -548 945 -554 451 -1034 967 -514 997 -514 445 -1036 967 -554 447 -1022 485 -1010 475 -1016 975 -518 977 -520 487 -1014 973 -552 451 -1040 441 -1050 447 -1022 485 -1014 987 -516 479 -1014 483 -1014 459 -1046 969 -514 449 -1044 967 -546 973 -488 447 -1016 443 -1000 973 -490 475 -980 983 -482 441 -1016 465 -976 1475 -23652 1451 -1548 479 -1014 461 -1014 471 -1044 975 -520 971 -502 495 -1012 977 -506 1005 -498 989 -516 481 -1016 975 -520 981 -514 475 -1014 979 -522 983 -512 475 -1022 965 -514 471 -1046 973 -494 473 -1016 475 -1046 447 -1050 463 -1012 999 -512 481 -1012 983 -520 477 -1014 977 -524 955 -548 973 -512 975 -520 967 -556 449 -1020 483 -1012 983 -520 973 -516 481 -1008 473 -1034 +RAW_Data: 967 -538 963 -544 973 -522 471 -1006 989 -512 1007 -520 443 -1036 985 -516 449 -1048 451 -1022 483 -1012 983 -520 977 -514 481 -1012 979 -514 483 -1022 481 -1010 471 -1020 479 -1020 979 -524 457 -1048 973 -514 483 -1012 981 -520 483 -1018 481 -1014 485 -986 467 -980 981 -486 469 -978 457 -1004 963 -480 983 -486 971 -514 1441 -23704 1383 -1628 389 -1112 385 -1092 407 -1092 915 -552 941 -570 441 -1064 423 -1046 451 -1044 939 -556 455 -1048 945 -552 973 -522 453 -1046 945 -552 947 -550 451 -1040 969 -518 479 -1028 951 -552 451 -1018 479 -1018 483 -1014 459 -1044 971 -514 483 -1010 971 -544 447 -1020 977 -524 987 -518 973 -516 979 -524 985 -518 479 -1016 447 -1050 953 -548 971 -514 483 -1014 459 -1048 967 -514 977 -526 953 -548 443 -1046 975 -492 995 -512 471 -1050 943 -552 445 -1032 455 -1044 449 -1048 941 -550 945 -552 449 -1050 945 -552 451 -1044 449 -1018 479 -1016 479 -1002 969 -542 973 -522 455 -1040 477 -1022 967 -534 959 -514 975 -554 469 -1008 449 -980 469 -1008 943 -484 1001 -484 467 -980 983 -482 961 -514 1439 -23700 1469 -1510 495 -1008 473 -1036 463 -1012 969 -546 973 -522 473 -1018 479 -1014 975 -526 955 -516 475 -1046 975 -490 999 -518 481 -1014 975 -520 967 -514 481 -1022 979 -524 457 -1048 971 -514 481 -1010 485 -1020 477 -1014 479 -1000 1001 -522 451 -1020 977 -520 473 -1032 967 -538 959 -514 1005 -522 965 -504 989 -514 475 -1046 441 -1050 971 -514 975 -520 473 -1018 481 -1014 979 -520 983 -520 977 -516 485 -1010 979 -544 975 -518 453 -1042 981 -520 453 -1024 483 -1010 457 -1050 975 -512 975 -524 459 -1048 973 -514 481 -1010 473 -1016 479 -1016 477 -1036 967 -506 995 -512 965 -546 445 -1048 957 -516 1005 -512 445 -1046 979 -486 473 -980 979 -486 473 -980 981 -486 473 -980 485 -986 467 -976 1477 -142204 197 -1486 165 -198 165 -664 295 -232 99 -266 231 -166 3045 -100 13411 -3670 197 -498 131 -166 231 -198 165 -66 265 -134 129 -1062 431 -130 465 -134 13447 -3848 329 -100 163 -298 99 -164 463 -98 197 -98 131 -198 65 -296 493 -264 789 -66 7225 -12438 99 -164 463 -132 197 -630 65 -198 2487 -66 165 -100 10097 -6554 459 -664 297 -460 4925 -132 6063 -12078 497 -98 99 -200 97 -234 165 -298 1721 -134 265 -100 3035 -100 12081 -3674 231 -100 97 -200 97 -264 461 -100 99 -132 231 -100 97 -430 527 -200 231 -64 2081 -132 327 -100 529 -66 831 -66 3067 -4704 99 -5520 97 -496 67 -198 167 -498 693 -462 2341 -15926 65 -1392 659 -134 131 -298 165 -66 99 -298 4777 -4208 429 -66 diff --git a/documentation/UniversalRemotes.md b/documentation/UniversalRemotes.md new file mode 100644 index 000000000..325f640d7 --- /dev/null +++ b/documentation/UniversalRemotes.md @@ -0,0 +1,76 @@ +# Universal Remotes + +## Televisions + +Adding your TV set to the universal remote is quite straightforward. Up to 6 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`, `Ch_next`, and `Ch_prev`. Any of them can be omitted if not supported by your TV. + +Each signal is recorded using the following algorithm: + +1. Get the remote and point it to Flipper's IR receiver. +2. Start learning a new remote if it's the first button or press `+` to add a new button otherwise. +3. Press a remote button and save it under a corresponding name. +4. Repeat steps 2-3 until all required signals are saved. + +The signal names are self-explanatory. Remember to make sure that every recorded signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [TV universal remote file](/assets/resources/infrared/assets/tv.ir). + +## Audio players + +Adding your audio player to the universal remote is done in the same manner as described above. Up to 8 signals can be recorded: `Power`, `Play`, `Pause`, `Vol_up`, `Vol_dn`, `Next`, `Prev`, and `Mute`. Any of them can be omitted if not supported by the player. + +The signal names are self-explanatory. +On many remotes, the `Play` button doubles as `Pause`. In this case, record it as `Play` omitting the `Pause`. +Make sure that every signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [audio player universal remote file](/assets/resources/infrared/assets/audio.ir). + +## Projectors + +Adding your projector to the universal remote is really simple. Up to 4 signals can be recorded: `Power`, `Mute`, `Vol_up`, `Vol_dn`. Any of them can be omitted if not supported by your projector. +To save time, please make sure every recording has been named accordingly. +In case of omitting, on most projectors with the 4 following buttons, you should not have a problem. + + +## Air conditioners + +Air conditioners differ from most other infrared-controlled devices because their state is tracked by the remote. +The majority of A/C remotes have a small display that shows the current mode, temperature, and other settings. +When the user presses a button, a whole set of parameters is transmitted to the device, which must be recorded and used as a whole. + +In order to add a particular air conditioner to the universal remote, 6 signals must be recorded: `Off`, `Dh`, `Cool_hi`, `Cool_lo`, `Heat_hi`, and `Heat_lo`. +Each signal (except `Off`) is recorded using the following algorithm: + +1. Get the remote and press the **Power Button** so that the display shows that A/C is ON. +2. Set the A/C to the corresponding mode (see table below), leaving other parameters such as fan speed or vane on **AUTO** (if applicable). +3. Press the **POWER** button to switch the A/C off. +4. Start learning a new remote on Flipper if it's the first button or press `+` to add a new button otherwise. +5. Point the remote to Flipper's IR receiver as directed and press **POWER** button once again. +6. Save the resulting signal under the specified name. +7. Repeat steps 2-6 for each signal from the table below. + +| Signal | Mode | Temperature | Note | +| :-----: | :--------: | :---------: | ----------------------------------- | +| Dh | Dehumidify | N/A | | +| Cool_hi | Cooling | See note | Lowest temperature in cooling mode | +| Cool_lo | Cooling | 23°C | | +| Heat_hi | Heating | See note | Highest temperature in heating mode | +| Heat_lo | Heating | 23°C | | + +Finally, record the `Off` signal: + +1. Make sure the display shows that the A/C is ON. +2. Start learning a new signal on Flipper and point the remote towards the IR receiver. +3. Press the **POWER** button so that the remote shows the OFF state. +4. Save the resulting signal under the name `Off`. + +The resulting remote file should now contain 6 signals. You can omit any of them, but you then won't be able to use their functionality. +Test the file against the actual device. Make sure that every signal does what it's supposed to. + +If everything checks out, append these signals **to the end** of the [A/C universal remote file](/assets/resources/infrared/assets/ac.ir). + +## Final steps + +The order of signals is not important, but they should be preceded by the following comment: `# Model: ` in order to keep the library organized. + +When done, open a pull request containing the changed file. diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3d88c7cc1..3a687f32e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,12.8,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1348,8 +1348,7 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" -Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, @@ -2651,12 +2650,14 @@ Function,+,subghz_block_generic_get_preset_name,void,"const char*, FuriString*" Function,+,subghz_block_generic_serialize,_Bool,"SubGhzBlockGeneric*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_environment_alloc,SubGhzEnvironment*, Function,+,subghz_environment_free,void,SubGhzEnvironment* +Function,+,subghz_environment_get_alutech_at_4n_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_came_atomo_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_keystore,SubGhzKeystore*,SubGhzEnvironment* Function,+,subghz_environment_get_nice_flor_s_rainbow_table_file_name,const char*,SubGhzEnvironment* Function,+,subghz_environment_get_protocol_name_registry,const char*,"SubGhzEnvironment*, size_t" Function,+,subghz_environment_get_protocol_registry,void*,SubGhzEnvironment* Function,+,subghz_environment_load_keystore,_Bool,"SubGhzEnvironment*, const char*" +Function,+,subghz_environment_set_alutech_at_4n_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_came_atomo_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_nice_flor_s_rainbow_table_file_name,void,"SubGhzEnvironment*, const char*" Function,+,subghz_environment_set_protocol_registry,void,"SubGhzEnvironment*, void*" @@ -2679,7 +2680,7 @@ Function,+,subghz_protocol_blocks_crc8le,uint8_t,"const uint8_t[], size_t, uint8 Function,+,subghz_protocol_blocks_get_bit_array,_Bool,"uint8_t[], size_t" Function,+,subghz_protocol_blocks_get_hash_data,uint8_t,"SubGhzBlockDecoder*, size_t" Function,+,subghz_protocol_blocks_get_parity,uint8_t,"uint64_t, uint8_t" -Function,+,subghz_protocol_blocks_get_upload,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t" +Function,+,subghz_protocol_blocks_get_upload_from_bit_array,size_t,"uint8_t[], size_t, LevelDuration*, size_t, uint32_t, SubGhzProtocolBlockAlignBit" Function,+,subghz_protocol_blocks_lfsr_digest16,uint16_t,"const uint8_t[], size_t, uint16_t, uint16_t" Function,+,subghz_protocol_blocks_lfsr_digest8,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" Function,+,subghz_protocol_blocks_lfsr_digest8_reflect,uint8_t,"const uint8_t[], size_t, uint8_t, uint8_t" @@ -2688,6 +2689,14 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" +Function,+,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_alutech_at_4n_free,void,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_alutech_at_4n_reset,void,void* +Function,+,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" @@ -2757,6 +2766,14 @@ Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_doitrand_reset,void,void* Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_dooya_free,void,void* +Function,+,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_dooya_reset,void,void* +Function,+,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" @@ -2837,7 +2854,23 @@ Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_kia_reset,void,void* Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* +Function,+,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_linear_delta3_free,void,void* +Function,+,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_linear_delta3_reset,void,void* +Function,+,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" Function,-,subghz_protocol_decoder_linear_free,void,void* @@ -3032,6 +3065,11 @@ Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFor Function,-,subghz_protocol_encoder_doitrand_free,void,void* Function,-,subghz_protocol_encoder_doitrand_stop,void,void* Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_dooya_free,void,void* +Function,+,subghz_protocol_encoder_dooya_stop,void,void* +Function,+,subghz_protocol_encoder_dooya_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_faac_slh_free,void,void* @@ -3074,6 +3112,11 @@ Function,-,subghz_protocol_encoder_keeloq_free,void,void* Function,-,subghz_protocol_encoder_keeloq_stop,void,void* Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_linear_delta3_free,void,void* +Function,+,subghz_protocol_encoder_linear_delta3_stop,void,void* +Function,+,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_linear_free,void,void* Function,-,subghz_protocol_encoder_linear_stop,void,void* @@ -3148,6 +3191,16 @@ Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperForm Function,-,subghz_protocol_encoder_smc5326_free,void,void* Function,-,subghz_protocol_encoder_smc5326_stop,void,void* Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_somfy_keytis_free,void,void* +Function,+,subghz_protocol_encoder_somfy_keytis_stop,void,void* +Function,+,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_somfy_telis_free,void,void* +Function,+,subghz_protocol_encoder_somfy_telis_stop,void,void* +Function,+,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_star_line_free,void,void* @@ -3169,6 +3222,8 @@ Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const Su Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,+,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,+,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" @@ -4879,6 +4934,9 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,+,subghz_protocol_alutech_at_4n,const SubGhzProtocol, +Variable,+,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, @@ -4903,6 +4961,9 @@ Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_dooya,const SubGhzProtocol, +Variable,+,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, @@ -4933,8 +4994,14 @@ Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_kia,const SubGhzProtocol, Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, +Variable,+,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, +Variable,+,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_linear,const SubGhzProtocol, Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_linear_delta3,const SubGhzProtocol, +Variable,+,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, +Variable,+,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, Variable,-,subghz_protocol_magellan,const SubGhzProtocol, Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, diff --git a/firmware/targets/f7/fatfs/sd_spi_io.c b/firmware/targets/f7/fatfs/sd_spi_io.c new file mode 100644 index 000000000..93b837e85 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.c @@ -0,0 +1,877 @@ +#include "sd_spi_io.h" +#include "sector_cache.h" +#include +#include +#include + +// #define SD_SPI_DEBUG 1 +#define TAG "SdSpi" + +#ifdef SD_SPI_DEBUG +#define sd_spi_debug(...) FURI_LOG_I(TAG, __VA_ARGS__) +#else +#define sd_spi_debug(...) +#endif + +#define SD_CMD_LENGTH 6 +#define SD_DUMMY_BYTE 0xFF +#define SD_ANSWER_RETRY_COUNT 8 +#define SD_IDLE_RETRY_COUNT 100 +#define SD_BLOCK_SIZE 512 + +#define FLAG_SET(x, y) (((x) & (y)) == (y)) + +static bool sd_high_capacity = false; + +typedef enum { + SdSpiDataResponceOK = 0x05, + SdSpiDataResponceCRCError = 0x0B, + SdSpiDataResponceWriteError = 0x0D, + SdSpiDataResponceOtherError = 0xFF, +} SdSpiDataResponce; + +typedef struct { + uint8_t r1; + uint8_t r2; + uint8_t r3; + uint8_t r4; + uint8_t r5; +} SdSpiCmdAnswer; + +typedef enum { + SdSpiCmdAnswerTypeR1, + SdSpiCmdAnswerTypeR1B, + SdSpiCmdAnswerTypeR2, + SdSpiCmdAnswerTypeR3, + SdSpiCmdAnswerTypeR4R5, + SdSpiCmdAnswerTypeR7, +} SdSpiCmdAnswerType; + +/* + SdSpiCmd and SdSpiToken use non-standard enum value names convention, + because it is more convenient to look for documentation on a specific command. + For example, to find out what the SD_CMD23_SET_BLOCK_COUNT command does, you need to look for + SET_BLOCK_COUNT or CMD23 in the "Part 1 Physical Layer Simplified Specification". + + Do not use that naming convention in other places. +*/ + +typedef enum { + SD_CMD0_GO_IDLE_STATE = 0, + SD_CMD1_SEND_OP_COND = 1, + SD_CMD8_SEND_IF_COND = 8, + SD_CMD9_SEND_CSD = 9, + SD_CMD10_SEND_CID = 10, + SD_CMD12_STOP_TRANSMISSION = 12, + SD_CMD13_SEND_STATUS = 13, + SD_CMD16_SET_BLOCKLEN = 16, + SD_CMD17_READ_SINGLE_BLOCK = 17, + SD_CMD18_READ_MULT_BLOCK = 18, + SD_CMD23_SET_BLOCK_COUNT = 23, + SD_CMD24_WRITE_SINGLE_BLOCK = 24, + SD_CMD25_WRITE_MULT_BLOCK = 25, + SD_CMD27_PROG_CSD = 27, + SD_CMD28_SET_WRITE_PROT = 28, + SD_CMD29_CLR_WRITE_PROT = 29, + SD_CMD30_SEND_WRITE_PROT = 30, + SD_CMD32_SD_ERASE_GRP_START = 32, + SD_CMD33_SD_ERASE_GRP_END = 33, + SD_CMD34_UNTAG_SECTOR = 34, + SD_CMD35_ERASE_GRP_START = 35, + SD_CMD36_ERASE_GRP_END = 36, + SD_CMD37_UNTAG_ERASE_GROUP = 37, + SD_CMD38_ERASE = 38, + SD_CMD41_SD_APP_OP_COND = 41, + SD_CMD55_APP_CMD = 55, + SD_CMD58_READ_OCR = 58, +} SdSpiCmd; + +/** Data tokens */ +typedef enum { + SD_TOKEN_START_DATA_SINGLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_READ = 0xFE, + SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE = 0xFE, + SD_TOKEN_START_DATA_MULTIPLE_BLOCK_WRITE = 0xFC, + SD_TOKEN_STOP_DATA_MULTIPLE_BLOCK_WRITE = 0xFD, +} SdSpiToken; + +/** R1 answer value */ +typedef enum { + SdSpi_R1_NO_ERROR = 0x00, + SdSpi_R1_IN_IDLE_STATE = 0x01, + SdSpi_R1_ERASE_RESET = 0x02, + SdSpi_R1_ILLEGAL_COMMAND = 0x04, + SdSpi_R1_COM_CRC_ERROR = 0x08, + SdSpi_R1_ERASE_SEQUENCE_ERROR = 0x10, + SdSpi_R1_ADDRESS_ERROR = 0x20, + SdSpi_R1_PARAMETER_ERROR = 0x40, +} SdSpiR1; + +/** R2 answer value */ +typedef enum { + /* R2 answer value */ + SdSpi_R2_NO_ERROR = 0x00, + SdSpi_R2_CARD_LOCKED = 0x01, + SdSpi_R2_LOCKUNLOCK_ERROR = 0x02, + SdSpi_R2_ERROR = 0x04, + SdSpi_R2_CC_ERROR = 0x08, + SdSpi_R2_CARD_ECC_FAILED = 0x10, + SdSpi_R2_WP_VIOLATION = 0x20, + SdSpi_R2_ERASE_PARAM = 0x40, + SdSpi_R2_OUTOFRANGE = 0x80, +} SdSpiR2; + +static inline void sd_spi_select_card() { + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, false); + furi_delay_us(10); // Entry guard time for some SD cards +} + +static inline void sd_spi_deselect_card() { + furi_delay_us(10); // Exit guard time for some SD cards + furi_hal_gpio_write(furi_hal_sd_spi_handle->cs, true); +} + +static void sd_spi_bus_to_ground() { + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedVeryHigh, + GpioAltFnUnused); + + sd_spi_select_card(); + furi_hal_gpio_write(furi_hal_sd_spi_handle->miso, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->mosi, false); + furi_hal_gpio_write(furi_hal_sd_spi_handle->sck, false); +} + +static void sd_spi_bus_rise_up() { + sd_spi_deselect_card(); + + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->miso, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->mosi, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); + furi_hal_gpio_init_ex( + furi_hal_sd_spi_handle->sck, + GpioModeAltFunctionPushPull, + GpioPullUp, + GpioSpeedVeryHigh, + GpioAltFn5SPI2); +} + +static inline uint8_t sd_spi_read_byte(void) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_byte(uint8_t data) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, NULL, 1, SD_TIMEOUT_MS)); +} + +static inline uint8_t sd_spi_write_and_read_byte(uint8_t data) { + uint8_t responce; + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, &data, &responce, 1, SD_TIMEOUT_MS)); + return responce; +} + +static inline void sd_spi_write_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_read_bytes(uint8_t* data, uint32_t size) { + furi_check(furi_hal_spi_bus_trx(furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS)); +} + +static inline void sd_spi_write_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, data, NULL, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static inline void sd_spi_read_bytes_dma(uint8_t* data, uint32_t size) { + uint32_t timeout_mul = (size / 512) + 1; + furi_check(furi_hal_spi_bus_trx_dma( + furi_hal_sd_spi_handle, NULL, data, size, SD_TIMEOUT_MS * timeout_mul)); +} + +static uint8_t sd_spi_wait_for_data_and_read(void) { + uint8_t retry_count = SD_ANSWER_RETRY_COUNT; + uint8_t responce; + + // Wait until we get a valid data + do { + responce = sd_spi_read_byte(); + retry_count--; + + } while((responce == SD_DUMMY_BYTE) && retry_count); + + return responce; +} + +static SdSpiStatus sd_spi_wait_for_data(uint8_t data, uint32_t timeout_ms) { + FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_ms * 1000); + uint8_t byte; + + do { + byte = sd_spi_read_byte(); + if(furi_hal_cortex_timer_is_expired(timer)) { + return SdSpiStatusTimeout; + } + } while((byte != data)); + + return SdSpiStatusOK; +} + +static inline void sd_spi_deselect_card_and_purge() { + sd_spi_deselect_card(); + sd_spi_read_byte(); +} + +static inline void sd_spi_purge_crc() { + sd_spi_read_byte(); + sd_spi_read_byte(); +} + +static SdSpiCmdAnswer + sd_spi_send_cmd(SdSpiCmd cmd, uint32_t arg, uint8_t crc, SdSpiCmdAnswerType answer_type) { + uint8_t frame[SD_CMD_LENGTH]; + SdSpiCmdAnswer cmd_answer = { + .r1 = SD_DUMMY_BYTE, + .r2 = SD_DUMMY_BYTE, + .r3 = SD_DUMMY_BYTE, + .r4 = SD_DUMMY_BYTE, + .r5 = SD_DUMMY_BYTE, + }; + + // R1 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 1 Bytes answer + NEC(0) = 15bytes + // R1b identical to R1 + Busy information + // R2 Length = NCS(0)+ 6 Bytes command + NCR(min1 max8) + 2 Bytes answer + NEC(0) = 16bytes + + frame[0] = ((uint8_t)cmd | 0x40); + frame[1] = (uint8_t)(arg >> 24); + frame[2] = (uint8_t)(arg >> 16); + frame[3] = (uint8_t)(arg >> 8); + frame[4] = (uint8_t)(arg); + frame[5] = (crc | 0x01); + + sd_spi_select_card(); + sd_spi_write_bytes(frame, sizeof(frame)); + + switch(answer_type) { + case SdSpiCmdAnswerTypeR1: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + break; + case SdSpiCmdAnswerTypeR1B: + // TODO: can be wrong, at least for SD_CMD12_STOP_TRANSMISSION you need to purge one byte before reading R1 + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + + // In general this shenenigans seems suspicious, please double check SD specs if you are using SdSpiCmdAnswerTypeR1B + // reassert card + sd_spi_deselect_card(); + furi_delay_us(1000); + sd_spi_deselect_card(); + + // and wait for it to be ready + while(sd_spi_read_byte() != 0xFF) { + }; + + break; + case SdSpiCmdAnswerTypeR2: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + break; + case SdSpiCmdAnswerTypeR3: + case SdSpiCmdAnswerTypeR7: + cmd_answer.r1 = sd_spi_wait_for_data_and_read(); + cmd_answer.r2 = sd_spi_read_byte(); + cmd_answer.r3 = sd_spi_read_byte(); + cmd_answer.r4 = sd_spi_read_byte(); + cmd_answer.r5 = sd_spi_read_byte(); + break; + default: + break; + } + return cmd_answer; +} + +static SdSpiDataResponce sd_spi_get_data_response(uint32_t timeout_ms) { + SdSpiDataResponce responce = sd_spi_read_byte(); + // read busy response byte + sd_spi_read_byte(); + + switch(responce & 0x1F) { + case SdSpiDataResponceOK: + // TODO: check timings + sd_spi_deselect_card(); + sd_spi_select_card(); + + // wait for 0xFF + if(sd_spi_wait_for_data(0xFF, timeout_ms) == SdSpiStatusOK) { + return SdSpiDataResponceOK; + } else { + return SdSpiDataResponceOtherError; + } + case SdSpiDataResponceCRCError: + return SdSpiDataResponceCRCError; + case SdSpiDataResponceWriteError: + return SdSpiDataResponceWriteError; + default: + return SdSpiDataResponceOtherError; + } +} + +static SdSpiStatus sd_spi_init_spi_mode_v1(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v1"); + + do { + retry_count++; + + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + sd_spi_debug("Init SD card in SPI mode v1 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode_v2(void) { + SdSpiCmdAnswer response; + uint8_t retry_count = 0; + + sd_spi_debug("Init SD card in SPI mode v2"); + + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + // ACMD41 (APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0x40000000, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + sd_spi_debug("ACMD41 is illegal command"); + retry_count = 0; + do { + retry_count++; + // CMD55 (APP_CMD) before any ACMD command: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD55_APP_CMD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_IN_IDLE_STATE) { + sd_spi_debug("CMD55 failed"); + return SdSpiStatusError; + } + // ACMD41 (SD_APP_OP_COND) to initialize SDHC or SDXC cards: R1 response (0x00: no errors) + response = sd_spi_send_cmd(SD_CMD41_SD_APP_OP_COND, 0, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("ACMD41 failed"); + return SdSpiStatusError; + } + } while(response.r1 == SdSpi_R1_IN_IDLE_STATE); + } + + sd_spi_debug("Init SD card in SPI mode v2 done"); + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_init_spi_mode(void) { + SdSpiCmdAnswer response; + uint8_t retry_count; + + // CMD0 (GO_IDLE_STATE) to put SD in SPI mode and + // wait for In Idle State Response (R1 Format) equal to 0x01 + retry_count = 0; + do { + retry_count++; + response = sd_spi_send_cmd(SD_CMD0_GO_IDLE_STATE, 0, 0x95, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(retry_count >= SD_IDLE_RETRY_COUNT) { + sd_spi_debug("CMD0 failed"); + return SdSpiStatusError; + } + } while(response.r1 != SdSpi_R1_IN_IDLE_STATE); + + // CMD8 (SEND_IF_COND) to check the power supply status + // and wait until response (R7 Format) equal to 0xAA and + response = sd_spi_send_cmd(SD_CMD8_SEND_IF_COND, 0x1AA, 0x87, SdSpiCmdAnswerTypeR7); + sd_spi_deselect_card_and_purge(); + + if(FLAG_SET(response.r1, SdSpi_R1_ILLEGAL_COMMAND)) { + if(sd_spi_init_spi_mode_v1() != SdSpiStatusOK) { + sd_spi_debug("Init mode v1 failed"); + return SdSpiStatusError; + } + sd_high_capacity = 0; + } else if(response.r1 == SdSpi_R1_IN_IDLE_STATE) { + if(sd_spi_init_spi_mode_v2() != SdSpiStatusOK) { + sd_spi_debug("Init mode v2 failed"); + return SdSpiStatusError; + } + + // CMD58 (READ_OCR) to initialize SDHC or SDXC cards: R3 response + response = sd_spi_send_cmd(SD_CMD58_READ_OCR, 0, 0xFF, SdSpiCmdAnswerTypeR3); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_debug("CMD58 failed"); + return SdSpiStatusError; + } + sd_high_capacity = (response.r2 & 0x40) >> 6; + } else { + return SdSpiStatusError; + } + + sd_spi_debug("SD card is %s", sd_high_capacity ? "SDHC or SDXC" : "SDSC"); + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_get_csd(SD_CSD* csd) { + uint16_t counter = 0; + uint8_t csd_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD9 (SEND_CSD): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD9_SEND_CSD, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CSD data + for(counter = 0; counter < 16; counter++) { + csd_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + /************************************************************************* + CSD header decoding + *************************************************************************/ + + csd->CSDStruct = (csd_data[0] & 0xC0) >> 6; + csd->Reserved1 = csd_data[0] & 0x3F; + csd->TAAC = csd_data[1]; + csd->NSAC = csd_data[2]; + csd->MaxBusClkFrec = csd_data[3]; + csd->CardComdClasses = (csd_data[4] << 4) | ((csd_data[5] & 0xF0) >> 4); + csd->RdBlockLen = csd_data[5] & 0x0F; + csd->PartBlockRead = (csd_data[6] & 0x80) >> 7; + csd->WrBlockMisalign = (csd_data[6] & 0x40) >> 6; + csd->RdBlockMisalign = (csd_data[6] & 0x20) >> 5; + csd->DSRImpl = (csd_data[6] & 0x10) >> 4; + + /************************************************************************* + CSD v1/v2 decoding + *************************************************************************/ + + if(sd_high_capacity == 0) { + csd->version.v1.Reserved1 = ((csd_data[6] & 0x0C) >> 2); + csd->version.v1.DeviceSize = ((csd_data[6] & 0x03) << 10) | (csd_data[7] << 2) | + ((csd_data[8] & 0xC0) >> 6); + csd->version.v1.MaxRdCurrentVDDMin = (csd_data[8] & 0x38) >> 3; + csd->version.v1.MaxRdCurrentVDDMax = (csd_data[8] & 0x07); + csd->version.v1.MaxWrCurrentVDDMin = (csd_data[9] & 0xE0) >> 5; + csd->version.v1.MaxWrCurrentVDDMax = (csd_data[9] & 0x1C) >> 2; + csd->version.v1.DeviceSizeMul = ((csd_data[9] & 0x03) << 1) | + ((csd_data[10] & 0x80) >> 7); + } else { + csd->version.v2.Reserved1 = ((csd_data[6] & 0x0F) << 2) | + ((csd_data[7] & 0xC0) >> 6); + csd->version.v2.DeviceSize = ((csd_data[7] & 0x3F) << 16) | (csd_data[8] << 8) | + csd_data[9]; + csd->version.v2.Reserved2 = ((csd_data[10] & 0x80) >> 8); + } + + csd->EraseSingleBlockEnable = (csd_data[10] & 0x40) >> 6; + csd->EraseSectorSize = ((csd_data[10] & 0x3F) << 1) | ((csd_data[11] & 0x80) >> 7); + csd->WrProtectGrSize = (csd_data[11] & 0x7F); + csd->WrProtectGrEnable = (csd_data[12] & 0x80) >> 7; + csd->Reserved2 = (csd_data[12] & 0x60) >> 5; + csd->WrSpeedFact = (csd_data[12] & 0x1C) >> 2; + csd->MaxWrBlockLen = ((csd_data[12] & 0x03) << 2) | ((csd_data[13] & 0xC0) >> 6); + csd->WriteBlockPartial = (csd_data[13] & 0x20) >> 5; + csd->Reserved3 = (csd_data[13] & 0x1F); + csd->FileFormatGrouop = (csd_data[14] & 0x80) >> 7; + csd->CopyFlag = (csd_data[14] & 0x40) >> 6; + csd->PermWrProtect = (csd_data[14] & 0x20) >> 5; + csd->TempWrProtect = (csd_data[14] & 0x10) >> 4; + csd->FileFormat = (csd_data[14] & 0x0C) >> 2; + csd->Reserved4 = (csd_data[14] & 0x03); + csd->crc = (csd_data[15] & 0xFE) >> 1; + csd->Reserved5 = (csd_data[15] & 0x01); + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static SdSpiStatus sd_spi_get_cid(SD_CID* Cid) { + uint16_t counter = 0; + uint8_t cid_data[16]; + SdSpiStatus ret = SdSpiStatusError; + SdSpiCmdAnswer response; + + // CMD10 (SEND_CID): R1 format (0x00 is no errors) + response = sd_spi_send_cmd(SD_CMD10_SEND_CID, 0, 0xFF, SdSpiCmdAnswerTypeR1); + + if(response.r1 == SdSpi_R1_NO_ERROR) { + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, SD_TIMEOUT_MS) == + SdSpiStatusOK) { + // read CID data + for(counter = 0; counter < 16; counter++) { + cid_data[counter] = sd_spi_read_byte(); + } + + sd_spi_purge_crc(); + + Cid->ManufacturerID = cid_data[0]; + memcpy(Cid->OEM_AppliID, cid_data + 1, 2); + memcpy(Cid->ProdName, cid_data + 3, 5); + Cid->ProdRev = cid_data[8]; + Cid->ProdSN = cid_data[9] << 24; + Cid->ProdSN |= cid_data[10] << 16; + Cid->ProdSN |= cid_data[11] << 8; + Cid->ProdSN |= cid_data[12]; + Cid->Reserved1 = (cid_data[13] & 0xF0) >> 4; + Cid->ManufactYear = (cid_data[13] & 0x0F) << 4; + Cid->CID_CRC = (cid_data[15] & 0xFE) >> 1; + Cid->Reserved2 = 1; + + ret = SdSpiStatusOK; + } + } + + sd_spi_deselect_card_and_purge(); + + return ret; +} + +static inline bool sd_cache_get(uint32_t address, uint32_t* data) { + uint8_t* cached_data = sector_cache_get(address); + if(cached_data) { + memcpy(data, cached_data, SD_BLOCK_SIZE); + return true; + } + return false; +} + +static inline void sd_cache_put(uint32_t address, uint32_t* data) { + sector_cache_put(address, (uint8_t*)data); +} + +static inline void sd_cache_invalidate_range(uint32_t start_sector, uint32_t end_sector) { + sector_cache_invalidate_range(start_sector, end_sector); +} + +static SdSpiStatus + sd_spi_cmd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD17 (READ_SINGLE_BLOCK): R1 response (0x00: no errors) + response = + sd_spi_send_cmd(SD_CMD17_READ_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Wait for the data start token + if(sd_spi_wait_for_data(SD_TOKEN_START_DATA_SINGLE_BLOCK_READ, timeout_ms) == + SdSpiStatusOK) { + // Read the data block + sd_spi_read_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } else { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + sd_spi_deselect_card_and_purge(); + } + + return SdSpiStatusOK; +} + +static SdSpiStatus sd_spi_cmd_write_blocks( + uint32_t* data, + uint32_t address, + uint32_t blocks, + uint32_t timeout_ms) { + uint32_t block_address = address; + uint32_t offset = 0; + + // CMD16 (SET_BLOCKLEN): R1 response (0x00: no errors) + SdSpiCmdAnswer response = + sd_spi_send_cmd(SD_CMD16_SET_BLOCKLEN, SD_BLOCK_SIZE, 0xFF, SdSpiCmdAnswerTypeR1); + sd_spi_deselect_card_and_purge(); + + if(response.r1 != SdSpi_R1_NO_ERROR) { + return SdSpiStatusError; + } + + if(!sd_high_capacity) { + block_address = address * SD_BLOCK_SIZE; + } + + while(blocks--) { + // CMD24 (WRITE_SINGLE_BLOCK): R1 response (0x00: no errors) + response = sd_spi_send_cmd( + SD_CMD24_WRITE_SINGLE_BLOCK, block_address, 0xFF, SdSpiCmdAnswerTypeR1); + if(response.r1 != SdSpi_R1_NO_ERROR) { + sd_spi_deselect_card_and_purge(); + return SdSpiStatusError; + } + + // Send dummy byte for NWR timing : one byte between CMD_WRITE and TOKEN + // TODO: check bytes count + sd_spi_write_byte(SD_DUMMY_BYTE); + sd_spi_write_byte(SD_DUMMY_BYTE); + + // Send the data start token + sd_spi_write_byte(SD_TOKEN_START_DATA_SINGLE_BLOCK_WRITE); + sd_spi_write_bytes_dma((uint8_t*)data + offset, SD_BLOCK_SIZE); + sd_spi_purge_crc(); + + // Read data response + SdSpiDataResponce data_responce = sd_spi_get_data_response(timeout_ms); + sd_spi_deselect_card_and_purge(); + + if(data_responce != SdSpiDataResponceOK) { + return SdSpiStatusError; + } + + // increase offset + offset += SD_BLOCK_SIZE; + + // increase block address + if(sd_high_capacity) { + block_address += 1; + } else { + block_address += SD_BLOCK_SIZE; + } + } + + return SdSpiStatusOK; +} + +uint8_t sd_max_mount_retry_count() { + return 10; +} + +SdSpiStatus sd_init(bool power_reset) { + // Slow speed init + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_slow); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_slow; + + // We reset card in spi_lock context, so it is safe to disturb spi bus + if(power_reset) { + sd_spi_debug("Power reset"); + + // disable power and set low on all bus pins + furi_hal_power_disable_external_3_3v(); + sd_spi_bus_to_ground(); + hal_sd_detect_set_low(); + furi_delay_ms(250); + + // reinit bus and enable power + sd_spi_bus_rise_up(); + hal_sd_detect_init(); + furi_hal_power_enable_external_3_3v(); + furi_delay_ms(100); + } + + SdSpiStatus status = SdSpiStatusError; + + // Send 80 dummy clocks with CS high + sd_spi_deselect_card(); + for(uint8_t i = 0; i < 80; i++) { + sd_spi_write_byte(SD_DUMMY_BYTE); + } + + for(uint8_t i = 0; i < 128; i++) { + status = sd_spi_init_spi_mode(); + if(status == SdSpiStatusOK) { + // SD initialized and init to SPI mode properly + sd_spi_debug("SD init OK after %d retries", i); + break; + } + } + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_slow); + + // Init sector cache + sector_cache_init(); + + return status; +} + +SdSpiStatus sd_get_card_state(void) { + SdSpiCmdAnswer response; + + // Send CMD13 (SEND_STATUS) to get SD status + response = sd_spi_send_cmd(SD_CMD13_SEND_STATUS, 0, 0xFF, SdSpiCmdAnswerTypeR2); + sd_spi_deselect_card_and_purge(); + + // Return status OK if response is valid + if((response.r1 == SdSpi_R1_NO_ERROR) && (response.r2 == SdSpi_R2_NO_ERROR)) { + return SdSpiStatusOK; + } + + return SdSpiStatusError; +} + +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info) { + SdSpiStatus status; + + status = sd_spi_get_csd(&(card_info->Csd)); + + if(status != SdSpiStatusOK) { + return status; + } + + status = sd_spi_get_cid(&(card_info->Cid)); + + if(status != SdSpiStatusOK) { + return status; + } + + if(sd_high_capacity == 1) { + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 512; + card_info->CardCapacity = ((uint64_t)card_info->Csd.version.v2.DeviceSize + 1UL) * 1024UL * + (uint64_t)card_info->LogBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } else { + card_info->CardCapacity = (card_info->Csd.version.v1.DeviceSize + 1); + card_info->CardCapacity *= (1UL << (card_info->Csd.version.v1.DeviceSizeMul + 2)); + card_info->LogBlockSize = 512; + card_info->CardBlockSize = 1UL << (card_info->Csd.RdBlockLen); + card_info->CardCapacity *= card_info->CardBlockSize; + card_info->LogBlockNbr = (card_info->CardCapacity) / (card_info->LogBlockSize); + } + + return status; +} + +SdSpiStatus + sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + SdSpiStatus status = SdSpiStatusError; + + bool single_sector_read = (blocks == 1); + + if(single_sector_read) { + if(sd_cache_get(address, data)) { + return SdSpiStatusOK; + } + + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + + if(status == SdSpiStatusOK) { + sd_cache_put(address, data); + } + } else { + status = sd_spi_cmd_read_blocks(data, address, blocks, timeout_ms); + } + + return status; +} + +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms) { + sd_cache_invalidate_range(address, address + blocks); + SdSpiStatus status = sd_spi_cmd_write_blocks(data, address, blocks, timeout_ms); + return status; +} + +SdSpiStatus sd_get_cid(SD_CID* cid) { + SdSpiStatus status; + + furi_hal_spi_acquire(&furi_hal_spi_bus_handle_sd_fast); + furi_hal_sd_spi_handle = &furi_hal_spi_bus_handle_sd_fast; + + memset(cid, 0, sizeof(SD_CID)); + status = sd_spi_get_cid(cid); + + furi_hal_sd_spi_handle = NULL; + furi_hal_spi_release(&furi_hal_spi_bus_handle_sd_fast); + + return status; +} \ No newline at end of file diff --git a/firmware/targets/f7/fatfs/sd_spi_io.h b/firmware/targets/f7/fatfs/sd_spi_io.h new file mode 100644 index 000000000..8850eceb7 --- /dev/null +++ b/firmware/targets/f7/fatfs/sd_spi_io.h @@ -0,0 +1,157 @@ +#pragma once +#include +#include + +#define __IO volatile + +#define SD_TIMEOUT_MS (1000) + +typedef enum { + SdSpiStatusOK, + SdSpiStatusError, + SdSpiStatusTimeout, +} SdSpiStatus; + +/** + * @brief Card Specific Data: CSD Register + */ +typedef struct { + /* Header part */ + uint8_t CSDStruct : 2; /* CSD structure */ + uint8_t Reserved1 : 6; /* Reserved */ + uint8_t TAAC : 8; /* Data read access-time 1 */ + uint8_t NSAC : 8; /* Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec : 8; /* Max. bus clock frequency */ + uint16_t CardComdClasses : 12; /* Card command classes */ + uint8_t RdBlockLen : 4; /* Max. read data block length */ + uint8_t PartBlockRead : 1; /* Partial blocks for read allowed */ + uint8_t WrBlockMisalign : 1; /* Write block misalignment */ + uint8_t RdBlockMisalign : 1; /* Read block misalignment */ + uint8_t DSRImpl : 1; /* DSR implemented */ + + /* v1 or v2 struct */ + union csd_version { + struct { + uint8_t Reserved1 : 2; /* Reserved */ + uint16_t DeviceSize : 12; /* Device Size */ + uint8_t MaxRdCurrentVDDMin : 3; /* Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax : 3; /* Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin : 3; /* Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax : 3; /* Max. write current @ VDD max */ + uint8_t DeviceSizeMul : 3; /* Device size multiplier */ + } v1; + struct { + uint8_t Reserved1 : 6; /* Reserved */ + uint32_t DeviceSize : 22; /* Device Size */ + uint8_t Reserved2 : 1; /* Reserved */ + } v2; + } version; + + uint8_t EraseSingleBlockEnable : 1; /* Erase single block enable */ + uint8_t EraseSectorSize : 7; /* Erase group size multiplier */ + uint8_t WrProtectGrSize : 7; /* Write protect group size */ + uint8_t WrProtectGrEnable : 1; /* Write protect group enable */ + uint8_t Reserved2 : 2; /* Reserved */ + uint8_t WrSpeedFact : 3; /* Write speed factor */ + uint8_t MaxWrBlockLen : 4; /* Max. write data block length */ + uint8_t WriteBlockPartial : 1; /* Partial blocks for write allowed */ + uint8_t Reserved3 : 5; /* Reserved */ + uint8_t FileFormatGrouop : 1; /* File format group */ + uint8_t CopyFlag : 1; /* Copy flag (OTP) */ + uint8_t PermWrProtect : 1; /* Permanent write protection */ + uint8_t TempWrProtect : 1; /* Temporary write protection */ + uint8_t FileFormat : 2; /* File Format */ + uint8_t Reserved4 : 2; /* Reserved */ + uint8_t crc : 7; /* Reserved */ + uint8_t Reserved5 : 1; /* always 1*/ + +} SD_CSD; + +/** + * @brief Card Identification Data: CID Register + */ +typedef struct { + uint8_t ManufacturerID; /* ManufacturerID */ + char OEM_AppliID[2]; /* OEM/Application ID */ + char ProdName[5]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + uint32_t ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint8_t ManufactYear; /* Manufacturing Year */ + uint8_t ManufactMonth; /* Manufacturing Month */ + uint8_t CID_CRC; /* CID CRC */ + uint8_t Reserved2; /* always 1 */ +} SD_CID; + +/** + * @brief SD Card information structure + */ +typedef struct { + SD_CSD Csd; + SD_CID Cid; + uint64_t CardCapacity; /*!< Card Capacity */ + uint32_t CardBlockSize; /*!< Card Block Size */ + uint32_t LogBlockNbr; /*!< Specifies the Card logical Capacity in blocks */ + uint32_t LogBlockSize; /*!< Specifies logical block size in bytes */ +} SD_CardInfo; + +/** + * @brief SD card max mount retry count + * + * @return uint8_t + */ +uint8_t sd_max_mount_retry_count(); + +/** + * @brief Init sd card + * + * @param power_reset reset card power + * @return SdSpiStatus + */ +SdSpiStatus sd_init(bool power_reset); + +/** + * @brief Get card state + * + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_state(void); + +/** + * @brief Get card info + * + * @param card_info + * @return SdSpiStatus + */ +SdSpiStatus sd_get_card_info(SD_CardInfo* card_info); + +/** + * @brief Read blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus sd_read_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Write blocks + * + * @param data + * @param address + * @param blocks + * @param timeout_ms + * @return SdSpiStatus + */ +SdSpiStatus + sd_write_blocks(uint32_t* data, uint32_t address, uint32_t blocks, uint32_t timeout_ms); + +/** + * @brief Get card CSD register + * + * @param Cid + * @return SdSpiStatus + */ +SdSpiStatus sd_get_cid(SD_CID* cid); \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi.c b/firmware/targets/f7/furi_hal/furi_hal_spi.c index 2f9d87080..42b854799 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi.c @@ -1,14 +1,33 @@ +#include #include #include #include +#include -#include -#include - +#include #include #include #include +#define TAG "FuriHalSpi" + +#define SPI_DMA DMA2 +#define SPI_DMA_RX_CHANNEL LL_DMA_CHANNEL_3 +#define SPI_DMA_TX_CHANNEL LL_DMA_CHANNEL_4 +#define SPI_DMA_RX_IRQ FuriHalInterruptIdDma2Ch3 +#define SPI_DMA_TX_IRQ FuriHalInterruptIdDma2Ch4 +#define SPI_DMA_RX_DEF SPI_DMA, SPI_DMA_RX_CHANNEL +#define SPI_DMA_TX_DEF SPI_DMA, SPI_DMA_TX_CHANNEL + +// For simplicity, I assume that only one SPI DMA transaction can occur at a time. +static FuriSemaphore* spi_dma_lock = NULL; +static FuriSemaphore* spi_dma_completed = NULL; + +void furi_hal_spi_dma_init() { + spi_dma_lock = furi_semaphore_alloc(1, 1); + spi_dma_completed = furi_semaphore_alloc(1, 1); +} + void furi_hal_spi_bus_init(FuriHalSpiBus* bus) { furi_assert(bus); bus->callback(bus, FuriHalSpiBusEventInit); @@ -84,7 +103,7 @@ bool furi_hal_spi_bus_rx( bool furi_hal_spi_bus_tx( FuriHalSpiBusHandle* handle, - uint8_t* buffer, + const uint8_t* buffer, size_t size, uint32_t timeout) { furi_assert(handle); @@ -109,7 +128,7 @@ bool furi_hal_spi_bus_tx( bool furi_hal_spi_bus_trx( FuriHalSpiBusHandle* handle, - uint8_t* tx_buffer, + const uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) { @@ -149,3 +168,209 @@ bool furi_hal_spi_bus_trx( return ret; } + +static void spi_dma_isr() { +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + if(LL_DMA_IsActiveFlag_TC3(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_RX_DEF)) { + LL_DMA_ClearFlag_TC3(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + if(LL_DMA_IsActiveFlag_TC4(SPI_DMA) && LL_DMA_IsEnabledIT_TC(SPI_DMA_TX_DEF)) { + LL_DMA_ClearFlag_TC4(SPI_DMA); + furi_check(furi_semaphore_release(spi_dma_completed) == FuriStatusOk); + } +#else +#error Update this code. Would you kindly? +#endif +} + +bool furi_hal_spi_bus_trx_dma( + FuriHalSpiBusHandle* handle, + uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout_ms) { + furi_assert(handle); + furi_assert(handle->bus->current_handle == handle); + furi_assert(size > 0); + + // If scheduler is not running, use blocking mode + if(xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + return furi_hal_spi_bus_trx(handle, tx_buffer, rx_buffer, size, timeout_ms); + } + + // Lock DMA + furi_check(furi_semaphore_acquire(spi_dma_lock, FuriWaitForever) == FuriStatusOk); + + const uint32_t dma_dummy_u32 = 0xFFFFFFFF; + + bool ret = true; + SPI_TypeDef* spi = handle->bus->spi; + uint32_t dma_rx_req; + uint32_t dma_tx_req; + + if(spi == SPI1) { + dma_rx_req = LL_DMAMUX_REQ_SPI1_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI1_TX; + } else if(spi == SPI2) { + dma_rx_req = LL_DMAMUX_REQ_SPI2_RX; + dma_tx_req = LL_DMAMUX_REQ_SPI2_TX; + } else { + furi_crash(NULL); + } + + if(rx_buffer == NULL) { + // Only TX mode, do not use RX channel + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + +#if SPI_DMA_TX_CHANNEL == LL_DMA_CHANNEL_4 + LL_DMA_ClearFlag_TC4(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + furi_hal_interrupt_set_isr(SPI_DMA_TX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + } else { + // TRX or RX mode, use both channels + uint32_t tx_mem_increase_mode; + + if(tx_buffer == NULL) { + // RX mode, use dummy data instead of TX buffer + tx_buffer = (uint8_t*)&dma_dummy_u32; + tx_mem_increase_mode = LL_DMA_PERIPH_NOINCREMENT; + } else { + tx_mem_increase_mode = LL_DMA_MEMORY_INCREMENT; + } + + LL_DMA_InitTypeDef dma_config = {0}; + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)tx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = tx_mem_increase_mode; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_tx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_TX_DEF, &dma_config); + + dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (spi->DR); + dma_config.MemoryOrM2MDstAddress = (uint32_t)rx_buffer; + dma_config.Direction = LL_DMA_DIRECTION_PERIPH_TO_MEMORY; + dma_config.Mode = LL_DMA_MODE_NORMAL; + dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT; + dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT; + dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_BYTE; + dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE; + dma_config.NbData = size; + dma_config.PeriphRequest = dma_rx_req; + dma_config.Priority = LL_DMA_PRIORITY_MEDIUM; + LL_DMA_Init(SPI_DMA_RX_DEF, &dma_config); + +#if SPI_DMA_RX_CHANNEL == LL_DMA_CHANNEL_3 + LL_DMA_ClearFlag_TC3(SPI_DMA); +#else +#error Update this code. Would you kindly? +#endif + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, spi_dma_isr, NULL); + + bool dma_tx_was_enabled = LL_SPI_IsEnabledDMAReq_TX(spi); + bool dma_rx_was_enabled = LL_SPI_IsEnabledDMAReq_RX(spi); + + if(!dma_tx_was_enabled) { + LL_SPI_EnableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_EnableDMAReq_RX(spi); + } + + // acquire semaphore before enabling DMA + furi_check(furi_semaphore_acquire(spi_dma_completed, timeout_ms) == FuriStatusOk); + + LL_DMA_EnableIT_TC(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_RX_DEF); + LL_DMA_EnableChannel(SPI_DMA_TX_DEF); + + // and wait for it to be released (DMA transfer complete) + if(furi_semaphore_acquire(spi_dma_completed, timeout_ms) != FuriStatusOk) { + ret = false; + FURI_LOG_E(TAG, "DMA timeout\r\n"); + } + // release semaphore, because we are using it as a flag + furi_semaphore_release(spi_dma_completed); + + LL_DMA_DisableIT_TC(SPI_DMA_RX_DEF); + + LL_DMA_DisableChannel(SPI_DMA_TX_DEF); + LL_DMA_DisableChannel(SPI_DMA_RX_DEF); + + if(!dma_tx_was_enabled) { + LL_SPI_DisableDMAReq_TX(spi); + } + + if(!dma_rx_was_enabled) { + LL_SPI_DisableDMAReq_RX(spi); + } + + furi_hal_interrupt_set_isr(SPI_DMA_RX_IRQ, NULL, NULL); + + LL_DMA_DeInit(SPI_DMA_TX_DEF); + LL_DMA_DeInit(SPI_DMA_RX_DEF); + } + + furi_hal_spi_bus_end_txrx(handle, timeout_ms); + + furi_check(furi_semaphore_release(spi_dma_lock) == FuriStatusOk); + + return ret; +} \ No newline at end of file diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index ab00ef0d7..9c54befb4 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -71,38 +71,23 @@ bool furi_hal_spi_bus_rx( size_t size, uint32_t timeout); -/** SPI Transmit - * - * @param handle pointer to FuriHalSpiBusHandle instance - * @param buffer transmit buffer - * @param size transaction size (buffer size) - * @param timeout operation timeout in ms - * - * @return true on success - */ -bool furi_hal_spi_bus_tx( - FuriHalSpiBusHandle* handle, - uint8_t* buffer, - size_t size, - uint32_t timeout); - -/** SPI Transmit and Receive +/** SPI Transmit and Receive with DMA * * @param handle pointer to FuriHalSpiBusHandle instance * @param tx_buffer pointer to tx buffer * @param rx_buffer pointer to rx buffer * @param size transaction size (buffer size) - * @param timeout operation timeout in ms + * @param timeout_ms operation timeout in ms * * @return true on success */ -bool furi_hal_spi_bus_trx( +bool furi_hal_spi_bus_trx_dma( FuriHalSpiBusHandle* handle, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, - uint32_t timeout); + uint32_t timeout_ms); #ifdef __cplusplus } -#endif +#endif \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index f3349b5fc..e325be21c 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -2,6 +2,8 @@ #include "math.h" #include +#include "furi.h" + #define TAG "SubGhzBlockEncoder" void subghz_protocol_blocks_set_bit_array( @@ -17,21 +19,32 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); } -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit) { - size_t index_bit = 0; + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; size_t size_upload = 0; uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1; i < count_bit_data_array; i++) { + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { duration += duration_bit; } else { - furi_assert(max_size_upload > size_upload); + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); last_bit = !last_bit; @@ -42,4 +55,4 @@ size_t subghz_protocol_blocks_get_upload( upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); return size_upload; -} +} \ No newline at end of file diff --git a/lib/subghz/blocks/encoder.h b/lib/subghz/blocks/encoder.h index 1ff077726..aeaa2add0 100644 --- a/lib/subghz/blocks/encoder.h +++ b/lib/subghz/blocks/encoder.h @@ -19,6 +19,11 @@ typedef struct { } SubGhzProtocolBlockEncoder; +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + /** * Set data bit when encoding HEX array. * @param bit_value The value of the bit to be set @@ -47,13 +52,15 @@ bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_inde * @param upload Pointer to a LevelDuration * @param max_size_upload upload size, check not to overflow * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array */ -size_t subghz_protocol_blocks_get_upload( +size_t subghz_protocol_blocks_get_upload_from_bit_array( uint8_t data_array[], size_t count_bit_data_array, LevelDuration* upload, size_t max_size_upload, - uint32_t duration_bit); + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); #ifdef __cplusplus } diff --git a/lib/subghz/environment.c b/lib/subghz/environment.c index 0a4b7b606..b39b259d4 100644 --- a/lib/subghz/environment.c +++ b/lib/subghz/environment.c @@ -6,6 +6,7 @@ struct SubGhzEnvironment { const SubGhzProtocolRegistry* protocol_registry; const char* came_atomo_rainbow_table_file_name; const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; }; SubGhzEnvironment* subghz_environment_alloc() { @@ -57,6 +58,21 @@ const char* return instance->came_atomo_rainbow_table_file_name; } +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + void subghz_environment_set_nice_flor_s_rainbow_table_file_name( SubGhzEnvironment* instance, const char* filename) { diff --git a/lib/subghz/environment.h b/lib/subghz/environment.h index e994f7c97..7bd38ba2f 100644 --- a/lib/subghz/environment.h +++ b/lib/subghz/environment.h @@ -52,6 +52,23 @@ void subghz_environment_set_came_atomo_rainbow_table_file_name( */ const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + /** * Set filename to work with Nice Flor-S. * @param instance Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..3fef76af2 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeStatic, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* 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_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} \ No newline at end of file diff --git a/lib/subghz/protocols/bin_raw.h b/lib/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/lib/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/chamberlain_code.c b/lib/subghz/protocols/chamberlain_code.c index 32f4e9520..9c8e5ee4a 100644 --- a/lib/subghz/protocols/chamberlain_code.c +++ b/lib/subghz/protocols/chamberlain_code.c @@ -196,12 +196,13 @@ static bool break; } - instance->encoder.size_upload = subghz_protocol_blocks_get_upload( + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( upload_hex_data, upload_hex_count_bit, instance->encoder.upload, instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short); + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); return true; } diff --git a/lib/subghz/protocols/dooya.c b/lib/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/lib/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* 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_dooya_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); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* 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_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_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); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/lib/subghz/protocols/dooya.h b/lib/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/lib/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index aaf573a57..a0970de4d 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -520,11 +520,14 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur subghz_protocol_keeloq_const.te_delta)) { // Found end TX instance->decoder.parser_step = KeeloqDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) { + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { if(instance->generic.data != instance->decoder.decode_data) { instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -541,6 +544,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else if( @@ -551,6 +556,8 @@ void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t dur if(instance->decoder.decode_count_bit < subghz_protocol_keeloq_const.min_count_bit_for_found) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; } instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; } else { diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..2c8de0d2d --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,336 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->data; + instance->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 = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + uint64_t data, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->data, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->data, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..c9f1cf380 --- /dev/null +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/linear_delta3.c b/lib/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* 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; + } + + //Send key data + 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_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_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); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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 = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_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_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/lib/subghz/protocols/linear_delta3.h b/lib/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/lib/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 5ca95901d..6447676cc 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -14,6 +14,9 @@ #define TAG "SubGhzProtocolNiceFlorS" +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { .te_short = 500, .te_long = 1000, @@ -28,6 +31,7 @@ struct SubGhzProtocolDecoderNiceFlorS { SubGhzBlockGeneric generic; const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; }; struct SubGhzProtocolEncoderNiceFlorS { @@ -243,6 +247,64 @@ LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { return ret; } +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -427,10 +489,14 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ subghz_protocol_nice_flor_s_const.te_delta) { //Found STOP bit instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) instance->base.callback(&instance->base, instance->base.context); } @@ -464,6 +530,11 @@ void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_ } else { instance->decoder.parser_step = NiceFlorSDecoderStepReset; } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } break; } } @@ -477,6 +548,7 @@ static void subghz_protocol_nice_flor_s_remote_controller( SubGhzBlockGeneric* instance, const char* file_name) { /* + * Protocol Nice Flor-S * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; * P1 (4-bit) - batch repetition number, calculated by the formula: @@ -497,6 +569,24 @@ static void subghz_protocol_nice_flor_s_remote_controller( * data => 0x1c5783607f7b3 key serial cnt * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * */ if(!file_name) { instance->cnt = 0; @@ -523,7 +613,15 @@ bool subghz_protocol_decoder_nice_flor_s_serialize( SubGhzRadioPreset* preset) { furi_assert(context); SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; } bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { @@ -534,11 +632,25 @@ bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperForma if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { break; } - if(instance->generic.data_count_bit != - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { FURI_LOG_E(TAG, "Wrong number of bits in key"); break; } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + ret = true; } while(false); return ret; @@ -550,20 +662,33 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } } diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 5f733d6bd..96717e56f 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -1,21 +1,50 @@ #include "protocol_items.h" const SubGhzProtocol* subghz_protocol_registry_items[] = { - &subghz_protocol_gate_tx, &subghz_protocol_keeloq, &subghz_protocol_star_line, - &subghz_protocol_nice_flo, &subghz_protocol_came, &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, &subghz_protocol_came_twee, &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, &subghz_protocol_ido, &subghz_protocol_kia, - &subghz_protocol_hormann, &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_princeton, - &subghz_protocol_raw, &subghz_protocol_linear, &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, &subghz_protocol_megacode, &subghz_protocol_holtek, - &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_magellan, &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, &subghz_protocol_ansonic, &subghz_protocol_pocsag, - &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &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_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_pocsag, + &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, }; const SubGhzProtocolRegistry subghz_protocol_registry = { .items = subghz_protocol_registry_items, - .size = COUNT_OF(subghz_protocol_registry_items)}; \ No newline at end of file + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 55a1ea860..362b0459a 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -21,6 +21,7 @@ #include "gate_tx.h" #include "raw.h" #include "linear.h" +#include "linear_delta3.h" #include "secplus_v2.h" #include "secplus_v1.h" #include "megacode.h" @@ -39,6 +40,9 @@ #include "pocsag.h" #include "smc5326.h" #include "holtek_ht12x.h" +#include "dooya.h" +#include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" #ifdef __cplusplus extern "C" { diff --git a/lib/subghz/protocols/secplus_v1.c b/lib/subghz/protocols/secplus_v1.c index 9c3afeb46..3ef95db36 100644 --- a/lib/subghz/protocols/secplus_v1.c +++ b/lib/subghz/protocols/secplus_v1.c @@ -606,7 +606,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03lX\r\n" + "Cnt:0x%03lX " "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, @@ -625,7 +625,7 @@ void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* ou furi_string_cat_printf( output, "Sn:0x%08lX\r\n" - "Cnt:0x%03lX\r\n" + "Cnt:0x%03lX " "Sw_id:0x%X\r\n", instance->generic.serial, instance->generic.cnt, diff --git a/lib/subghz/protocols/somfy_keytis.c b/lib/subghz/protocols/somfy_keytis.c index 63a3e26d2..ab9202cc3 100644 --- a/lib/subghz/protocols/somfy_keytis.c +++ b/lib/subghz/protocols/somfy_keytis.c @@ -55,25 +55,40 @@ const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = { .get_string = subghz_protocol_decoder_somfy_keytis_get_string, }; -const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - const SubGhzProtocol subghz_protocol_somfy_keytis = { .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_somfy_keytis_decoder, .encoder = &subghz_protocol_somfy_keytis_encoder, }; +const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { + .alloc = subghz_protocol_encoder_somfy_keytis_alloc, + .free = subghz_protocol_encoder_somfy_keytis_free, + + .deserialize = subghz_protocol_encoder_somfy_keytis_deserialize, + .stop = subghz_protocol_encoder_somfy_keytis_stop, + .yield = subghz_protocol_encoder_somfy_keytis_yield, +}; + +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis)); + + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis)); @@ -83,6 +98,13 @@ void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) return instance; } +void subghz_protocol_encoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + free(instance->encoder.upload); + free(instance); +} + void subghz_protocol_decoder_somfy_keytis_free(void* context) { furi_assert(context); SubGhzProtocolDecoderSomfyKeytis* instance = context; @@ -100,6 +122,330 @@ void subghz_protocol_decoder_somfy_keytis_reset(void* context) { NULL); } +static bool + subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 48) & 0xF; + instance->generic.cnt = (data >> 24) & 0xFFFF; + instance->generic.serial = data & 0xFFFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[10]; + frame[0] = (0xA << 4) | instance->generic.btn; + frame[1] = 0xF << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + frame[7] = 0xC4; + frame[8] = 0x00; + frame[9] = 0x19; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + data = 0; + for(uint8_t i = 7; i < 10; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data_2 = data; + return true; +} + +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 80; + bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_keytis_get_upload( + SubGhzProtocolEncoderSomfyKeytis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + + for(uint8_t i = 0; i < 2; ++i) { + //Hardware sync + for(uint8_t i = 0; i < 6; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + } + + //Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + + size_t size_upload = index; + + 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; + } + return true; +} + +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_keytis_stop(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) { + SubGhzProtocolEncoderSomfyKeytis* 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; +} + /** * Сhecksum calculation. * @param data Вata for checksum calculation diff --git a/lib/subghz/protocols/somfy_keytis.h b/lib/subghz/protocols/somfy_keytis.h index 3b5950611..b08da3641 100644 --- a/lib/subghz/protocols/somfy_keytis.h +++ b/lib/subghz/protocols/somfy_keytis.h @@ -11,6 +11,58 @@ extern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder; extern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder; extern const SubGhzProtocol subghz_protocol_somfy_keytis; +/** + * Allocate SubGhzProtocolEncoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyKeytis* pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context); + /** * Allocate SubGhzProtocolDecoderSomfyKeytis. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/somfy_telis.c b/lib/subghz/protocols/somfy_telis.c index 945a88405..96997c581 100644 --- a/lib/subghz/protocols/somfy_telis.c +++ b/lib/subghz/protocols/somfy_telis.c @@ -55,24 +55,301 @@ const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_somfy_telis_alloc, + .free = subghz_protocol_encoder_somfy_telis_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_somfy_telis_deserialize, + .stop = subghz_protocol_encoder_somfy_telis_stop, + .yield = subghz_protocol_encoder_somfy_telis_yield, }; const SubGhzProtocol subghz_protocol_somfy_telis = { .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_somfy_telis_decoder, .encoder = &subghz_protocol_somfy_telis_encoder, }; +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); + + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool + subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 44) & 0xF; // ctrl + instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code + instance->generic.serial = data & 0xFFFFFF; // address + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[7]; + frame[0] = data >> 48; + frame[1] = instance->generic.btn << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + return true; +} + +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 56; + bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_telis_get_upload( + SubGhzProtocolEncoderSomfyTelis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 2; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + + //Retransmission + for(uint8_t i = 0; i < 2; i++) { + //Hardware sync + for(uint8_t i = 0; i < 7; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + } + + size_t size_upload = index; + + 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; + } + return true; +} + +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_telis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_telis_stop(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context) { + SubGhzProtocolEncoderSomfyTelis* 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_somfy_telis_alloc(SubGhzEnvironment* environment) { UNUSED(environment); SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis)); @@ -299,9 +576,9 @@ static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGener */ uint64_t data = instance->data ^ (instance->data >> 8); - instance->btn = (data >> 44) & 0xF; - instance->cnt = (data >> 24) & 0xFFFF; - instance->serial = data & 0xFFFFFF; + instance->btn = (data >> 44) & 0xF; // ctrl + instance->cnt = (data >> 24) & 0xFFFF; // rolling code + instance->serial = data & 0xFFFFFF; // address } /** @@ -309,7 +586,7 @@ static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGener * @param btn Button number, 4 bit */ static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { - const char* name_btn[0x10] = { + const char* name_btn[16] = { "Unknown", "My", "Up", @@ -368,7 +645,6 @@ void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* o SubGhzProtocolDecoderSomfyTelis* instance = context; subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); - furi_string_cat_printf( output, "%s %db\r\n" diff --git a/lib/subghz/protocols/somfy_telis.h b/lib/subghz/protocols/somfy_telis.h index a6a9fa5b2..b5e989866 100644 --- a/lib/subghz/protocols/somfy_telis.h +++ b/lib/subghz/protocols/somfy_telis.h @@ -11,6 +11,58 @@ extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; extern const SubGhzProtocol subghz_protocol_somfy_telis; +/** + * Allocate SubGhzProtocolEncoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyTelis* pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context); + /** * Allocate SubGhzProtocolDecoderSomfyTelis. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 733fc35b5..35ba54a8a 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -39,6 +39,7 @@ static const uint32_t subghz_frequency_list[] = { 330000000, 345000000, 348000000, + 350000000, /* 387 - 464 */ 387000000, @@ -75,10 +76,10 @@ static const uint32_t subghz_frequency_list[] = { }; static const uint32_t subghz_hopper_frequency_list[] = { - 310000000, 315000000, - 318000000, + 330000000, 390000000, + 433420000, 433920000, 868350000, 0, diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc..205cc2752 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,14 +104,16 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -134,7 +136,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 6c34dc728..1b8ef6a14 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -90,6 +90,7 @@ typedef enum { SubGhzProtocolFlag_Save = (1 << 7), SubGhzProtocolFlag_Load = (1 << 8), SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), } SubGhzProtocolFlag; typedef struct { From 17db80794cc6c40c3d8d052049fafe8f75ba6418 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 18:46:40 +0000 Subject: [PATCH 140/231] Better asset pack git ignore --- .gitignore | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index dd487d0ce..bf7addba9 100644 --- a/.gitignore +++ b/.gitignore @@ -68,8 +68,13 @@ PVS-Studio.log # Automate files, etc automate.py deployments/ -assets/dolphin/custom/ -assets/resources/dolphin_custom/ fbt_options.py commitnotes.md lib/STM32CubeWB + +# Asset packs +assets/dolphin/custom/* +!assets/dolphin/custom/WatchDogs/ +!assets/dolphin/custom/ReadMe.md +assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/WatchDogs/ From dfb6e9daa6d52fa672864dae7733ac337a3081a7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:04:53 +0000 Subject: [PATCH 141/231] Fix spi header --- .../targets/furi_hal_include/furi_hal_spi.h | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/firmware/targets/furi_hal_include/furi_hal_spi.h b/firmware/targets/furi_hal_include/furi_hal_spi.h index 9c54befb4..af15a8838 100644 --- a/firmware/targets/furi_hal_include/furi_hal_spi.h +++ b/firmware/targets/furi_hal_include/furi_hal_spi.h @@ -16,6 +16,9 @@ void furi_hal_spi_config_deinit_early(); /** Initialize SPI HAL */ void furi_hal_spi_config_init(); +/** Initialize SPI DMA HAL */ +void furi_hal_spi_dma_init(); + /** Initialize SPI Bus * * @param handle pointer to FuriHalSpiBus instance @@ -71,6 +74,38 @@ bool furi_hal_spi_bus_rx( size_t size, uint32_t timeout); +/** SPI Transmit + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param buffer transmit buffer + * @param size transaction size (buffer size) + * @param timeout operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_tx( + FuriHalSpiBusHandle* handle, + const uint8_t* buffer, + size_t size, + uint32_t timeout); + +/** SPI Transmit and Receive + * + * @param handle pointer to FuriHalSpiBusHandle instance + * @param tx_buffer pointer to tx buffer + * @param rx_buffer pointer to rx buffer + * @param size transaction size (buffer size) + * @param timeout operation timeout in ms + * + * @return true on success + */ +bool furi_hal_spi_bus_trx( + FuriHalSpiBusHandle* handle, + const uint8_t* tx_buffer, + uint8_t* rx_buffer, + size_t size, + uint32_t timeout); + /** SPI Transmit and Receive with DMA * * @param handle pointer to FuriHalSpiBusHandle instance @@ -90,4 +125,4 @@ bool furi_hal_spi_bus_trx_dma( #ifdef __cplusplus } -#endif \ No newline at end of file +#endif From 7474ac193e863063137da392d533cbd6218b1d65 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:15:35 +0000 Subject: [PATCH 142/231] Kinda fix subghz gpio pin shenanigans --- lib/subghz/subghz_tx_rx_worker.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 205cc2752..42124bebc 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + while(furi_hal_gpio_read(&gpio_cc1101_g0)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,16 +104,14 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -136,7 +134,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); From 55d18eb8a4e2c03857b6790c6b30d30c4a7e9337 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:37:19 +0000 Subject: [PATCH 143/231] Kinda fix subghz protocols linker issues --- lib/subghz/SConscript | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 8fbec94ad..6d091114b 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -11,7 +11,6 @@ env.Append( File("subghz_tx_rx_worker.h"), File("transmitter.h"), File("registry.h"), - File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), From 5afe51589637fa8b4d227e1a8c3627a93896d53f Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:44:34 +0000 Subject: [PATCH 144/231] Fix missing canvas custom font api --- applications/services/gui/canvas.c | 6 ++++++ applications/services/gui/canvas.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/applications/services/gui/canvas.c b/applications/services/gui/canvas.c index 6a2bd3537..ee472138e 100644 --- a/applications/services/gui/canvas.c +++ b/applications/services/gui/canvas.c @@ -140,6 +140,12 @@ void canvas_set_font(Canvas* canvas, Font font) { } } +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font) { + furi_assert(canvas); + u8g2_SetFontMode(&canvas->fb, 1); + u8g2_SetFont(&canvas->fb, font); +} + void canvas_draw_str(Canvas* canvas, uint8_t x, uint8_t y, const char* str) { furi_assert(canvas); if(!str) return; diff --git a/applications/services/gui/canvas.h b/applications/services/gui/canvas.h index 9c03ef59a..9fb554be2 100644 --- a/applications/services/gui/canvas.h +++ b/applications/services/gui/canvas.h @@ -147,6 +147,13 @@ void canvas_invert_color(Canvas* canvas); */ void canvas_set_font(Canvas* canvas, Font font); +/** Set custom drawing font + * + * @param canvas Canvas instance + * @param font Pointer to u8g2 const uint8_t* font array + */ +void canvas_set_custom_u8g2_font(Canvas* canvas, const uint8_t* font); + /** Draw string at position of baseline defined by x, y. * * @param canvas Canvas instance From 8b1179794916cdf54d3e6a6d8bd1685c014a9ec7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 19:53:25 +0000 Subject: [PATCH 145/231] Fix api symbols --- firmware/targets/f7/api_symbols.csv | 640 +--------------------------- 1 file changed, 5 insertions(+), 635 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 3a687f32e..a38fe72fe 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,14.0,, +Version,v,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -178,7 +178,6 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, -Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -535,8 +534,6 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* -Function,-,atomo_decrypt,void,uint8_t* -Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -645,6 +642,7 @@ Function,+,canvas_invert_color,void,Canvas* Function,+,canvas_reset,void,Canvas* Function,+,canvas_set_bitmap_mode,void,"Canvas*, _Bool" Function,+,canvas_set_color,void,"Canvas*, Color" +Function,+,canvas_set_custom_u8g2_font,void,"Canvas*, const uint8_t*" Function,+,canvas_set_font,void,"Canvas*, Font" Function,+,canvas_set_font_direction,void,"Canvas*, CanvasDirection" Function,-,canvas_set_orientation,void,"Canvas*, CanvasOrientation" @@ -1348,10 +1346,13 @@ Function,+,furi_hal_spi_bus_handle_deinit,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_handle_init,void,FuriHalSpiBusHandle* Function,+,furi_hal_spi_bus_init,void,FuriHalSpiBus* Function,+,furi_hal_spi_bus_rx,_Bool,"FuriHalSpiBusHandle*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_trx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, uint8_t*, size_t, uint32_t" Function,+,furi_hal_spi_bus_trx_dma,_Bool,"FuriHalSpiBusHandle*, uint8_t*, uint8_t*, size_t, uint32_t" +Function,+,furi_hal_spi_bus_tx,_Bool,"FuriHalSpiBusHandle*, const uint8_t*, size_t, uint32_t" Function,-,furi_hal_spi_config_deinit_early,void, Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, +Function,+,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* Function,-,furi_hal_subghz_dump_state,void, Function,+,furi_hal_subghz_flush_rx,void, @@ -1745,8 +1746,6 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] -Function,-,keeloq_reset_kl_type,void, -Function,-,keeloq_reset_mfname,void, Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -2506,8 +2505,6 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." -Function,-,star_line_reset_kl_type,void, -Function,-,star_line_reset_mfname,void, Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" @@ -2689,275 +2686,11 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_alutech_at_4n_free,void,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_alutech_at_4n_reset,void,void* -Function,+,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ansonic_free,void,void* -Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ansonic_reset,void,void* -Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" -Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_bett_free,void,void* -Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_bett_reset,void,void* -Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_atomo_free,void,void* -Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* -Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_free,void,void* -Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_reset,void,void* -Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_came_twee_free,void,void* -Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_came_twee_reset,void,void* -Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_chamb_code_free,void,void* -Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* -Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_clemsa_free,void,void* -Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_clemsa_reset,void,void* -Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_doitrand_free,void,void* -Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_doitrand_reset,void,void* -Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_dooya_free,void,void* -Function,+,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_dooya_reset,void,void* -Function,+,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_faac_slh_free,void,void* -Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* -Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_gate_tx_free,void,void* -Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* -Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_free,void,void* -Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* -Function,-,subghz_protocol_decoder_holtek_th12x_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* -Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_hormann_free,void,void* -Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_hormann_reset,void,void* -Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_ido_free,void,void* -Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_ido_reset,void,void* -Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* -Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_keeloq_free,void,void* -Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_keeloq_reset,void,void* -Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_kia_free,void,void* -Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_kia_reset,void,void* -Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* -Function,+,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_linear_delta3_free,void,void* -Function,+,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_linear_delta3_reset,void,void* -Function,+,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_linear_free,void,void* -Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_linear_reset,void,void* -Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_magellan_free,void,void* -Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_magellan_reset,void,void* -Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_marantec_free,void,void* -Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_marantec_reset,void,void* -Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_megacode_free,void,void* -Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_megacode_reset,void,void* -Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_radio_free,void,void* -Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* -Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* -Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flo_free,void,void* -Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* -Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* -Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_power_smart_free,void,void* -Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_power_smart_reset,void,void* -Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" -Function,+,subghz_protocol_decoder_princeton_free,void,void* -Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* -Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" -Function,+,subghz_protocol_decoder_princeton_reset,void,void* -Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" @@ -2969,248 +2702,12 @@ Function,+,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, S Function,+,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" Function,+,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" Function,+,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_scher_khan_free,void,void* -Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* -Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* -Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_smc5326_free,void,void* -Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_smc5326_reset,void,void* -Function,-,subghz_protocol_decoder_smc5326_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* -Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_decoder_star_line_free,void,void* -Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* -Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" -Function,-,subghz_protocol_decoder_star_line_reset,void,void* -Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_ansonic_free,void,void* -Function,-,subghz_protocol_encoder_ansonic_stop,void,void* -Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_bett_free,void,void* -Function,-,subghz_protocol_encoder_bett_stop,void,void* -Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_atomo_free,void,void* -Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* -Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_free,void,void* -Function,-,subghz_protocol_encoder_came_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_came_twee_free,void,void* -Function,-,subghz_protocol_encoder_came_twee_stop,void,void* -Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_chamb_code_free,void,void* -Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* -Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_clemsa_free,void,void* -Function,-,subghz_protocol_encoder_clemsa_stop,void,void* -Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_doitrand_free,void,void* -Function,-,subghz_protocol_encoder_doitrand_stop,void,void* -Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_dooya_free,void,void* -Function,+,subghz_protocol_encoder_dooya_stop,void,void* -Function,+,subghz_protocol_encoder_dooya_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_faac_slh_free,void,void* -Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* -Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_gate_tx_free,void,void* -Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* -Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* Function,-,subghz_protocol_encoder_get_rssi_threshold,int,void* -Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_free,void,void* -Function,-,subghz_protocol_encoder_holtek_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* -Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* -Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_hormann_free,void,void* -Function,-,subghz_protocol_encoder_hormann_stop,void,void* -Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* -Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_keeloq_free,void,void* -Function,-,subghz_protocol_encoder_keeloq_stop,void,void* -Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_linear_delta3_free,void,void* -Function,+,subghz_protocol_encoder_linear_delta3_stop,void,void* -Function,+,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_linear_free,void,void* -Function,-,subghz_protocol_encoder_linear_stop,void,void* -Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_magellan_free,void,void* -Function,-,subghz_protocol_encoder_magellan_stop,void,void* -Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_marantec_free,void,void* -Function,-,subghz_protocol_encoder_marantec_stop,void,void* -Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_megacode_free,void,void* -Function,-,subghz_protocol_encoder_megacode_stop,void,void* -Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_radio_free,void,void* -Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* -Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* -Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flo_free,void,void* -Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* -Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* -Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_power_smart_free,void,void* -Function,-,subghz_protocol_encoder_power_smart_stop,void,void* -Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_princeton_free,void,void* -Function,+,subghz_protocol_encoder_princeton_stop,void,void* -Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* -Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_smc5326_free,void,void* -Function,-,subghz_protocol_encoder_smc5326_stop,void,void* -Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_somfy_keytis_free,void,void* -Function,+,subghz_protocol_encoder_somfy_keytis_stop,void,void* -Function,+,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* -Function,+,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* -Function,+,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" -Function,+,subghz_protocol_encoder_somfy_telis_free,void,void* -Function,+,subghz_protocol_encoder_somfy_telis_stop,void,void* -Function,+,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* -Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* -Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" -Function,-,subghz_protocol_encoder_star_line_free,void,void* -Function,-,subghz_protocol_encoder_star_line_stop,void,void* -Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* -Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -3220,11 +2717,6 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" -Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t -Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" -Function,+,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,+,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" -Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* @@ -4934,131 +4426,9 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, -Variable,+,subghz_protocol_alutech_at_4n,const SubGhzProtocol, -Variable,+,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, -Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_bett,const SubGhzProtocol, -Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, -Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, -Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, -Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, -Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, -Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_dooya,const SubGhzProtocol, -Variable,+,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, -Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, -Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, -Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, -Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_hormann,const SubGhzProtocol, -Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_ido,const SubGhzProtocol, -Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, -Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, -Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_kia,const SubGhzProtocol, -Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, -Variable,+,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear,const SubGhzProtocol, -Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_linear_delta3,const SubGhzProtocol, -Variable,+,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, -Variable,+,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_magellan,const SubGhzProtocol, -Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_marantec,const SubGhzProtocol, -Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_megacode,const SubGhzProtocol, -Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, -Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, -Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_pocsag,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, -Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_princeton,const SubGhzProtocol, -Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, -Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, -Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, -Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v1,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v1_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v1_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_secplus_v2,const SubGhzProtocol, -Variable,-,subghz_protocol_secplus_v2_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_secplus_v2_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_smc5326,const SubGhzProtocol, -Variable,-,subghz_protocol_smc5326_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_smc5326_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_keytis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_keytis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_keytis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_somfy_telis,const SubGhzProtocol, -Variable,-,subghz_protocol_somfy_telis_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_somfy_telis_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_star_line,const SubGhzProtocol, -Variable,-,subghz_protocol_star_line_decoder,const SubGhzProtocolDecoder, -Variable,-,subghz_protocol_star_line_encoder,const SubGhzProtocolEncoder, Variable,-,suboptarg,char*, Variable,-,u8g2_cb_mirror,const u8g2_cb_t, Variable,-,u8g2_cb_r0,const u8g2_cb_t, From 0a6f830ad05cf0be5c61d62eb85fd50c35bdf434 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Thu, 9 Feb 2023 20:05:48 +0000 Subject: [PATCH 146/231] Fix api version tag --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a38fe72fe..24c2d32db 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,15.0,, +Version,+,15.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From 955e16b0ef88f120d1c85dc6be24281cf93252a4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:14:40 +0000 Subject: [PATCH 147/231] Move NSFW mode to an asset pack --- .gitignore | 2 + .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 ++- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +-- .../desktop/animations/animation_manager.c | 4 +- .../desktop/animations/animation_storage.c | 10 +-- .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 ---- .../settings/xtreme_settings/xtreme_assets.c | 68 ++++++------------ .../settings/xtreme_settings/xtreme_assets.h | 1 + .../xtreme_settings/xtreme_settings.h | 1 - .../PaxGod_TikTok_Marketing/frame_0.png | Bin .../PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../PaxGod_TikTok_Marketing/frame_3.png | Bin .../PaxGod_TikTok_Marketing/frame_4.png | Bin .../PaxGod_TikTok_Marketing/frame_5.png | Bin .../PaxGod_TikTok_Marketing/frame_6.png | Bin .../PaxGod_TikTok_Marketing/frame_7.png | Bin .../PaxGod_TikTok_Marketing/frame_8.png | Bin .../PaxGod_TikTok_Marketing/frame_9.png | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 0 .../NSFW/Anims}/lvl_1/frame_0.png | Bin .../NSFW/Anims}/lvl_1/frame_1.png | Bin .../NSFW/Anims}/lvl_1/frame_10.png | Bin .../NSFW/Anims}/lvl_1/frame_11.png | Bin .../NSFW/Anims}/lvl_1/frame_12.png | Bin .../NSFW/Anims}/lvl_1/frame_13.png | Bin .../NSFW/Anims}/lvl_1/frame_14.png | Bin .../NSFW/Anims}/lvl_1/frame_15.png | Bin .../NSFW/Anims}/lvl_1/frame_16.png | Bin .../NSFW/Anims}/lvl_1/frame_17.png | Bin .../NSFW/Anims}/lvl_1/frame_18.png | Bin .../NSFW/Anims}/lvl_1/frame_19.png | Bin .../NSFW/Anims}/lvl_1/frame_2.png | Bin .../NSFW/Anims}/lvl_1/frame_20.png | Bin .../NSFW/Anims}/lvl_1/frame_21.png | Bin .../NSFW/Anims}/lvl_1/frame_22.png | Bin .../NSFW/Anims}/lvl_1/frame_23.png | Bin .../NSFW/Anims}/lvl_1/frame_24.png | Bin .../NSFW/Anims}/lvl_1/frame_25.png | Bin .../NSFW/Anims}/lvl_1/frame_26.png | Bin .../NSFW/Anims}/lvl_1/frame_27.png | Bin .../NSFW/Anims}/lvl_1/frame_28.png | Bin .../NSFW/Anims}/lvl_1/frame_29.png | Bin .../NSFW/Anims}/lvl_1/frame_3.png | Bin .../NSFW/Anims}/lvl_1/frame_30.png | Bin .../NSFW/Anims}/lvl_1/frame_4.png | Bin .../NSFW/Anims}/lvl_1/frame_5.png | Bin .../NSFW/Anims}/lvl_1/frame_6.png | Bin .../NSFW/Anims}/lvl_1/frame_7.png | Bin .../NSFW/Anims}/lvl_1/frame_8.png | Bin .../NSFW/Anims}/lvl_1/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.png | Bin .../NSFW/Anims}/lvl_10/frame_1.png | Bin .../NSFW/Anims}/lvl_10/frame_10.png | Bin .../NSFW/Anims}/lvl_10/frame_11.png | Bin .../NSFW/Anims}/lvl_10/frame_12.png | Bin .../NSFW/Anims}/lvl_10/frame_13.png | Bin .../NSFW/Anims}/lvl_10/frame_14.png | Bin .../NSFW/Anims}/lvl_10/frame_15.png | Bin .../NSFW/Anims}/lvl_10/frame_16.png | Bin .../NSFW/Anims}/lvl_10/frame_17.png | Bin .../NSFW/Anims}/lvl_10/frame_18.png | Bin .../NSFW/Anims}/lvl_10/frame_19.png | Bin .../NSFW/Anims}/lvl_10/frame_2.png | Bin .../NSFW/Anims}/lvl_10/frame_20.png | Bin .../NSFW/Anims}/lvl_10/frame_21.png | Bin .../NSFW/Anims}/lvl_10/frame_22.png | Bin .../NSFW/Anims}/lvl_10/frame_23.png | Bin .../NSFW/Anims}/lvl_10/frame_24.png | Bin .../NSFW/Anims}/lvl_10/frame_25.png | Bin .../NSFW/Anims}/lvl_10/frame_26.png | Bin .../NSFW/Anims}/lvl_10/frame_27.png | Bin .../NSFW/Anims}/lvl_10/frame_3.png | Bin .../NSFW/Anims}/lvl_10/frame_4.png | Bin .../NSFW/Anims}/lvl_10/frame_5.png | Bin .../NSFW/Anims}/lvl_10/frame_6.png | Bin .../NSFW/Anims}/lvl_10/frame_7.png | Bin .../NSFW/Anims}/lvl_10/frame_8.png | Bin .../NSFW/Anims}/lvl_10/frame_9.png | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.png | Bin .../NSFW/Anims}/lvl_11/frame_1.png | Bin .../NSFW/Anims}/lvl_11/frame_10.png | Bin .../NSFW/Anims}/lvl_11/frame_11.png | Bin .../NSFW/Anims}/lvl_11/frame_12.png | Bin .../NSFW/Anims}/lvl_11/frame_13.png | Bin .../NSFW/Anims}/lvl_11/frame_14.png | Bin .../NSFW/Anims}/lvl_11/frame_15.png | Bin .../NSFW/Anims}/lvl_11/frame_16.png | Bin .../NSFW/Anims}/lvl_11/frame_17.png | Bin .../NSFW/Anims}/lvl_11/frame_18.png | Bin .../NSFW/Anims}/lvl_11/frame_19.png | Bin .../NSFW/Anims}/lvl_11/frame_2.png | Bin .../NSFW/Anims}/lvl_11/frame_20.png | Bin .../NSFW/Anims}/lvl_11/frame_21.png | Bin .../NSFW/Anims}/lvl_11/frame_22.png | Bin .../NSFW/Anims}/lvl_11/frame_23.png | Bin .../NSFW/Anims}/lvl_11/frame_24.png | Bin .../NSFW/Anims}/lvl_11/frame_25.png | Bin .../NSFW/Anims}/lvl_11/frame_26.png | Bin .../NSFW/Anims}/lvl_11/frame_27.png | Bin .../NSFW/Anims}/lvl_11/frame_28.png | Bin .../NSFW/Anims}/lvl_11/frame_29.png | Bin .../NSFW/Anims}/lvl_11/frame_3.png | Bin .../NSFW/Anims}/lvl_11/frame_30.png | Bin .../NSFW/Anims}/lvl_11/frame_31.png | Bin .../NSFW/Anims}/lvl_11/frame_32.png | Bin .../NSFW/Anims}/lvl_11/frame_33.png | Bin .../NSFW/Anims}/lvl_11/frame_34.png | Bin .../NSFW/Anims}/lvl_11/frame_35.png | Bin .../NSFW/Anims}/lvl_11/frame_36.png | Bin .../NSFW/Anims}/lvl_11/frame_37.png | Bin .../NSFW/Anims}/lvl_11/frame_38.png | Bin .../NSFW/Anims}/lvl_11/frame_39.png | Bin .../NSFW/Anims}/lvl_11/frame_4.png | Bin .../NSFW/Anims}/lvl_11/frame_40.png | Bin .../NSFW/Anims}/lvl_11/frame_41.png | Bin .../NSFW/Anims}/lvl_11/frame_42.png | Bin .../NSFW/Anims}/lvl_11/frame_43.png | Bin .../NSFW/Anims}/lvl_11/frame_44.png | Bin .../NSFW/Anims}/lvl_11/frame_45.png | Bin .../NSFW/Anims}/lvl_11/frame_46.png | Bin .../NSFW/Anims}/lvl_11/frame_47.png | Bin .../NSFW/Anims}/lvl_11/frame_48.png | Bin .../NSFW/Anims}/lvl_11/frame_49.png | Bin .../NSFW/Anims}/lvl_11/frame_5.png | Bin .../NSFW/Anims}/lvl_11/frame_6.png | Bin .../NSFW/Anims}/lvl_11/frame_7.png | Bin .../NSFW/Anims}/lvl_11/frame_8.png | Bin .../NSFW/Anims}/lvl_11/frame_9.png | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.png | Bin .../NSFW/Anims}/lvl_12/frame_1.png | Bin .../NSFW/Anims}/lvl_12/frame_10.png | Bin .../NSFW/Anims}/lvl_12/frame_11.png | Bin .../NSFW/Anims}/lvl_12/frame_12.png | Bin .../NSFW/Anims}/lvl_12/frame_13.png | Bin .../NSFW/Anims}/lvl_12/frame_14.png | Bin .../NSFW/Anims}/lvl_12/frame_15.png | Bin .../NSFW/Anims}/lvl_12/frame_2.png | Bin .../NSFW/Anims}/lvl_12/frame_3.png | Bin .../NSFW/Anims}/lvl_12/frame_4.png | Bin .../NSFW/Anims}/lvl_12/frame_5.png | Bin .../NSFW/Anims}/lvl_12/frame_6.png | Bin .../NSFW/Anims}/lvl_12/frame_7.png | Bin .../NSFW/Anims}/lvl_12/frame_8.png | Bin .../NSFW/Anims}/lvl_12/frame_9.png | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.png | Bin .../NSFW/Anims}/lvl_13/frame_1.png | Bin .../NSFW/Anims}/lvl_13/frame_10.png | Bin .../NSFW/Anims}/lvl_13/frame_2.png | Bin .../NSFW/Anims}/lvl_13/frame_3.png | Bin .../NSFW/Anims}/lvl_13/frame_4.png | Bin .../NSFW/Anims}/lvl_13/frame_5.png | Bin .../NSFW/Anims}/lvl_13/frame_6.png | Bin .../NSFW/Anims}/lvl_13/frame_7.png | Bin .../NSFW/Anims}/lvl_13/frame_8.png | Bin .../NSFW/Anims}/lvl_13/frame_9.png | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.png | Bin .../NSFW/Anims}/lvl_14/frame_1.png | Bin .../NSFW/Anims}/lvl_14/frame_2.png | Bin .../NSFW/Anims}/lvl_14/frame_3.png | Bin .../NSFW/Anims}/lvl_14/frame_4.png | Bin .../NSFW/Anims}/lvl_14/frame_5.png | Bin .../NSFW/Anims}/lvl_14/frame_6.png | Bin .../NSFW/Anims}/lvl_14/frame_7.png | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.png | Bin .../NSFW/Anims}/lvl_15/frame_1.png | Bin .../NSFW/Anims}/lvl_15/frame_10.png | Bin .../NSFW/Anims}/lvl_15/frame_11.png | Bin .../NSFW/Anims}/lvl_15/frame_12.png | Bin .../NSFW/Anims}/lvl_15/frame_13.png | Bin .../NSFW/Anims}/lvl_15/frame_14.png | Bin .../NSFW/Anims}/lvl_15/frame_15.png | Bin .../NSFW/Anims}/lvl_15/frame_16.png | Bin .../NSFW/Anims}/lvl_15/frame_17.png | Bin .../NSFW/Anims}/lvl_15/frame_18.png | Bin .../NSFW/Anims}/lvl_15/frame_19.png | Bin .../NSFW/Anims}/lvl_15/frame_2.png | Bin .../NSFW/Anims}/lvl_15/frame_20.png | Bin .../NSFW/Anims}/lvl_15/frame_21.png | Bin .../NSFW/Anims}/lvl_15/frame_3.png | Bin .../NSFW/Anims}/lvl_15/frame_4.png | Bin .../NSFW/Anims}/lvl_15/frame_5.png | Bin .../NSFW/Anims}/lvl_15/frame_6.png | Bin .../NSFW/Anims}/lvl_15/frame_7.png | Bin .../NSFW/Anims}/lvl_15/frame_8.png | Bin .../NSFW/Anims}/lvl_15/frame_9.png | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.png | Bin .../NSFW/Anims}/lvl_16/frame_1.png | Bin .../NSFW/Anims}/lvl_16/frame_10.png | Bin .../NSFW/Anims}/lvl_16/frame_11.png | Bin .../NSFW/Anims}/lvl_16/frame_12.png | Bin .../NSFW/Anims}/lvl_16/frame_13.png | Bin .../NSFW/Anims}/lvl_16/frame_14.png | Bin .../NSFW/Anims}/lvl_16/frame_15.png | Bin .../NSFW/Anims}/lvl_16/frame_16.png | Bin .../NSFW/Anims}/lvl_16/frame_17.png | Bin .../NSFW/Anims}/lvl_16/frame_18.png | Bin .../NSFW/Anims}/lvl_16/frame_19.png | Bin .../NSFW/Anims}/lvl_16/frame_2.png | Bin .../NSFW/Anims}/lvl_16/frame_20.png | Bin .../NSFW/Anims}/lvl_16/frame_3.png | Bin .../NSFW/Anims}/lvl_16/frame_4.png | Bin .../NSFW/Anims}/lvl_16/frame_5.png | Bin .../NSFW/Anims}/lvl_16/frame_6.png | Bin .../NSFW/Anims}/lvl_16/frame_7.png | Bin .../NSFW/Anims}/lvl_16/frame_8.png | Bin .../NSFW/Anims}/lvl_16/frame_9.png | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.png | Bin .../NSFW/Anims}/lvl_17/frame_1.png | Bin .../NSFW/Anims}/lvl_17/frame_10.png | Bin .../NSFW/Anims}/lvl_17/frame_11.png | Bin .../NSFW/Anims}/lvl_17/frame_12.png | Bin .../NSFW/Anims}/lvl_17/frame_13.png | Bin .../NSFW/Anims}/lvl_17/frame_14.png | Bin .../NSFW/Anims}/lvl_17/frame_15.png | Bin .../NSFW/Anims}/lvl_17/frame_16.png | Bin .../NSFW/Anims}/lvl_17/frame_17.png | Bin .../NSFW/Anims}/lvl_17/frame_18.png | Bin .../NSFW/Anims}/lvl_17/frame_19.png | Bin .../NSFW/Anims}/lvl_17/frame_2.png | Bin .../NSFW/Anims}/lvl_17/frame_20.png | Bin .../NSFW/Anims}/lvl_17/frame_21.png | Bin .../NSFW/Anims}/lvl_17/frame_22.png | Bin .../NSFW/Anims}/lvl_17/frame_23.png | Bin .../NSFW/Anims}/lvl_17/frame_24.png | Bin .../NSFW/Anims}/lvl_17/frame_25.png | Bin .../NSFW/Anims}/lvl_17/frame_26.png | Bin .../NSFW/Anims}/lvl_17/frame_27.png | Bin .../NSFW/Anims}/lvl_17/frame_28.png | Bin .../NSFW/Anims}/lvl_17/frame_29.png | Bin .../NSFW/Anims}/lvl_17/frame_3.png | Bin .../NSFW/Anims}/lvl_17/frame_30.png | Bin .../NSFW/Anims}/lvl_17/frame_31.png | Bin .../NSFW/Anims}/lvl_17/frame_4.png | Bin .../NSFW/Anims}/lvl_17/frame_5.png | Bin .../NSFW/Anims}/lvl_17/frame_6.png | Bin .../NSFW/Anims}/lvl_17/frame_7.png | Bin .../NSFW/Anims}/lvl_17/frame_8.png | Bin .../NSFW/Anims}/lvl_17/frame_9.png | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.png | Bin .../NSFW/Anims}/lvl_18/frame_1.png | Bin .../NSFW/Anims}/lvl_18/frame_10.png | Bin .../NSFW/Anims}/lvl_18/frame_11.png | Bin .../NSFW/Anims}/lvl_18/frame_12.png | Bin .../NSFW/Anims}/lvl_18/frame_13.png | Bin .../NSFW/Anims}/lvl_18/frame_14.png | Bin .../NSFW/Anims}/lvl_18/frame_15.png | Bin .../NSFW/Anims}/lvl_18/frame_16.png | Bin .../NSFW/Anims}/lvl_18/frame_17.png | Bin .../NSFW/Anims}/lvl_18/frame_18.png | Bin .../NSFW/Anims}/lvl_18/frame_19.png | Bin .../NSFW/Anims}/lvl_18/frame_2.png | Bin .../NSFW/Anims}/lvl_18/frame_20.png | Bin .../NSFW/Anims}/lvl_18/frame_21.png | Bin .../NSFW/Anims}/lvl_18/frame_22.png | Bin .../NSFW/Anims}/lvl_18/frame_3.png | Bin .../NSFW/Anims}/lvl_18/frame_4.png | Bin .../NSFW/Anims}/lvl_18/frame_5.png | Bin .../NSFW/Anims}/lvl_18/frame_6.png | Bin .../NSFW/Anims}/lvl_18/frame_7.png | Bin .../NSFW/Anims}/lvl_18/frame_8.png | Bin .../NSFW/Anims}/lvl_18/frame_9.png | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.png | Bin .../NSFW/Anims}/lvl_19/frame_1.png | Bin .../NSFW/Anims}/lvl_19/frame_10.png | Bin .../NSFW/Anims}/lvl_19/frame_11.png | Bin .../NSFW/Anims}/lvl_19/frame_12.png | Bin .../NSFW/Anims}/lvl_19/frame_13.png | Bin .../NSFW/Anims}/lvl_19/frame_14.png | Bin .../NSFW/Anims}/lvl_19/frame_15.png | Bin .../NSFW/Anims}/lvl_19/frame_16.png | Bin .../NSFW/Anims}/lvl_19/frame_17.png | Bin .../NSFW/Anims}/lvl_19/frame_18.png | Bin .../NSFW/Anims}/lvl_19/frame_19.png | Bin .../NSFW/Anims}/lvl_19/frame_2.png | Bin .../NSFW/Anims}/lvl_19/frame_20.png | Bin .../NSFW/Anims}/lvl_19/frame_21.png | Bin .../NSFW/Anims}/lvl_19/frame_3.png | Bin .../NSFW/Anims}/lvl_19/frame_4.png | Bin .../NSFW/Anims}/lvl_19/frame_5.png | Bin .../NSFW/Anims}/lvl_19/frame_6.png | Bin .../NSFW/Anims}/lvl_19/frame_7.png | Bin .../NSFW/Anims}/lvl_19/frame_8.png | Bin .../NSFW/Anims}/lvl_19/frame_9.png | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.png | Bin .../NSFW/Anims}/lvl_2/frame_1.png | Bin .../NSFW/Anims}/lvl_2/frame_10.png | Bin .../NSFW/Anims}/lvl_2/frame_11.png | Bin .../NSFW/Anims}/lvl_2/frame_12.png | Bin .../NSFW/Anims}/lvl_2/frame_13.png | Bin .../NSFW/Anims}/lvl_2/frame_14.png | Bin .../NSFW/Anims}/lvl_2/frame_2.png | Bin .../NSFW/Anims}/lvl_2/frame_3.png | Bin .../NSFW/Anims}/lvl_2/frame_4.png | Bin .../NSFW/Anims}/lvl_2/frame_5.png | Bin .../NSFW/Anims}/lvl_2/frame_6.png | Bin .../NSFW/Anims}/lvl_2/frame_7.png | Bin .../NSFW/Anims}/lvl_2/frame_8.png | Bin .../NSFW/Anims}/lvl_2/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.png | Bin .../NSFW/Anims}/lvl_20/frame_1.png | Bin .../NSFW/Anims}/lvl_20/frame_10.png | Bin .../NSFW/Anims}/lvl_20/frame_11.png | Bin .../NSFW/Anims}/lvl_20/frame_12.png | Bin .../NSFW/Anims}/lvl_20/frame_13.png | Bin .../NSFW/Anims}/lvl_20/frame_2.png | Bin .../NSFW/Anims}/lvl_20/frame_3.png | Bin .../NSFW/Anims}/lvl_20/frame_4.png | Bin .../NSFW/Anims}/lvl_20/frame_5.png | Bin .../NSFW/Anims}/lvl_20/frame_6.png | Bin .../NSFW/Anims}/lvl_20/frame_7.png | Bin .../NSFW/Anims}/lvl_20/frame_8.png | Bin .../NSFW/Anims}/lvl_20/frame_9.png | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.png | Bin .../NSFW/Anims}/lvl_21/frame_1.png | Bin .../NSFW/Anims}/lvl_21/frame_2.png | Bin .../NSFW/Anims}/lvl_21/frame_3.png | Bin .../NSFW/Anims}/lvl_21/frame_4.png | Bin .../NSFW/Anims}/lvl_21/frame_5.png | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.png | Bin .../NSFW/Anims}/lvl_22/frame_1.png | Bin .../NSFW/Anims}/lvl_22/frame_10.png | Bin .../NSFW/Anims}/lvl_22/frame_11.png | Bin .../NSFW/Anims}/lvl_22/frame_12.png | Bin .../NSFW/Anims}/lvl_22/frame_13.png | Bin .../NSFW/Anims}/lvl_22/frame_14.png | Bin .../NSFW/Anims}/lvl_22/frame_15.png | Bin .../NSFW/Anims}/lvl_22/frame_16.png | Bin .../NSFW/Anims}/lvl_22/frame_17.png | Bin .../NSFW/Anims}/lvl_22/frame_18.png | Bin .../NSFW/Anims}/lvl_22/frame_19.png | Bin .../NSFW/Anims}/lvl_22/frame_2.png | Bin .../NSFW/Anims}/lvl_22/frame_20.png | Bin .../NSFW/Anims}/lvl_22/frame_21.png | Bin .../NSFW/Anims}/lvl_22/frame_22.png | Bin .../NSFW/Anims}/lvl_22/frame_23.png | Bin .../NSFW/Anims}/lvl_22/frame_24.png | Bin .../NSFW/Anims}/lvl_22/frame_25.png | Bin .../NSFW/Anims}/lvl_22/frame_26.png | Bin .../NSFW/Anims}/lvl_22/frame_27.png | Bin .../NSFW/Anims}/lvl_22/frame_28.png | Bin .../NSFW/Anims}/lvl_22/frame_29.png | Bin .../NSFW/Anims}/lvl_22/frame_3.png | Bin .../NSFW/Anims}/lvl_22/frame_30.png | Bin .../NSFW/Anims}/lvl_22/frame_31.png | Bin .../NSFW/Anims}/lvl_22/frame_32.png | Bin .../NSFW/Anims}/lvl_22/frame_33.png | Bin .../NSFW/Anims}/lvl_22/frame_34.png | Bin .../NSFW/Anims}/lvl_22/frame_35.png | Bin .../NSFW/Anims}/lvl_22/frame_36.png | Bin .../NSFW/Anims}/lvl_22/frame_37.png | Bin .../NSFW/Anims}/lvl_22/frame_38.png | Bin .../NSFW/Anims}/lvl_22/frame_39.png | Bin .../NSFW/Anims}/lvl_22/frame_4.png | Bin .../NSFW/Anims}/lvl_22/frame_40.png | Bin .../NSFW/Anims}/lvl_22/frame_41.png | Bin .../NSFW/Anims}/lvl_22/frame_42.png | Bin .../NSFW/Anims}/lvl_22/frame_43.png | Bin .../NSFW/Anims}/lvl_22/frame_44.png | Bin .../NSFW/Anims}/lvl_22/frame_45.png | Bin .../NSFW/Anims}/lvl_22/frame_46.png | Bin .../NSFW/Anims}/lvl_22/frame_47.png | Bin .../NSFW/Anims}/lvl_22/frame_48.png | Bin .../NSFW/Anims}/lvl_22/frame_49.png | Bin .../NSFW/Anims}/lvl_22/frame_5.png | Bin .../NSFW/Anims}/lvl_22/frame_50.png | Bin .../NSFW/Anims}/lvl_22/frame_51.png | Bin .../NSFW/Anims}/lvl_22/frame_52.png | Bin .../NSFW/Anims}/lvl_22/frame_53.png | Bin .../NSFW/Anims}/lvl_22/frame_54.png | Bin .../NSFW/Anims}/lvl_22/frame_55.png | Bin .../NSFW/Anims}/lvl_22/frame_56.png | Bin .../NSFW/Anims}/lvl_22/frame_57.png | Bin .../NSFW/Anims}/lvl_22/frame_58.png | Bin .../NSFW/Anims}/lvl_22/frame_59.png | Bin .../NSFW/Anims}/lvl_22/frame_6.png | Bin .../NSFW/Anims}/lvl_22/frame_7.png | Bin .../NSFW/Anims}/lvl_22/frame_8.png | Bin .../NSFW/Anims}/lvl_22/frame_9.png | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.png | Bin .../NSFW/Anims}/lvl_23/frame_1.png | Bin .../NSFW/Anims}/lvl_23/frame_10.png | Bin .../NSFW/Anims}/lvl_23/frame_11.png | Bin .../NSFW/Anims}/lvl_23/frame_12.png | Bin .../NSFW/Anims}/lvl_23/frame_13.png | Bin .../NSFW/Anims}/lvl_23/frame_14.png | Bin .../NSFW/Anims}/lvl_23/frame_15.png | Bin .../NSFW/Anims}/lvl_23/frame_16.png | Bin .../NSFW/Anims}/lvl_23/frame_2.png | Bin .../NSFW/Anims}/lvl_23/frame_3.png | Bin .../NSFW/Anims}/lvl_23/frame_4.png | Bin .../NSFW/Anims}/lvl_23/frame_5.png | Bin .../NSFW/Anims}/lvl_23/frame_6.png | Bin .../NSFW/Anims}/lvl_23/frame_7.png | Bin .../NSFW/Anims}/lvl_23/frame_8.png | Bin .../NSFW/Anims}/lvl_23/frame_9.png | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.png | Bin .../NSFW/Anims}/lvl_24/frame_1.png | Bin .../NSFW/Anims}/lvl_24/frame_10.png | Bin .../NSFW/Anims}/lvl_24/frame_11.png | Bin .../NSFW/Anims}/lvl_24/frame_12.png | Bin .../NSFW/Anims}/lvl_24/frame_13.png | Bin .../NSFW/Anims}/lvl_24/frame_14.png | Bin .../NSFW/Anims}/lvl_24/frame_15.png | Bin .../NSFW/Anims}/lvl_24/frame_16.png | Bin .../NSFW/Anims}/lvl_24/frame_17.png | Bin .../NSFW/Anims}/lvl_24/frame_18.png | Bin .../NSFW/Anims}/lvl_24/frame_19.png | Bin .../NSFW/Anims}/lvl_24/frame_2.png | Bin .../NSFW/Anims}/lvl_24/frame_20.png | Bin .../NSFW/Anims}/lvl_24/frame_21.png | Bin .../NSFW/Anims}/lvl_24/frame_22.png | Bin .../NSFW/Anims}/lvl_24/frame_23.png | Bin .../NSFW/Anims}/lvl_24/frame_24.png | Bin .../NSFW/Anims}/lvl_24/frame_25.png | Bin .../NSFW/Anims}/lvl_24/frame_26.png | Bin .../NSFW/Anims}/lvl_24/frame_27.png | Bin .../NSFW/Anims}/lvl_24/frame_28.png | Bin .../NSFW/Anims}/lvl_24/frame_29.png | Bin .../NSFW/Anims}/lvl_24/frame_3.png | Bin .../NSFW/Anims}/lvl_24/frame_4.png | Bin .../NSFW/Anims}/lvl_24/frame_5.png | Bin .../NSFW/Anims}/lvl_24/frame_6.png | Bin .../NSFW/Anims}/lvl_24/frame_7.png | Bin .../NSFW/Anims}/lvl_24/frame_8.png | Bin .../NSFW/Anims}/lvl_24/frame_9.png | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.png | Bin .../NSFW/Anims}/lvl_25/frame_1.png | Bin .../NSFW/Anims}/lvl_25/frame_10.png | Bin .../NSFW/Anims}/lvl_25/frame_11.png | Bin .../NSFW/Anims}/lvl_25/frame_12.png | Bin .../NSFW/Anims}/lvl_25/frame_13.png | Bin .../NSFW/Anims}/lvl_25/frame_14.png | Bin .../NSFW/Anims}/lvl_25/frame_15.png | Bin .../NSFW/Anims}/lvl_25/frame_16.png | Bin .../NSFW/Anims}/lvl_25/frame_17.png | Bin .../NSFW/Anims}/lvl_25/frame_18.png | Bin .../NSFW/Anims}/lvl_25/frame_19.png | Bin .../NSFW/Anims}/lvl_25/frame_2.png | Bin .../NSFW/Anims}/lvl_25/frame_20.png | Bin .../NSFW/Anims}/lvl_25/frame_21.png | Bin .../NSFW/Anims}/lvl_25/frame_22.png | Bin .../NSFW/Anims}/lvl_25/frame_23.png | Bin .../NSFW/Anims}/lvl_25/frame_24.png | Bin .../NSFW/Anims}/lvl_25/frame_25.png | Bin .../NSFW/Anims}/lvl_25/frame_26.png | Bin .../NSFW/Anims}/lvl_25/frame_27.png | Bin .../NSFW/Anims}/lvl_25/frame_28.png | Bin .../NSFW/Anims}/lvl_25/frame_29.png | Bin .../NSFW/Anims}/lvl_25/frame_3.png | Bin .../NSFW/Anims}/lvl_25/frame_30.png | Bin .../NSFW/Anims}/lvl_25/frame_31.png | Bin .../NSFW/Anims}/lvl_25/frame_32.png | Bin .../NSFW/Anims}/lvl_25/frame_33.png | Bin .../NSFW/Anims}/lvl_25/frame_34.png | Bin .../NSFW/Anims}/lvl_25/frame_35.png | Bin .../NSFW/Anims}/lvl_25/frame_4.png | Bin .../NSFW/Anims}/lvl_25/frame_5.png | Bin .../NSFW/Anims}/lvl_25/frame_6.png | Bin .../NSFW/Anims}/lvl_25/frame_7.png | Bin .../NSFW/Anims}/lvl_25/frame_8.png | Bin .../NSFW/Anims}/lvl_25/frame_9.png | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.png | Bin .../NSFW/Anims}/lvl_26/frame_1.png | Bin .../NSFW/Anims}/lvl_26/frame_10.png | Bin .../NSFW/Anims}/lvl_26/frame_11.png | Bin .../NSFW/Anims}/lvl_26/frame_2.png | Bin .../NSFW/Anims}/lvl_26/frame_3.png | Bin .../NSFW/Anims}/lvl_26/frame_4.png | Bin .../NSFW/Anims}/lvl_26/frame_5.png | Bin .../NSFW/Anims}/lvl_26/frame_6.png | Bin .../NSFW/Anims}/lvl_26/frame_7.png | Bin .../NSFW/Anims}/lvl_26/frame_8.png | Bin .../NSFW/Anims}/lvl_26/frame_9.png | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.png | Bin .../NSFW/Anims}/lvl_27/frame_1.png | Bin .../NSFW/Anims}/lvl_27/frame_10.png | Bin .../NSFW/Anims}/lvl_27/frame_11.png | Bin .../NSFW/Anims}/lvl_27/frame_12.png | Bin .../NSFW/Anims}/lvl_27/frame_13.png | Bin .../NSFW/Anims}/lvl_27/frame_14.png | Bin .../NSFW/Anims}/lvl_27/frame_15.png | Bin .../NSFW/Anims}/lvl_27/frame_16.png | Bin .../NSFW/Anims}/lvl_27/frame_17.png | Bin .../NSFW/Anims}/lvl_27/frame_18.png | Bin .../NSFW/Anims}/lvl_27/frame_19.png | Bin .../NSFW/Anims}/lvl_27/frame_2.png | Bin .../NSFW/Anims}/lvl_27/frame_20.png | Bin .../NSFW/Anims}/lvl_27/frame_21.png | Bin .../NSFW/Anims}/lvl_27/frame_3.png | Bin .../NSFW/Anims}/lvl_27/frame_4.png | Bin .../NSFW/Anims}/lvl_27/frame_5.png | Bin .../NSFW/Anims}/lvl_27/frame_6.png | Bin .../NSFW/Anims}/lvl_27/frame_7.png | Bin .../NSFW/Anims}/lvl_27/frame_8.png | Bin .../NSFW/Anims}/lvl_27/frame_9.png | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.png | Bin .../NSFW/Anims}/lvl_28/frame_1.png | Bin .../NSFW/Anims}/lvl_28/frame_2.png | Bin .../NSFW/Anims}/lvl_28/frame_3.png | Bin .../NSFW/Anims}/lvl_28/frame_4.png | Bin .../NSFW/Anims}/lvl_28/frame_5.png | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.png | Bin .../NSFW/Anims}/lvl_29/frame_1.png | Bin .../NSFW/Anims}/lvl_29/frame_10.png | Bin .../NSFW/Anims}/lvl_29/frame_11.png | Bin .../NSFW/Anims}/lvl_29/frame_12.png | Bin .../NSFW/Anims}/lvl_29/frame_13.png | Bin .../NSFW/Anims}/lvl_29/frame_14.png | Bin .../NSFW/Anims}/lvl_29/frame_15.png | Bin .../NSFW/Anims}/lvl_29/frame_16.png | Bin .../NSFW/Anims}/lvl_29/frame_17.png | Bin .../NSFW/Anims}/lvl_29/frame_18.png | Bin .../NSFW/Anims}/lvl_29/frame_19.png | Bin .../NSFW/Anims}/lvl_29/frame_2.png | Bin .../NSFW/Anims}/lvl_29/frame_20.png | Bin .../NSFW/Anims}/lvl_29/frame_21.png | Bin .../NSFW/Anims}/lvl_29/frame_22.png | Bin .../NSFW/Anims}/lvl_29/frame_23.png | Bin .../NSFW/Anims}/lvl_29/frame_24.png | Bin .../NSFW/Anims}/lvl_29/frame_25.png | Bin .../NSFW/Anims}/lvl_29/frame_26.png | Bin .../NSFW/Anims}/lvl_29/frame_27.png | Bin .../NSFW/Anims}/lvl_29/frame_28.png | Bin .../NSFW/Anims}/lvl_29/frame_29.png | Bin .../NSFW/Anims}/lvl_29/frame_3.png | Bin .../NSFW/Anims}/lvl_29/frame_30.png | Bin .../NSFW/Anims}/lvl_29/frame_31.png | Bin .../NSFW/Anims}/lvl_29/frame_32.png | Bin .../NSFW/Anims}/lvl_29/frame_33.png | Bin .../NSFW/Anims}/lvl_29/frame_34.png | Bin .../NSFW/Anims}/lvl_29/frame_35.png | Bin .../NSFW/Anims}/lvl_29/frame_36.png | Bin .../NSFW/Anims}/lvl_29/frame_37.png | Bin .../NSFW/Anims}/lvl_29/frame_38.png | Bin .../NSFW/Anims}/lvl_29/frame_39.png | Bin .../NSFW/Anims}/lvl_29/frame_4.png | Bin .../NSFW/Anims}/lvl_29/frame_40.png | Bin .../NSFW/Anims}/lvl_29/frame_41.png | Bin .../NSFW/Anims}/lvl_29/frame_42.png | Bin .../NSFW/Anims}/lvl_29/frame_43.png | Bin .../NSFW/Anims}/lvl_29/frame_44.png | Bin .../NSFW/Anims}/lvl_29/frame_45.png | Bin .../NSFW/Anims}/lvl_29/frame_46.png | Bin .../NSFW/Anims}/lvl_29/frame_47.png | Bin .../NSFW/Anims}/lvl_29/frame_48.png | Bin .../NSFW/Anims}/lvl_29/frame_49.png | Bin .../NSFW/Anims}/lvl_29/frame_5.png | Bin .../NSFW/Anims}/lvl_29/frame_50.png | Bin .../NSFW/Anims}/lvl_29/frame_51.png | Bin .../NSFW/Anims}/lvl_29/frame_6.png | Bin .../NSFW/Anims}/lvl_29/frame_7.png | Bin .../NSFW/Anims}/lvl_29/frame_8.png | Bin .../NSFW/Anims}/lvl_29/frame_9.png | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.png | Bin .../NSFW/Anims}/lvl_3/frame_1.png | Bin .../NSFW/Anims}/lvl_3/frame_10.png | Bin .../NSFW/Anims}/lvl_3/frame_11.png | Bin .../NSFW/Anims}/lvl_3/frame_12.png | Bin .../NSFW/Anims}/lvl_3/frame_13.png | Bin .../NSFW/Anims}/lvl_3/frame_14.png | Bin .../NSFW/Anims}/lvl_3/frame_2.png | Bin .../NSFW/Anims}/lvl_3/frame_3.png | Bin .../NSFW/Anims}/lvl_3/frame_4.png | Bin .../NSFW/Anims}/lvl_3/frame_5.png | Bin .../NSFW/Anims}/lvl_3/frame_6.png | Bin .../NSFW/Anims}/lvl_3/frame_7.png | Bin .../NSFW/Anims}/lvl_3/frame_8.png | Bin .../NSFW/Anims}/lvl_3/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.png | Bin .../NSFW/Anims}/lvl_30/frame_1.png | Bin .../NSFW/Anims}/lvl_30/frame_10.png | Bin .../NSFW/Anims}/lvl_30/frame_11.png | Bin .../NSFW/Anims}/lvl_30/frame_12.png | Bin .../NSFW/Anims}/lvl_30/frame_13.png | Bin .../NSFW/Anims}/lvl_30/frame_14.png | Bin .../NSFW/Anims}/lvl_30/frame_15.png | Bin .../NSFW/Anims}/lvl_30/frame_16.png | Bin .../NSFW/Anims}/lvl_30/frame_17.png | Bin .../NSFW/Anims}/lvl_30/frame_18.png | Bin .../NSFW/Anims}/lvl_30/frame_19.png | Bin .../NSFW/Anims}/lvl_30/frame_2.png | Bin .../NSFW/Anims}/lvl_30/frame_20.png | Bin .../NSFW/Anims}/lvl_30/frame_21.png | Bin .../NSFW/Anims}/lvl_30/frame_22.png | Bin .../NSFW/Anims}/lvl_30/frame_23.png | Bin .../NSFW/Anims}/lvl_30/frame_24.png | Bin .../NSFW/Anims}/lvl_30/frame_25.png | Bin .../NSFW/Anims}/lvl_30/frame_26.png | Bin .../NSFW/Anims}/lvl_30/frame_27.png | Bin .../NSFW/Anims}/lvl_30/frame_28.png | Bin .../NSFW/Anims}/lvl_30/frame_29.png | Bin .../NSFW/Anims}/lvl_30/frame_3.png | Bin .../NSFW/Anims}/lvl_30/frame_30.png | Bin .../NSFW/Anims}/lvl_30/frame_31.png | Bin .../NSFW/Anims}/lvl_30/frame_32.png | Bin .../NSFW/Anims}/lvl_30/frame_33.png | Bin .../NSFW/Anims}/lvl_30/frame_34.png | Bin .../NSFW/Anims}/lvl_30/frame_35.png | Bin .../NSFW/Anims}/lvl_30/frame_36.png | Bin .../NSFW/Anims}/lvl_30/frame_37.png | Bin .../NSFW/Anims}/lvl_30/frame_38.png | Bin .../NSFW/Anims}/lvl_30/frame_39.png | Bin .../NSFW/Anims}/lvl_30/frame_4.png | Bin .../NSFW/Anims}/lvl_30/frame_40.png | Bin .../NSFW/Anims}/lvl_30/frame_41.png | Bin .../NSFW/Anims}/lvl_30/frame_42.png | Bin .../NSFW/Anims}/lvl_30/frame_43.png | Bin .../NSFW/Anims}/lvl_30/frame_44.png | Bin .../NSFW/Anims}/lvl_30/frame_45.png | Bin .../NSFW/Anims}/lvl_30/frame_46.png | Bin .../NSFW/Anims}/lvl_30/frame_47.png | Bin .../NSFW/Anims}/lvl_30/frame_48.png | Bin .../NSFW/Anims}/lvl_30/frame_49.png | Bin .../NSFW/Anims}/lvl_30/frame_5.png | Bin .../NSFW/Anims}/lvl_30/frame_6.png | Bin .../NSFW/Anims}/lvl_30/frame_7.png | Bin .../NSFW/Anims}/lvl_30/frame_8.png | Bin .../NSFW/Anims}/lvl_30/frame_9.png | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.png | Bin .../NSFW/Anims}/lvl_4/frame_1.png | Bin .../NSFW/Anims}/lvl_4/frame_10.png | Bin .../NSFW/Anims}/lvl_4/frame_11.png | Bin .../NSFW/Anims}/lvl_4/frame_12.png | Bin .../NSFW/Anims}/lvl_4/frame_13.png | Bin .../NSFW/Anims}/lvl_4/frame_14.png | Bin .../NSFW/Anims}/lvl_4/frame_15.png | Bin .../NSFW/Anims}/lvl_4/frame_16.png | Bin .../NSFW/Anims}/lvl_4/frame_17.png | Bin .../NSFW/Anims}/lvl_4/frame_18.png | Bin .../NSFW/Anims}/lvl_4/frame_19.png | Bin .../NSFW/Anims}/lvl_4/frame_2.png | Bin .../NSFW/Anims}/lvl_4/frame_3.png | Bin .../NSFW/Anims}/lvl_4/frame_4.png | Bin .../NSFW/Anims}/lvl_4/frame_5.png | Bin .../NSFW/Anims}/lvl_4/frame_6.png | Bin .../NSFW/Anims}/lvl_4/frame_7.png | Bin .../NSFW/Anims}/lvl_4/frame_8.png | Bin .../NSFW/Anims}/lvl_4/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.png | Bin .../NSFW/Anims}/lvl_5/frame_1.png | Bin .../NSFW/Anims}/lvl_5/frame_10.png | Bin .../NSFW/Anims}/lvl_5/frame_11.png | Bin .../NSFW/Anims}/lvl_5/frame_12.png | Bin .../NSFW/Anims}/lvl_5/frame_13.png | Bin .../NSFW/Anims}/lvl_5/frame_14.png | Bin .../NSFW/Anims}/lvl_5/frame_15.png | Bin .../NSFW/Anims}/lvl_5/frame_16.png | Bin .../NSFW/Anims}/lvl_5/frame_17.png | Bin .../NSFW/Anims}/lvl_5/frame_18.png | Bin .../NSFW/Anims}/lvl_5/frame_19.png | Bin .../NSFW/Anims}/lvl_5/frame_2.png | Bin .../NSFW/Anims}/lvl_5/frame_20.png | Bin .../NSFW/Anims}/lvl_5/frame_21.png | Bin .../NSFW/Anims}/lvl_5/frame_22.png | Bin .../NSFW/Anims}/lvl_5/frame_23.png | Bin .../NSFW/Anims}/lvl_5/frame_24.png | Bin .../NSFW/Anims}/lvl_5/frame_25.png | Bin .../NSFW/Anims}/lvl_5/frame_26.png | Bin .../NSFW/Anims}/lvl_5/frame_27.png | Bin .../NSFW/Anims}/lvl_5/frame_3.png | Bin .../NSFW/Anims}/lvl_5/frame_4.png | Bin .../NSFW/Anims}/lvl_5/frame_5.png | Bin .../NSFW/Anims}/lvl_5/frame_6.png | Bin .../NSFW/Anims}/lvl_5/frame_7.png | Bin .../NSFW/Anims}/lvl_5/frame_8.png | Bin .../NSFW/Anims}/lvl_5/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.png | Bin .../NSFW/Anims}/lvl_6/frame_1.png | Bin .../NSFW/Anims}/lvl_6/frame_2.png | Bin .../NSFW/Anims}/lvl_6/frame_3.png | Bin .../NSFW/Anims}/lvl_6/frame_4.png | Bin .../NSFW/Anims}/lvl_6/frame_5.png | Bin .../NSFW/Anims}/lvl_6/frame_6.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.png | Bin .../NSFW/Anims}/lvl_7/frame_1.png | Bin .../NSFW/Anims}/lvl_7/frame_10.png | Bin .../NSFW/Anims}/lvl_7/frame_11.png | Bin .../NSFW/Anims}/lvl_7/frame_12.png | Bin .../NSFW/Anims}/lvl_7/frame_13.png | Bin .../NSFW/Anims}/lvl_7/frame_2.png | Bin .../NSFW/Anims}/lvl_7/frame_3.png | Bin .../NSFW/Anims}/lvl_7/frame_4.png | Bin .../NSFW/Anims}/lvl_7/frame_5.png | Bin .../NSFW/Anims}/lvl_7/frame_6.png | Bin .../NSFW/Anims}/lvl_7/frame_7.png | Bin .../NSFW/Anims}/lvl_7/frame_8.png | Bin .../NSFW/Anims}/lvl_7/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_7/meta.txt | 0 .../NSFW/Anims}/lvl_8/frame_0.png | Bin .../NSFW/Anims}/lvl_8/frame_1.png | Bin .../NSFW/Anims}/lvl_8/frame_2.png | Bin .../NSFW/Anims}/lvl_8/frame_3.png | Bin .../NSFW/Anims}/lvl_8/frame_4.png | Bin .../NSFW/Anims}/lvl_8/frame_5.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.png | Bin .../NSFW/Anims}/lvl_9/frame_1.png | Bin .../NSFW/Anims}/lvl_9/frame_2.png | Bin .../NSFW/Anims}/lvl_9/frame_3.png | Bin .../NSFW/Anims}/lvl_9/frame_4.png | Bin .../NSFW/Anims}/lvl_9/frame_5.png | Bin .../NSFW/Anims}/lvl_9/frame_6.png | Bin .../NSFW/Anims}/lvl_9/frame_7.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_9/meta.txt | 0 .../nsfw => custom/NSFW/Anims}/manifest.txt | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 0 -> 2610 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 0 -> 3376 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 0 -> 5390 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 0 -> 4224 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 0 -> 852 bytes .../Icons/Passport/passport_bad_46x49.png} | Bin .../Icons/Passport/passport_happy_46x49.png | Bin 0 -> 6373 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 0 -> 6373 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 0 -> 4862 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 0 -> 4882 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 0 -> 4466 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 0 -> 3798 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 0 -> 4092 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 0 -> 1864 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 0 -> 1895 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 0 -> 1874 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 0 -> 1863 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 0 -> 6876 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 0 -> 5422 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 0 -> 5122 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 0 -> 4719 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.png | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.png | Bin .../{sfw => }/L1_Cry_128x64/frame_1.png | Bin .../{sfw => }/L1_Cry_128x64/frame_2.png | Bin .../{sfw => }/L1_Cry_128x64/frame_3.png | Bin .../{sfw => }/L1_Cry_128x64/frame_4.png | Bin .../{sfw => }/L1_Cry_128x64/frame_5.png | Bin .../{sfw => }/L1_Cry_128x64/frame_6.png | Bin .../{sfw => }/L1_Cry_128x64/frame_7.png | Bin .../external/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.png | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.png | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.png | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.png | Bin .../{sfw => }/L1_Mods_128x64/frame_1.png | Bin .../{sfw => }/L1_Mods_128x64/frame_10.png | Bin .../{sfw => }/L1_Mods_128x64/frame_11.png | Bin .../{sfw => }/L1_Mods_128x64/frame_12.png | Bin .../{sfw => }/L1_Mods_128x64/frame_13.png | Bin .../{sfw => }/L1_Mods_128x64/frame_14.png | Bin .../{sfw => }/L1_Mods_128x64/frame_15.png | Bin .../{sfw => }/L1_Mods_128x64/frame_16.png | Bin .../{sfw => }/L1_Mods_128x64/frame_17.png | Bin .../{sfw => }/L1_Mods_128x64/frame_18.png | Bin .../{sfw => }/L1_Mods_128x64/frame_19.png | Bin .../{sfw => }/L1_Mods_128x64/frame_2.png | Bin .../{sfw => }/L1_Mods_128x64/frame_20.png | Bin .../{sfw => }/L1_Mods_128x64/frame_21.png | Bin .../{sfw => }/L1_Mods_128x64/frame_22.png | Bin .../{sfw => }/L1_Mods_128x64/frame_23.png | Bin .../{sfw => }/L1_Mods_128x64/frame_24.png | Bin .../{sfw => }/L1_Mods_128x64/frame_25.png | Bin .../{sfw => }/L1_Mods_128x64/frame_26.png | Bin .../{sfw => }/L1_Mods_128x64/frame_27.png | Bin .../{sfw => }/L1_Mods_128x64/frame_28.png | Bin .../{sfw => }/L1_Mods_128x64/frame_29.png | Bin .../{sfw => }/L1_Mods_128x64/frame_3.png | Bin .../{sfw => }/L1_Mods_128x64/frame_30.png | Bin .../{sfw => }/L1_Mods_128x64/frame_31.png | Bin .../{sfw => }/L1_Mods_128x64/frame_32.png | Bin .../{sfw => }/L1_Mods_128x64/frame_33.png | Bin .../{sfw => }/L1_Mods_128x64/frame_34.png | Bin .../{sfw => }/L1_Mods_128x64/frame_35.png | Bin .../{sfw => }/L1_Mods_128x64/frame_36.png | Bin .../{sfw => }/L1_Mods_128x64/frame_37.png | Bin .../{sfw => }/L1_Mods_128x64/frame_38.png | Bin .../{sfw => }/L1_Mods_128x64/frame_39.png | Bin .../{sfw => }/L1_Mods_128x64/frame_4.png | Bin .../{sfw => }/L1_Mods_128x64/frame_40.png | Bin .../{sfw => }/L1_Mods_128x64/frame_5.png | Bin .../{sfw => }/L1_Mods_128x64/frame_6.png | Bin .../{sfw => }/L1_Mods_128x64/frame_7.png | Bin .../{sfw => }/L1_Mods_128x64/frame_8.png | Bin .../{sfw => }/L1_Mods_128x64/frame_9.png | Bin .../{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.png | Bin .../{sfw => }/L1_Painting_128x64/frame_1.png | Bin .../{sfw => }/L1_Painting_128x64/frame_10.png | Bin .../{sfw => }/L1_Painting_128x64/frame_11.png | Bin .../{sfw => }/L1_Painting_128x64/frame_2.png | Bin .../{sfw => }/L1_Painting_128x64/frame_3.png | Bin .../{sfw => }/L1_Painting_128x64/frame_4.png | Bin .../{sfw => }/L1_Painting_128x64/frame_5.png | Bin .../{sfw => }/L1_Painting_128x64/frame_6.png | Bin .../{sfw => }/L1_Painting_128x64/frame_7.png | Bin .../{sfw => }/L1_Painting_128x64/frame_8.png | Bin .../{sfw => }/L1_Painting_128x64/frame_9.png | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.png | Bin .../{sfw => }/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{sfw => }/L1_Recording_128x51/frame_2.png | Bin .../{sfw => }/L1_Recording_128x51/frame_3.png | Bin .../{sfw => }/L1_Recording_128x51/frame_4.png | Bin .../{sfw => }/L1_Recording_128x51/frame_5.png | Bin .../{sfw => }/L1_Recording_128x51/frame_6.png | Bin .../{sfw => }/L1_Recording_128x51/frame_7.png | Bin .../{sfw => }/L1_Recording_128x51/frame_8.png | Bin .../{sfw => }/L1_Recording_128x51/frame_9.png | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.png | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.png | Bin .../{sfw => }/L1_Waves_128x50/frame_1.png | Bin .../{sfw => }/L1_Waves_128x50/frame_2.png | Bin .../{sfw => }/L1_Waves_128x50/frame_3.png | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.png | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.png | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.png | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.png | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{sfw => }/manifest.txt | 0 assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 2307 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 2307 -> 0 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 1416 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 1416 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 1177 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 1177 -> 0 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 1541 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 1541 -> 0 bytes assets/icons/Passport/passport_DB.png | Bin 852 -> 750 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 750 -> 0 bytes .../Passport/passport_bad2_46x49_sfw.png | Bin 1295 -> 0 bytes .../Passport/passport_bad3_46x49_sfw.png | Bin 1304 -> 0 bytes ...1_46x49_sfw.png => passport_bad_46x49.png} | Bin .../Passport/passport_happy2_46x49_sfw.png | Bin 1328 -> 0 bytes .../Passport/passport_happy3_46x49_sfw.png | Bin 1348 -> 0 bytes ...46x49_sfw.png => passport_happy_46x49.png} | Bin .../Passport/passport_okay2_46x49_sfw.png | Bin 1281 -> 0 bytes .../Passport/passport_okay3_46x49_sfw.png | Bin 1304 -> 0 bytes ..._46x49_sfw.png => passport_okay_46x49.png} | Bin .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 1421 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 1421 -> 0 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 1418 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 1418 -> 0 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 2681 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 2681 -> 0 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 3898 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 3898 -> 0 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 1690 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 1690 -> 0 bytes assets/icons/U2F/Auth_62x31.png | Bin 1864 -> 3761 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 3761 -> 0 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 1895 -> 3767 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 3767 -> 0 bytes assets/icons/U2F/Connected_62x31.png | Bin 1874 -> 3765 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 3765 -> 0 bytes assets/icons/U2F/Error_62x31.png | Bin 1863 -> 3751 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 3751 -> 0 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 2504 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 2504 -> 0 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 2459 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 2459 -> 0 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 2023 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 2023 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 2157 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 2157 -> 0 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.bm | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_1.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_2.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_3.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_4.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_5.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_6.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.bm | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.bm | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_14.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_15.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_16.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_17.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_18.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_19.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_20.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_21.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_22.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_23.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_24.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_25.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_26.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_27.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_28.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_29.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_30.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_31.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_32.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_33.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_34.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_35.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_36.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_37.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_38.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_39.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_40.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_1.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_10.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_11.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_2.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_3.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_4.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_5.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_6.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_7.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_8.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_9.bm | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../{sfw => }/L1_Read_books_128x64/frame_0.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_1.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_2.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_3.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_4.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_5.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_6.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_7.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_8.bm | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_1.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_10.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_11.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_2.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_3.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_4.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_5.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_6.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_7.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_8.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_9.bm | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.bm | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_1.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_2.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_3.bm | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.bm | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../{sfw => }/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_10.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.bm | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.bm | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.bm | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{sfw => }/manifest.txt | 0 .../Anims}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../PaxGod_TikTok_Marketing/frame_10.bm | Bin .../PaxGod_TikTok_Marketing/frame_11.bm | Bin .../PaxGod_TikTok_Marketing/frame_12.bm | Bin .../PaxGod_TikTok_Marketing/frame_13.bm | Bin .../PaxGod_TikTok_Marketing/frame_14.bm | Bin .../PaxGod_TikTok_Marketing/frame_15.bm | Bin .../PaxGod_TikTok_Marketing/frame_16.bm | Bin .../PaxGod_TikTok_Marketing/frame_17.bm | Bin .../PaxGod_TikTok_Marketing/frame_18.bm | Bin .../PaxGod_TikTok_Marketing/frame_19.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../PaxGod_TikTok_Marketing/frame_20.bm | Bin .../PaxGod_TikTok_Marketing/frame_21.bm | Bin .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 0 -> 472 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 0 -> 462 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 0 -> 487 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 0 -> 488 bytes .../Anims}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../NSFW/Anims}/lvl_1/frame_0.bm | Bin .../NSFW/Anims}/lvl_1/frame_1.bm | Bin .../NSFW/Anims}/lvl_1/frame_10.bm | Bin .../NSFW/Anims}/lvl_1/frame_11.bm | Bin .../NSFW/Anims}/lvl_1/frame_12.bm | Bin .../NSFW/Anims}/lvl_1/frame_13.bm | Bin .../NSFW/Anims}/lvl_1/frame_14.bm | Bin .../NSFW/Anims}/lvl_1/frame_15.bm | Bin .../NSFW/Anims}/lvl_1/frame_16.bm | Bin .../NSFW/Anims}/lvl_1/frame_17.bm | Bin .../NSFW/Anims}/lvl_1/frame_18.bm | Bin .../NSFW/Anims}/lvl_1/frame_19.bm | Bin .../NSFW/Anims}/lvl_1/frame_2.bm | Bin .../NSFW/Anims}/lvl_1/frame_20.bm | Bin .../NSFW/Anims}/lvl_1/frame_21.bm | Bin .../NSFW/Anims}/lvl_1/frame_22.bm | Bin .../NSFW/Anims}/lvl_1/frame_23.bm | Bin .../NSFW/Anims}/lvl_1/frame_24.bm | Bin .../NSFW/Anims}/lvl_1/frame_25.bm | Bin .../NSFW/Anims}/lvl_1/frame_26.bm | Bin .../NSFW/Anims}/lvl_1/frame_27.bm | Bin .../NSFW/Anims}/lvl_1/frame_28.bm | Bin .../NSFW/Anims}/lvl_1/frame_29.bm | Bin .../NSFW/Anims}/lvl_1/frame_3.bm | Bin .../NSFW/Anims}/lvl_1/frame_30.bm | Bin .../NSFW/Anims}/lvl_1/frame_4.bm | Bin .../NSFW/Anims}/lvl_1/frame_5.bm | Bin .../NSFW/Anims}/lvl_1/frame_6.bm | Bin .../NSFW/Anims}/lvl_1/frame_7.bm | Bin .../NSFW/Anims}/lvl_1/frame_8.bm | Bin .../NSFW/Anims}/lvl_1/frame_9.bm | Bin .../NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.bm | Bin .../NSFW/Anims}/lvl_10/frame_1.bm | Bin .../NSFW/Anims}/lvl_10/frame_10.bm | Bin .../NSFW/Anims}/lvl_10/frame_11.bm | Bin .../NSFW/Anims}/lvl_10/frame_12.bm | Bin .../NSFW/Anims}/lvl_10/frame_13.bm | Bin .../NSFW/Anims}/lvl_10/frame_14.bm | Bin .../NSFW/Anims}/lvl_10/frame_15.bm | Bin .../NSFW/Anims}/lvl_10/frame_16.bm | Bin .../NSFW/Anims}/lvl_10/frame_17.bm | Bin .../NSFW/Anims}/lvl_10/frame_18.bm | Bin .../NSFW/Anims}/lvl_10/frame_19.bm | Bin .../NSFW/Anims}/lvl_10/frame_2.bm | Bin .../NSFW/Anims}/lvl_10/frame_20.bm | Bin .../NSFW/Anims}/lvl_10/frame_21.bm | Bin .../NSFW/Anims}/lvl_10/frame_22.bm | Bin .../NSFW/Anims}/lvl_10/frame_23.bm | Bin .../NSFW/Anims}/lvl_10/frame_24.bm | Bin .../NSFW/Anims}/lvl_10/frame_25.bm | Bin .../NSFW/Anims}/lvl_10/frame_26.bm | Bin .../NSFW/Anims}/lvl_10/frame_27.bm | Bin .../NSFW/Anims}/lvl_10/frame_3.bm | Bin .../NSFW/Anims}/lvl_10/frame_4.bm | Bin .../NSFW/Anims}/lvl_10/frame_5.bm | Bin .../NSFW/Anims}/lvl_10/frame_6.bm | Bin .../NSFW/Anims}/lvl_10/frame_7.bm | Bin .../NSFW/Anims}/lvl_10/frame_8.bm | Bin .../NSFW/Anims}/lvl_10/frame_9.bm | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.bm | Bin .../NSFW/Anims}/lvl_11/frame_1.bm | Bin .../NSFW/Anims}/lvl_11/frame_10.bm | Bin .../NSFW/Anims}/lvl_11/frame_11.bm | Bin .../NSFW/Anims}/lvl_11/frame_12.bm | Bin .../NSFW/Anims}/lvl_11/frame_13.bm | Bin .../NSFW/Anims}/lvl_11/frame_14.bm | Bin .../NSFW/Anims}/lvl_11/frame_15.bm | Bin .../NSFW/Anims}/lvl_11/frame_16.bm | Bin .../NSFW/Anims}/lvl_11/frame_17.bm | Bin .../NSFW/Anims}/lvl_11/frame_18.bm | Bin .../NSFW/Anims}/lvl_11/frame_19.bm | Bin .../NSFW/Anims}/lvl_11/frame_2.bm | Bin .../NSFW/Anims}/lvl_11/frame_20.bm | Bin .../NSFW/Anims}/lvl_11/frame_21.bm | Bin .../NSFW/Anims}/lvl_11/frame_22.bm | Bin .../NSFW/Anims}/lvl_11/frame_23.bm | Bin .../NSFW/Anims}/lvl_11/frame_24.bm | Bin .../NSFW/Anims}/lvl_11/frame_25.bm | Bin .../NSFW/Anims}/lvl_11/frame_26.bm | Bin .../NSFW/Anims}/lvl_11/frame_27.bm | Bin .../NSFW/Anims}/lvl_11/frame_28.bm | Bin .../NSFW/Anims}/lvl_11/frame_29.bm | Bin .../NSFW/Anims}/lvl_11/frame_3.bm | Bin .../NSFW/Anims}/lvl_11/frame_30.bm | Bin .../NSFW/Anims}/lvl_11/frame_31.bm | Bin .../NSFW/Anims}/lvl_11/frame_32.bm | Bin .../NSFW/Anims}/lvl_11/frame_33.bm | Bin .../NSFW/Anims}/lvl_11/frame_34.bm | Bin .../NSFW/Anims}/lvl_11/frame_35.bm | Bin .../NSFW/Anims}/lvl_11/frame_36.bm | Bin .../NSFW/Anims}/lvl_11/frame_37.bm | Bin .../NSFW/Anims}/lvl_11/frame_38.bm | Bin .../NSFW/Anims}/lvl_11/frame_39.bm | Bin .../NSFW/Anims}/lvl_11/frame_4.bm | Bin .../NSFW/Anims}/lvl_11/frame_40.bm | Bin .../NSFW/Anims}/lvl_11/frame_41.bm | Bin .../NSFW/Anims}/lvl_11/frame_42.bm | Bin .../NSFW/Anims}/lvl_11/frame_43.bm | Bin .../NSFW/Anims}/lvl_11/frame_44.bm | Bin .../NSFW/Anims}/lvl_11/frame_45.bm | Bin .../NSFW/Anims}/lvl_11/frame_46.bm | Bin .../NSFW/Anims}/lvl_11/frame_47.bm | Bin .../NSFW/Anims}/lvl_11/frame_48.bm | Bin .../NSFW/Anims}/lvl_11/frame_49.bm | Bin .../NSFW/Anims}/lvl_11/frame_5.bm | Bin .../NSFW/Anims}/lvl_11/frame_6.bm | Bin .../NSFW/Anims}/lvl_11/frame_7.bm | Bin .../NSFW/Anims}/lvl_11/frame_8.bm | Bin .../NSFW/Anims}/lvl_11/frame_9.bm | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.bm | Bin .../NSFW/Anims}/lvl_12/frame_1.bm | Bin .../NSFW/Anims}/lvl_12/frame_10.bm | Bin .../NSFW/Anims}/lvl_12/frame_11.bm | Bin .../NSFW/Anims}/lvl_12/frame_12.bm | Bin .../NSFW/Anims}/lvl_12/frame_13.bm | Bin .../NSFW/Anims}/lvl_12/frame_14.bm | Bin .../NSFW/Anims}/lvl_12/frame_15.bm | Bin .../NSFW/Anims}/lvl_12/frame_2.bm | Bin .../NSFW/Anims}/lvl_12/frame_3.bm | Bin .../NSFW/Anims}/lvl_12/frame_4.bm | Bin .../NSFW/Anims}/lvl_12/frame_5.bm | Bin .../NSFW/Anims}/lvl_12/frame_6.bm | Bin .../NSFW/Anims}/lvl_12/frame_7.bm | Bin .../NSFW/Anims}/lvl_12/frame_8.bm | Bin .../NSFW/Anims}/lvl_12/frame_9.bm | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.bm | Bin .../NSFW/Anims}/lvl_13/frame_1.bm | Bin .../NSFW/Anims}/lvl_13/frame_10.bm | Bin .../NSFW/Anims}/lvl_13/frame_2.bm | Bin .../NSFW/Anims}/lvl_13/frame_3.bm | Bin .../NSFW/Anims}/lvl_13/frame_4.bm | Bin .../NSFW/Anims}/lvl_13/frame_5.bm | Bin .../NSFW/Anims}/lvl_13/frame_6.bm | Bin .../NSFW/Anims}/lvl_13/frame_7.bm | Bin .../NSFW/Anims}/lvl_13/frame_8.bm | Bin .../NSFW/Anims}/lvl_13/frame_9.bm | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.bm | Bin .../NSFW/Anims}/lvl_14/frame_1.bm | Bin .../NSFW/Anims}/lvl_14/frame_2.bm | Bin .../NSFW/Anims}/lvl_14/frame_3.bm | Bin .../NSFW/Anims}/lvl_14/frame_4.bm | Bin .../NSFW/Anims}/lvl_14/frame_5.bm | Bin .../NSFW/Anims}/lvl_14/frame_6.bm | Bin .../NSFW/Anims}/lvl_14/frame_7.bm | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.bm | Bin .../NSFW/Anims}/lvl_15/frame_1.bm | Bin .../NSFW/Anims}/lvl_15/frame_10.bm | Bin .../NSFW/Anims}/lvl_15/frame_11.bm | Bin .../NSFW/Anims}/lvl_15/frame_12.bm | Bin .../NSFW/Anims}/lvl_15/frame_13.bm | Bin .../NSFW/Anims}/lvl_15/frame_14.bm | Bin .../NSFW/Anims}/lvl_15/frame_15.bm | Bin .../NSFW/Anims}/lvl_15/frame_16.bm | Bin .../NSFW/Anims}/lvl_15/frame_17.bm | Bin .../NSFW/Anims}/lvl_15/frame_18.bm | Bin .../NSFW/Anims}/lvl_15/frame_19.bm | Bin .../NSFW/Anims}/lvl_15/frame_2.bm | Bin .../NSFW/Anims}/lvl_15/frame_20.bm | Bin .../NSFW/Anims}/lvl_15/frame_21.bm | Bin .../NSFW/Anims}/lvl_15/frame_3.bm | Bin .../NSFW/Anims}/lvl_15/frame_4.bm | Bin .../NSFW/Anims}/lvl_15/frame_5.bm | Bin .../NSFW/Anims}/lvl_15/frame_6.bm | Bin .../NSFW/Anims}/lvl_15/frame_7.bm | Bin .../NSFW/Anims}/lvl_15/frame_8.bm | Bin .../NSFW/Anims}/lvl_15/frame_9.bm | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.bm | Bin .../NSFW/Anims}/lvl_16/frame_1.bm | Bin .../NSFW/Anims}/lvl_16/frame_10.bm | Bin .../NSFW/Anims}/lvl_16/frame_11.bm | Bin .../NSFW/Anims}/lvl_16/frame_12.bm | Bin .../NSFW/Anims}/lvl_16/frame_13.bm | Bin .../NSFW/Anims}/lvl_16/frame_14.bm | Bin .../NSFW/Anims}/lvl_16/frame_15.bm | Bin .../NSFW/Anims}/lvl_16/frame_16.bm | Bin .../NSFW/Anims}/lvl_16/frame_17.bm | Bin .../NSFW/Anims}/lvl_16/frame_18.bm | Bin .../NSFW/Anims}/lvl_16/frame_19.bm | Bin .../NSFW/Anims}/lvl_16/frame_2.bm | Bin .../NSFW/Anims}/lvl_16/frame_20.bm | Bin .../NSFW/Anims}/lvl_16/frame_3.bm | Bin .../NSFW/Anims}/lvl_16/frame_4.bm | Bin .../NSFW/Anims}/lvl_16/frame_5.bm | Bin .../NSFW/Anims}/lvl_16/frame_6.bm | Bin .../NSFW/Anims}/lvl_16/frame_7.bm | Bin .../NSFW/Anims}/lvl_16/frame_8.bm | Bin .../NSFW/Anims}/lvl_16/frame_9.bm | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.bm | Bin .../NSFW/Anims}/lvl_17/frame_1.bm | Bin .../NSFW/Anims}/lvl_17/frame_10.bm | Bin .../NSFW/Anims}/lvl_17/frame_11.bm | Bin .../NSFW/Anims}/lvl_17/frame_12.bm | Bin .../NSFW/Anims}/lvl_17/frame_13.bm | Bin .../NSFW/Anims}/lvl_17/frame_14.bm | Bin .../NSFW/Anims}/lvl_17/frame_15.bm | Bin .../NSFW/Anims}/lvl_17/frame_16.bm | Bin .../NSFW/Anims}/lvl_17/frame_17.bm | Bin .../NSFW/Anims}/lvl_17/frame_18.bm | Bin .../NSFW/Anims}/lvl_17/frame_19.bm | Bin .../NSFW/Anims}/lvl_17/frame_2.bm | Bin .../NSFW/Anims}/lvl_17/frame_20.bm | Bin .../NSFW/Anims}/lvl_17/frame_21.bm | Bin .../NSFW/Anims}/lvl_17/frame_22.bm | Bin .../NSFW/Anims}/lvl_17/frame_23.bm | Bin .../NSFW/Anims}/lvl_17/frame_24.bm | Bin .../NSFW/Anims}/lvl_17/frame_25.bm | Bin .../NSFW/Anims}/lvl_17/frame_26.bm | Bin .../NSFW/Anims}/lvl_17/frame_27.bm | Bin .../NSFW/Anims}/lvl_17/frame_28.bm | Bin .../NSFW/Anims}/lvl_17/frame_29.bm | Bin .../NSFW/Anims}/lvl_17/frame_3.bm | Bin .../NSFW/Anims}/lvl_17/frame_30.bm | Bin .../NSFW/Anims}/lvl_17/frame_31.bm | Bin .../NSFW/Anims}/lvl_17/frame_4.bm | Bin .../NSFW/Anims}/lvl_17/frame_5.bm | Bin .../NSFW/Anims}/lvl_17/frame_6.bm | Bin .../NSFW/Anims}/lvl_17/frame_7.bm | Bin .../NSFW/Anims}/lvl_17/frame_8.bm | Bin .../NSFW/Anims}/lvl_17/frame_9.bm | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.bm | Bin .../NSFW/Anims}/lvl_18/frame_1.bm | Bin .../NSFW/Anims}/lvl_18/frame_10.bm | Bin .../NSFW/Anims}/lvl_18/frame_11.bm | Bin .../NSFW/Anims}/lvl_18/frame_12.bm | Bin .../NSFW/Anims}/lvl_18/frame_13.bm | Bin .../NSFW/Anims}/lvl_18/frame_14.bm | Bin .../NSFW/Anims}/lvl_18/frame_15.bm | Bin .../NSFW/Anims}/lvl_18/frame_16.bm | Bin .../NSFW/Anims}/lvl_18/frame_17.bm | Bin .../NSFW/Anims}/lvl_18/frame_18.bm | Bin .../NSFW/Anims}/lvl_18/frame_19.bm | Bin .../NSFW/Anims}/lvl_18/frame_2.bm | Bin .../NSFW/Anims}/lvl_18/frame_20.bm | Bin .../NSFW/Anims}/lvl_18/frame_21.bm | Bin .../NSFW/Anims}/lvl_18/frame_22.bm | Bin .../NSFW/Anims}/lvl_18/frame_3.bm | Bin .../NSFW/Anims}/lvl_18/frame_4.bm | Bin .../NSFW/Anims}/lvl_18/frame_5.bm | Bin .../NSFW/Anims}/lvl_18/frame_6.bm | Bin .../NSFW/Anims}/lvl_18/frame_7.bm | Bin .../NSFW/Anims}/lvl_18/frame_8.bm | Bin .../NSFW/Anims}/lvl_18/frame_9.bm | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.bm | Bin .../NSFW/Anims}/lvl_19/frame_1.bm | Bin .../NSFW/Anims}/lvl_19/frame_10.bm | Bin .../NSFW/Anims}/lvl_19/frame_11.bm | Bin .../NSFW/Anims}/lvl_19/frame_12.bm | Bin .../NSFW/Anims}/lvl_19/frame_13.bm | Bin .../NSFW/Anims}/lvl_19/frame_14.bm | Bin .../NSFW/Anims}/lvl_19/frame_15.bm | Bin .../NSFW/Anims}/lvl_19/frame_16.bm | Bin .../NSFW/Anims}/lvl_19/frame_17.bm | Bin .../NSFW/Anims}/lvl_19/frame_18.bm | Bin .../NSFW/Anims}/lvl_19/frame_19.bm | Bin .../NSFW/Anims}/lvl_19/frame_2.bm | Bin .../NSFW/Anims}/lvl_19/frame_20.bm | Bin .../NSFW/Anims}/lvl_19/frame_21.bm | Bin .../NSFW/Anims}/lvl_19/frame_3.bm | Bin .../NSFW/Anims}/lvl_19/frame_4.bm | Bin .../NSFW/Anims}/lvl_19/frame_5.bm | Bin .../NSFW/Anims}/lvl_19/frame_6.bm | Bin .../NSFW/Anims}/lvl_19/frame_7.bm | Bin .../NSFW/Anims}/lvl_19/frame_8.bm | Bin .../NSFW/Anims}/lvl_19/frame_9.bm | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.bm | Bin .../NSFW/Anims}/lvl_2/frame_1.bm | Bin .../NSFW/Anims}/lvl_2/frame_10.bm | Bin .../NSFW/Anims}/lvl_2/frame_11.bm | Bin .../NSFW/Anims}/lvl_2/frame_12.bm | Bin .../NSFW/Anims}/lvl_2/frame_13.bm | Bin .../NSFW/Anims}/lvl_2/frame_14.bm | Bin .../NSFW/Anims}/lvl_2/frame_2.bm | Bin .../NSFW/Anims}/lvl_2/frame_3.bm | Bin .../NSFW/Anims}/lvl_2/frame_4.bm | Bin .../NSFW/Anims}/lvl_2/frame_5.bm | Bin .../NSFW/Anims}/lvl_2/frame_6.bm | Bin .../NSFW/Anims}/lvl_2/frame_7.bm | Bin .../NSFW/Anims}/lvl_2/frame_8.bm | Bin .../NSFW/Anims}/lvl_2/frame_9.bm | Bin .../NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.bm | Bin .../NSFW/Anims}/lvl_20/frame_1.bm | Bin .../NSFW/Anims}/lvl_20/frame_10.bm | Bin .../NSFW/Anims}/lvl_20/frame_11.bm | Bin .../NSFW/Anims}/lvl_20/frame_12.bm | Bin .../NSFW/Anims}/lvl_20/frame_13.bm | Bin .../NSFW/Anims}/lvl_20/frame_2.bm | Bin .../NSFW/Anims}/lvl_20/frame_3.bm | Bin .../NSFW/Anims}/lvl_20/frame_4.bm | Bin .../NSFW/Anims}/lvl_20/frame_5.bm | Bin .../NSFW/Anims}/lvl_20/frame_6.bm | Bin .../NSFW/Anims}/lvl_20/frame_7.bm | Bin .../NSFW/Anims}/lvl_20/frame_8.bm | Bin .../NSFW/Anims}/lvl_20/frame_9.bm | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.bm | Bin .../NSFW/Anims}/lvl_21/frame_1.bm | Bin .../NSFW/Anims}/lvl_21/frame_2.bm | Bin .../NSFW/Anims}/lvl_21/frame_3.bm | Bin .../NSFW/Anims}/lvl_21/frame_4.bm | Bin .../NSFW/Anims}/lvl_21/frame_5.bm | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.bm | Bin .../NSFW/Anims}/lvl_22/frame_1.bm | Bin .../NSFW/Anims}/lvl_22/frame_10.bm | Bin .../NSFW/Anims}/lvl_22/frame_11.bm | Bin .../NSFW/Anims}/lvl_22/frame_12.bm | Bin .../NSFW/Anims}/lvl_22/frame_13.bm | Bin .../NSFW/Anims}/lvl_22/frame_14.bm | Bin .../NSFW/Anims}/lvl_22/frame_15.bm | Bin .../NSFW/Anims}/lvl_22/frame_16.bm | Bin .../NSFW/Anims}/lvl_22/frame_17.bm | Bin .../NSFW/Anims}/lvl_22/frame_18.bm | Bin .../NSFW/Anims}/lvl_22/frame_19.bm | Bin .../NSFW/Anims}/lvl_22/frame_2.bm | Bin .../NSFW/Anims}/lvl_22/frame_20.bm | Bin .../NSFW/Anims}/lvl_22/frame_21.bm | Bin .../NSFW/Anims}/lvl_22/frame_22.bm | Bin .../NSFW/Anims}/lvl_22/frame_23.bm | Bin .../NSFW/Anims}/lvl_22/frame_24.bm | Bin .../NSFW/Anims}/lvl_22/frame_25.bm | Bin .../NSFW/Anims}/lvl_22/frame_26.bm | Bin .../NSFW/Anims}/lvl_22/frame_27.bm | Bin .../NSFW/Anims}/lvl_22/frame_28.bm | Bin .../NSFW/Anims}/lvl_22/frame_29.bm | Bin .../NSFW/Anims}/lvl_22/frame_3.bm | Bin .../NSFW/Anims}/lvl_22/frame_30.bm | Bin .../NSFW/Anims}/lvl_22/frame_31.bm | Bin .../NSFW/Anims}/lvl_22/frame_32.bm | Bin .../NSFW/Anims}/lvl_22/frame_33.bm | Bin .../NSFW/Anims}/lvl_22/frame_34.bm | Bin .../NSFW/Anims}/lvl_22/frame_35.bm | Bin .../NSFW/Anims}/lvl_22/frame_36.bm | Bin .../NSFW/Anims}/lvl_22/frame_37.bm | Bin .../NSFW/Anims}/lvl_22/frame_38.bm | Bin .../NSFW/Anims}/lvl_22/frame_39.bm | Bin .../NSFW/Anims}/lvl_22/frame_4.bm | Bin .../NSFW/Anims}/lvl_22/frame_40.bm | Bin .../NSFW/Anims}/lvl_22/frame_41.bm | Bin .../NSFW/Anims}/lvl_22/frame_42.bm | Bin .../NSFW/Anims}/lvl_22/frame_43.bm | Bin .../NSFW/Anims}/lvl_22/frame_44.bm | Bin .../NSFW/Anims}/lvl_22/frame_45.bm | Bin .../NSFW/Anims}/lvl_22/frame_46.bm | Bin .../NSFW/Anims}/lvl_22/frame_47.bm | Bin .../NSFW/Anims}/lvl_22/frame_48.bm | Bin .../NSFW/Anims}/lvl_22/frame_49.bm | Bin .../NSFW/Anims}/lvl_22/frame_5.bm | Bin .../NSFW/Anims}/lvl_22/frame_50.bm | Bin .../NSFW/Anims}/lvl_22/frame_51.bm | Bin .../NSFW/Anims}/lvl_22/frame_52.bm | Bin .../NSFW/Anims}/lvl_22/frame_53.bm | Bin .../NSFW/Anims}/lvl_22/frame_54.bm | Bin .../NSFW/Anims}/lvl_22/frame_55.bm | Bin .../NSFW/Anims}/lvl_22/frame_56.bm | Bin .../NSFW/Anims}/lvl_22/frame_57.bm | Bin .../NSFW/Anims}/lvl_22/frame_58.bm | Bin .../NSFW/Anims}/lvl_22/frame_59.bm | Bin .../NSFW/Anims}/lvl_22/frame_6.bm | Bin .../NSFW/Anims}/lvl_22/frame_7.bm | Bin .../NSFW/Anims}/lvl_22/frame_8.bm | Bin .../NSFW/Anims}/lvl_22/frame_9.bm | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.bm | Bin .../NSFW/Anims}/lvl_23/frame_1.bm | Bin .../NSFW/Anims}/lvl_23/frame_10.bm | Bin .../NSFW/Anims}/lvl_23/frame_11.bm | Bin .../NSFW/Anims}/lvl_23/frame_12.bm | Bin .../NSFW/Anims}/lvl_23/frame_13.bm | Bin .../NSFW/Anims}/lvl_23/frame_14.bm | Bin .../NSFW/Anims}/lvl_23/frame_15.bm | Bin .../NSFW/Anims}/lvl_23/frame_16.bm | Bin .../NSFW/Anims}/lvl_23/frame_2.bm | Bin .../NSFW/Anims}/lvl_23/frame_3.bm | Bin .../NSFW/Anims}/lvl_23/frame_4.bm | Bin .../NSFW/Anims}/lvl_23/frame_5.bm | Bin .../NSFW/Anims}/lvl_23/frame_6.bm | Bin .../NSFW/Anims}/lvl_23/frame_7.bm | Bin .../NSFW/Anims}/lvl_23/frame_8.bm | Bin .../NSFW/Anims}/lvl_23/frame_9.bm | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.bm | Bin .../NSFW/Anims}/lvl_24/frame_1.bm | Bin .../NSFW/Anims}/lvl_24/frame_10.bm | Bin .../NSFW/Anims}/lvl_24/frame_11.bm | Bin .../NSFW/Anims}/lvl_24/frame_12.bm | Bin .../NSFW/Anims}/lvl_24/frame_13.bm | Bin .../NSFW/Anims}/lvl_24/frame_14.bm | Bin .../NSFW/Anims}/lvl_24/frame_15.bm | Bin .../NSFW/Anims}/lvl_24/frame_16.bm | Bin .../NSFW/Anims}/lvl_24/frame_17.bm | Bin .../NSFW/Anims}/lvl_24/frame_18.bm | Bin .../NSFW/Anims}/lvl_24/frame_19.bm | Bin .../NSFW/Anims}/lvl_24/frame_2.bm | Bin .../NSFW/Anims}/lvl_24/frame_20.bm | Bin .../NSFW/Anims}/lvl_24/frame_21.bm | Bin .../NSFW/Anims}/lvl_24/frame_22.bm | Bin .../NSFW/Anims}/lvl_24/frame_23.bm | Bin .../NSFW/Anims}/lvl_24/frame_24.bm | Bin .../NSFW/Anims}/lvl_24/frame_25.bm | Bin .../NSFW/Anims}/lvl_24/frame_26.bm | Bin .../NSFW/Anims}/lvl_24/frame_27.bm | Bin .../NSFW/Anims}/lvl_24/frame_28.bm | Bin .../NSFW/Anims}/lvl_24/frame_29.bm | Bin .../NSFW/Anims}/lvl_24/frame_3.bm | Bin .../NSFW/Anims}/lvl_24/frame_4.bm | Bin .../NSFW/Anims}/lvl_24/frame_5.bm | Bin .../NSFW/Anims}/lvl_24/frame_6.bm | Bin .../NSFW/Anims}/lvl_24/frame_7.bm | Bin .../NSFW/Anims}/lvl_24/frame_8.bm | Bin .../NSFW/Anims}/lvl_24/frame_9.bm | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.bm | Bin .../NSFW/Anims}/lvl_25/frame_1.bm | Bin .../NSFW/Anims}/lvl_25/frame_10.bm | Bin .../NSFW/Anims}/lvl_25/frame_11.bm | Bin .../NSFW/Anims}/lvl_25/frame_12.bm | Bin .../NSFW/Anims}/lvl_25/frame_13.bm | Bin .../NSFW/Anims}/lvl_25/frame_14.bm | Bin .../NSFW/Anims}/lvl_25/frame_15.bm | Bin .../NSFW/Anims}/lvl_25/frame_16.bm | Bin .../NSFW/Anims}/lvl_25/frame_17.bm | Bin .../NSFW/Anims}/lvl_25/frame_18.bm | Bin .../NSFW/Anims}/lvl_25/frame_19.bm | Bin .../NSFW/Anims}/lvl_25/frame_2.bm | Bin .../NSFW/Anims}/lvl_25/frame_20.bm | Bin .../NSFW/Anims}/lvl_25/frame_21.bm | Bin .../NSFW/Anims}/lvl_25/frame_22.bm | Bin .../NSFW/Anims}/lvl_25/frame_23.bm | Bin .../NSFW/Anims}/lvl_25/frame_24.bm | Bin .../NSFW/Anims}/lvl_25/frame_25.bm | Bin .../NSFW/Anims}/lvl_25/frame_26.bm | Bin .../NSFW/Anims}/lvl_25/frame_27.bm | Bin .../NSFW/Anims}/lvl_25/frame_28.bm | Bin .../NSFW/Anims}/lvl_25/frame_29.bm | Bin .../NSFW/Anims}/lvl_25/frame_3.bm | Bin .../NSFW/Anims}/lvl_25/frame_30.bm | Bin .../NSFW/Anims}/lvl_25/frame_31.bm | Bin .../NSFW/Anims}/lvl_25/frame_32.bm | Bin .../NSFW/Anims}/lvl_25/frame_33.bm | Bin .../NSFW/Anims}/lvl_25/frame_34.bm | Bin .../NSFW/Anims}/lvl_25/frame_35.bm | Bin .../NSFW/Anims}/lvl_25/frame_4.bm | Bin .../NSFW/Anims}/lvl_25/frame_5.bm | Bin .../NSFW/Anims}/lvl_25/frame_6.bm | Bin .../NSFW/Anims}/lvl_25/frame_7.bm | Bin .../NSFW/Anims}/lvl_25/frame_8.bm | Bin .../NSFW/Anims}/lvl_25/frame_9.bm | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.bm | Bin .../NSFW/Anims}/lvl_26/frame_1.bm | Bin .../NSFW/Anims}/lvl_26/frame_10.bm | Bin .../NSFW/Anims}/lvl_26/frame_11.bm | Bin .../NSFW/Anims}/lvl_26/frame_2.bm | Bin .../NSFW/Anims}/lvl_26/frame_3.bm | Bin .../NSFW/Anims}/lvl_26/frame_4.bm | Bin .../NSFW/Anims}/lvl_26/frame_5.bm | Bin .../NSFW/Anims}/lvl_26/frame_6.bm | Bin .../NSFW/Anims}/lvl_26/frame_7.bm | Bin .../NSFW/Anims}/lvl_26/frame_8.bm | Bin .../NSFW/Anims}/lvl_26/frame_9.bm | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.bm | Bin .../NSFW/Anims}/lvl_27/frame_1.bm | Bin .../NSFW/Anims}/lvl_27/frame_10.bm | Bin .../NSFW/Anims}/lvl_27/frame_11.bm | Bin .../NSFW/Anims}/lvl_27/frame_12.bm | Bin .../NSFW/Anims}/lvl_27/frame_13.bm | Bin .../NSFW/Anims}/lvl_27/frame_14.bm | Bin .../NSFW/Anims}/lvl_27/frame_15.bm | Bin .../NSFW/Anims}/lvl_27/frame_16.bm | Bin .../NSFW/Anims}/lvl_27/frame_17.bm | Bin .../NSFW/Anims}/lvl_27/frame_18.bm | Bin .../NSFW/Anims}/lvl_27/frame_19.bm | Bin .../NSFW/Anims}/lvl_27/frame_2.bm | Bin .../NSFW/Anims}/lvl_27/frame_20.bm | Bin .../NSFW/Anims}/lvl_27/frame_21.bm | Bin .../NSFW/Anims}/lvl_27/frame_3.bm | Bin .../NSFW/Anims}/lvl_27/frame_4.bm | Bin .../NSFW/Anims}/lvl_27/frame_5.bm | Bin .../NSFW/Anims}/lvl_27/frame_6.bm | Bin .../NSFW/Anims}/lvl_27/frame_7.bm | Bin .../NSFW/Anims}/lvl_27/frame_8.bm | Bin .../NSFW/Anims}/lvl_27/frame_9.bm | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.bm | Bin .../NSFW/Anims}/lvl_28/frame_1.bm | Bin .../NSFW/Anims}/lvl_28/frame_2.bm | Bin .../NSFW/Anims}/lvl_28/frame_3.bm | Bin .../NSFW/Anims}/lvl_28/frame_4.bm | Bin .../NSFW/Anims}/lvl_28/frame_5.bm | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.bm | Bin .../NSFW/Anims}/lvl_29/frame_1.bm | Bin .../NSFW/Anims}/lvl_29/frame_10.bm | Bin .../NSFW/Anims}/lvl_29/frame_11.bm | Bin .../NSFW/Anims}/lvl_29/frame_12.bm | Bin .../NSFW/Anims}/lvl_29/frame_13.bm | Bin .../NSFW/Anims}/lvl_29/frame_14.bm | Bin .../NSFW/Anims}/lvl_29/frame_15.bm | Bin .../NSFW/Anims}/lvl_29/frame_16.bm | Bin .../NSFW/Anims}/lvl_29/frame_17.bm | Bin .../NSFW/Anims}/lvl_29/frame_18.bm | Bin .../NSFW/Anims}/lvl_29/frame_19.bm | Bin .../NSFW/Anims}/lvl_29/frame_2.bm | Bin .../NSFW/Anims}/lvl_29/frame_20.bm | Bin .../NSFW/Anims}/lvl_29/frame_21.bm | Bin .../NSFW/Anims}/lvl_29/frame_22.bm | Bin .../NSFW/Anims}/lvl_29/frame_23.bm | Bin .../NSFW/Anims}/lvl_29/frame_24.bm | Bin .../NSFW/Anims}/lvl_29/frame_25.bm | Bin .../NSFW/Anims}/lvl_29/frame_26.bm | Bin .../NSFW/Anims}/lvl_29/frame_27.bm | Bin .../NSFW/Anims}/lvl_29/frame_28.bm | Bin .../NSFW/Anims}/lvl_29/frame_29.bm | Bin .../NSFW/Anims}/lvl_29/frame_3.bm | Bin .../NSFW/Anims}/lvl_29/frame_30.bm | Bin .../NSFW/Anims}/lvl_29/frame_31.bm | Bin .../NSFW/Anims}/lvl_29/frame_32.bm | Bin .../NSFW/Anims}/lvl_29/frame_33.bm | Bin .../NSFW/Anims}/lvl_29/frame_34.bm | Bin .../NSFW/Anims}/lvl_29/frame_35.bm | Bin .../NSFW/Anims}/lvl_29/frame_36.bm | Bin .../NSFW/Anims}/lvl_29/frame_37.bm | Bin .../NSFW/Anims}/lvl_29/frame_38.bm | Bin .../NSFW/Anims}/lvl_29/frame_39.bm | Bin .../NSFW/Anims}/lvl_29/frame_4.bm | Bin .../NSFW/Anims}/lvl_29/frame_40.bm | Bin .../NSFW/Anims}/lvl_29/frame_41.bm | Bin .../NSFW/Anims}/lvl_29/frame_42.bm | Bin .../NSFW/Anims}/lvl_29/frame_43.bm | Bin .../NSFW/Anims}/lvl_29/frame_44.bm | Bin .../NSFW/Anims}/lvl_29/frame_45.bm | Bin .../NSFW/Anims}/lvl_29/frame_46.bm | Bin .../NSFW/Anims}/lvl_29/frame_47.bm | Bin .../NSFW/Anims}/lvl_29/frame_48.bm | Bin .../NSFW/Anims}/lvl_29/frame_49.bm | Bin .../NSFW/Anims}/lvl_29/frame_5.bm | Bin .../NSFW/Anims}/lvl_29/frame_50.bm | Bin .../NSFW/Anims}/lvl_29/frame_51.bm | Bin .../NSFW/Anims}/lvl_29/frame_6.bm | Bin .../NSFW/Anims}/lvl_29/frame_7.bm | Bin .../NSFW/Anims}/lvl_29/frame_8.bm | Bin .../NSFW/Anims}/lvl_29/frame_9.bm | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.bm | Bin .../NSFW/Anims}/lvl_3/frame_1.bm | Bin .../NSFW/Anims}/lvl_3/frame_10.bm | Bin .../NSFW/Anims}/lvl_3/frame_11.bm | Bin .../NSFW/Anims}/lvl_3/frame_12.bm | Bin .../NSFW/Anims}/lvl_3/frame_13.bm | Bin .../NSFW/Anims}/lvl_3/frame_14.bm | Bin .../NSFW/Anims}/lvl_3/frame_2.bm | Bin .../NSFW/Anims}/lvl_3/frame_3.bm | Bin .../NSFW/Anims}/lvl_3/frame_4.bm | Bin .../NSFW/Anims}/lvl_3/frame_5.bm | Bin .../NSFW/Anims}/lvl_3/frame_6.bm | Bin .../NSFW/Anims}/lvl_3/frame_7.bm | Bin .../NSFW/Anims}/lvl_3/frame_8.bm | Bin .../NSFW/Anims}/lvl_3/frame_9.bm | Bin .../NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.bm | Bin .../NSFW/Anims}/lvl_30/frame_1.bm | Bin .../NSFW/Anims}/lvl_30/frame_10.bm | Bin .../NSFW/Anims}/lvl_30/frame_11.bm | Bin .../NSFW/Anims}/lvl_30/frame_12.bm | Bin .../NSFW/Anims}/lvl_30/frame_13.bm | Bin .../NSFW/Anims}/lvl_30/frame_14.bm | Bin .../NSFW/Anims}/lvl_30/frame_15.bm | Bin .../NSFW/Anims}/lvl_30/frame_16.bm | Bin .../NSFW/Anims}/lvl_30/frame_17.bm | Bin .../NSFW/Anims}/lvl_30/frame_18.bm | Bin .../NSFW/Anims}/lvl_30/frame_19.bm | Bin .../NSFW/Anims}/lvl_30/frame_2.bm | Bin .../NSFW/Anims}/lvl_30/frame_20.bm | Bin .../NSFW/Anims}/lvl_30/frame_21.bm | Bin .../NSFW/Anims}/lvl_30/frame_22.bm | Bin .../NSFW/Anims}/lvl_30/frame_23.bm | Bin .../NSFW/Anims}/lvl_30/frame_24.bm | Bin .../NSFW/Anims}/lvl_30/frame_25.bm | Bin .../NSFW/Anims}/lvl_30/frame_26.bm | Bin .../NSFW/Anims}/lvl_30/frame_27.bm | Bin .../NSFW/Anims}/lvl_30/frame_28.bm | Bin .../NSFW/Anims}/lvl_30/frame_29.bm | Bin .../NSFW/Anims}/lvl_30/frame_3.bm | Bin .../NSFW/Anims}/lvl_30/frame_30.bm | Bin .../NSFW/Anims}/lvl_30/frame_31.bm | Bin .../NSFW/Anims}/lvl_30/frame_32.bm | Bin .../NSFW/Anims}/lvl_30/frame_33.bm | Bin .../NSFW/Anims}/lvl_30/frame_34.bm | Bin .../NSFW/Anims}/lvl_30/frame_35.bm | Bin .../NSFW/Anims}/lvl_30/frame_36.bm | Bin .../NSFW/Anims}/lvl_30/frame_37.bm | Bin .../NSFW/Anims}/lvl_30/frame_38.bm | Bin .../NSFW/Anims}/lvl_30/frame_39.bm | Bin .../NSFW/Anims}/lvl_30/frame_4.bm | Bin .../NSFW/Anims}/lvl_30/frame_40.bm | Bin .../NSFW/Anims}/lvl_30/frame_41.bm | Bin .../NSFW/Anims}/lvl_30/frame_42.bm | Bin .../NSFW/Anims}/lvl_30/frame_43.bm | Bin .../NSFW/Anims}/lvl_30/frame_44.bm | Bin .../NSFW/Anims}/lvl_30/frame_45.bm | Bin .../NSFW/Anims}/lvl_30/frame_46.bm | Bin .../NSFW/Anims}/lvl_30/frame_47.bm | Bin .../NSFW/Anims}/lvl_30/frame_48.bm | Bin .../NSFW/Anims}/lvl_30/frame_49.bm | Bin .../NSFW/Anims}/lvl_30/frame_5.bm | Bin .../NSFW/Anims}/lvl_30/frame_6.bm | Bin .../NSFW/Anims}/lvl_30/frame_7.bm | Bin .../NSFW/Anims}/lvl_30/frame_8.bm | Bin .../NSFW/Anims}/lvl_30/frame_9.bm | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.bm | Bin .../NSFW/Anims}/lvl_4/frame_1.bm | Bin .../NSFW/Anims}/lvl_4/frame_10.bm | Bin .../NSFW/Anims}/lvl_4/frame_11.bm | Bin .../NSFW/Anims}/lvl_4/frame_12.bm | Bin .../NSFW/Anims}/lvl_4/frame_13.bm | Bin .../NSFW/Anims}/lvl_4/frame_14.bm | Bin .../NSFW/Anims}/lvl_4/frame_15.bm | Bin .../NSFW/Anims}/lvl_4/frame_16.bm | Bin .../NSFW/Anims}/lvl_4/frame_17.bm | Bin .../NSFW/Anims}/lvl_4/frame_18.bm | Bin .../NSFW/Anims}/lvl_4/frame_19.bm | Bin .../NSFW/Anims}/lvl_4/frame_2.bm | Bin .../NSFW/Anims}/lvl_4/frame_3.bm | Bin .../NSFW/Anims}/lvl_4/frame_4.bm | Bin .../NSFW/Anims}/lvl_4/frame_5.bm | Bin .../NSFW/Anims}/lvl_4/frame_6.bm | Bin .../NSFW/Anims}/lvl_4/frame_7.bm | Bin .../NSFW/Anims}/lvl_4/frame_8.bm | Bin .../NSFW/Anims}/lvl_4/frame_9.bm | Bin .../NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.bm | Bin .../NSFW/Anims}/lvl_5/frame_1.bm | Bin .../NSFW/Anims}/lvl_5/frame_10.bm | Bin .../NSFW/Anims}/lvl_5/frame_11.bm | Bin .../NSFW/Anims}/lvl_5/frame_12.bm | Bin .../NSFW/Anims}/lvl_5/frame_13.bm | Bin .../NSFW/Anims}/lvl_5/frame_14.bm | Bin .../NSFW/Anims}/lvl_5/frame_15.bm | Bin .../NSFW/Anims}/lvl_5/frame_16.bm | Bin .../NSFW/Anims}/lvl_5/frame_17.bm | Bin .../NSFW/Anims}/lvl_5/frame_18.bm | Bin .../NSFW/Anims}/lvl_5/frame_19.bm | Bin .../NSFW/Anims}/lvl_5/frame_2.bm | Bin .../NSFW/Anims}/lvl_5/frame_20.bm | Bin .../NSFW/Anims}/lvl_5/frame_21.bm | Bin .../NSFW/Anims}/lvl_5/frame_22.bm | Bin .../NSFW/Anims}/lvl_5/frame_23.bm | Bin .../NSFW/Anims}/lvl_5/frame_24.bm | Bin .../NSFW/Anims}/lvl_5/frame_25.bm | Bin .../NSFW/Anims}/lvl_5/frame_26.bm | Bin .../NSFW/Anims}/lvl_5/frame_27.bm | Bin .../NSFW/Anims}/lvl_5/frame_3.bm | Bin .../NSFW/Anims}/lvl_5/frame_4.bm | Bin .../NSFW/Anims}/lvl_5/frame_5.bm | Bin .../NSFW/Anims}/lvl_5/frame_6.bm | Bin .../NSFW/Anims}/lvl_5/frame_7.bm | Bin .../NSFW/Anims}/lvl_5/frame_8.bm | Bin .../NSFW/Anims}/lvl_5/frame_9.bm | Bin .../NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.bm | Bin .../NSFW/Anims}/lvl_6/frame_1.bm | Bin .../NSFW/Anims}/lvl_6/frame_2.bm | Bin .../NSFW/Anims}/lvl_6/frame_3.bm | Bin .../NSFW/Anims}/lvl_6/frame_4.bm | Bin .../NSFW/Anims}/lvl_6/frame_5.bm | Bin .../NSFW/Anims}/lvl_6/frame_6.bm | Bin .../NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.bm | Bin .../NSFW/Anims}/lvl_7/frame_1.bm | Bin .../NSFW/Anims}/lvl_7/frame_10.bm | Bin .../NSFW/Anims}/lvl_7/frame_11.bm | Bin .../NSFW/Anims}/lvl_7/frame_12.bm | Bin .../NSFW/Anims}/lvl_7/frame_13.bm | Bin .../NSFW/Anims}/lvl_7/frame_2.bm | Bin .../NSFW/Anims}/lvl_7/frame_3.bm | Bin .../NSFW/Anims}/lvl_7/frame_4.bm | Bin .../NSFW/Anims}/lvl_7/frame_5.bm | Bin .../NSFW/Anims}/lvl_7/frame_6.bm | Bin .../NSFW/Anims}/lvl_7/frame_7.bm | Bin .../NSFW/Anims}/lvl_7/frame_8.bm | Bin .../NSFW/Anims}/lvl_7/frame_9.bm | Bin .../NSFW/Anims}/lvl_7/meta.txt | 2 +- .../NSFW/Anims}/lvl_8/frame_0.bm | Bin .../NSFW/Anims}/lvl_8/frame_1.bm | Bin .../NSFW/Anims}/lvl_8/frame_2.bm | Bin .../NSFW/Anims}/lvl_8/frame_3.bm | Bin .../NSFW/Anims}/lvl_8/frame_4.bm | Bin .../NSFW/Anims}/lvl_8/frame_5.bm | Bin .../NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.bm | Bin .../NSFW/Anims}/lvl_9/frame_1.bm | Bin .../NSFW/Anims}/lvl_9/frame_2.bm | Bin .../NSFW/Anims}/lvl_9/frame_3.bm | Bin .../NSFW/Anims}/lvl_9/frame_4.bm | Bin .../NSFW/Anims}/lvl_9/frame_5.bm | Bin .../NSFW/Anims}/lvl_9/frame_6.bm | Bin .../NSFW/Anims}/lvl_9/frame_7.bm | Bin .../NSFW/Anims}/lvl_9/meta.txt | 0 .../NSFW/Anims}/manifest.txt | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 0 -> 480 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 0 -> 325 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 0 -> 513 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 0 -> 375 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 0 -> 286 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 0 -> 297 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 0 -> 525 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 0 -> 525 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 0 -> 502 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 0 -> 352 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 0 -> 458 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 0 -> 257 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 0 -> 684 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 0 -> 609 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 0 -> 481 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 0 -> 482 bytes scripts/assets.py | 35 +-------- 2238 files changed, 50 insertions(+), 125 deletions(-) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_52.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_53.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_54.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_55.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_56.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_57.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_58.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_59.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png create mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png rename assets/{icons/Passport/flipper.png => dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png} (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{sfw => }/manifest.txt (100%) delete mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png delete mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png delete mode 100644 assets/icons/Passport/passport_DB_sfw.png delete mode 100644 assets/icons/Passport/passport_bad2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png rename assets/icons/Passport/{passport_bad1_46x49_sfw.png => passport_bad_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_happy2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png rename assets/icons/Passport/{passport_happy1_46x49_sfw.png => passport_happy_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_okay2_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png rename assets/icons/Passport/{passport_okay1_46x49_sfw.png => passport_okay_46x49.png} (100%) delete mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png delete mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png delete mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png delete mode 100644 assets/icons/U2F/Auth_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connected_62x31_sfw.png delete mode 100644 assets/icons/U2F/Error_62x31_sfw.png delete mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png delete mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png delete mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/manifest.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.bm (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index bf7addba9..025246faa 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,9 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* +!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 2c707bbf1..d32eee382 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index ad889cd1c..51bed3835 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define MAX_NAME_LEN 64 @@ -28,7 +28,6 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -49,7 +48,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -68,7 +67,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -78,7 +77,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 162faf2f1..35a6ce1d9 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index eecd32702..fc1c5c4fa 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 5239d72d5..14e06cdf2 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define TAG "AnimationManager" @@ -580,7 +580,7 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); } else { if(stats.level <= 20) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a80a958a3..4263dc0a4 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom Manifest selected"); + FURI_LOG_I(TAG, "Custom manifest selected"); } else { use_asset_pack = false; } @@ -48,14 +48,8 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); - if(xtreme_settings->nsfw_mode) { - furi_string_cat_str(anim_dir, "/nsfw"); - FURI_LOG_I(TAG, "NSFW Manifest selected"); - } else { - furi_string_cat_str(anim_dir, "/sfw"); - FURI_LOG_I(TAG, "SFW Manifest selected"); - } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); + FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index e4f5e788f..c2149253c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index f0430de5d..450c5af23 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index ecab8c333..6fd26138b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 3323cf1df..ba5e65ca0 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,15 +3,6 @@ #include #include -static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); - XTREME_SETTINGS()->nsfw_mode = value; - app->settings_changed = true; - app->assets_changed = true; -} - static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -172,11 +163,6 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); - item = variable_item_list_add( - var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); - variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); - item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 0f6ab998d..b40f272e4 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -17,54 +17,30 @@ void XTREME_ASSETS_LOAD() { xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - if(xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = - &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; - } + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; if(xtreme_settings->asset_pack[0] == '\0') return; + xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; FileInfo info; FuriString* path = furi_string_alloc(); const char* pack = xtreme_settings->asset_pack; diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index c49f5b590..6904a36ab 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,6 +7,7 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { + bool is_nsfw; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index ee0ec5583..13bb574ad 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,7 +18,6 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; - bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_1/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_10/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_11/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_12/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_13/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_14/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_15/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_16/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_17/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_18/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_19/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_2/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_20/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_21/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_52.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_52.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_53.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_53.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_54.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_54.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_55.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_55.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_56.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_56.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_57.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_57.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_58.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_58.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_59.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_59.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_22/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_23/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_24/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_25/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_26/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_27/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_28/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_29/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_3/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_30/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_4/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_5/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_6/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_7/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_8/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_9/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/dolphin/external/nsfw/manifest.txt b/assets/dolphin/custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/dolphin/external/nsfw/manifest.txt rename to assets/dolphin/custom/NSFW/Anims/manifest.txt diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f60598005d41ff05a9f763f42f1a6b7900150e33 GIT binary patch literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e80fea5bd7f694549da1b45e9f3db056342a95cc GIT binary patch literal 3376 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f GIT binary patch literal 4224 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png new file mode 100644 index 0000000000000000000000000000000000000000..98d7e15f9c105cc36c1f6d3473fd915a9687968b GIT binary patch literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..2528ebc95d79335975511a384c70c010d476a8c0 GIT binary patch literal 4862 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..fef503263fd962534e7e5543954612bf6c1faefd GIT binary patch literal 4882 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png new file mode 100644 index 0000000000000000000000000000000000000000..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png new file mode 100644 index 0000000000000000000000000000000000000000..d1164c59483a2242769327b5b2c9ac01acb83186 GIT binary patch literal 3798 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 M07*qoM6N<$g8AuxBLDyZ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..a48c5330e85c2135866fe99bcb6f3534d06c6d53 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1010ca9da500055615c00359ea7536df4caa01 GIT binary patch literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1 GIT binary patch literal 5122 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 0000000000000000000000000000000000000000..90b589ff8e9f18b28d7fd75ca525a1db24381b06 GIT binary patch literal 4719 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt b/assets/dolphin/external/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt b/assets/dolphin/external/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt b/assets/dolphin/external/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/sfw/manifest.txt b/assets/dolphin/external/manifest.txt similarity index 100% rename from assets/dolphin/external/sfw/manifest.txt rename to assets/dolphin/external/manifest.txt diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index f60598005d41ff05a9f763f42f1a6b7900150e33..34068c300386e0c88e45b40a8995b0a4360ed79f 100644 GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG delta 2594 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 Ef}^?D!2kdN diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png deleted file mode 100644 index 34068c300386e0c88e45b40a8995b0a4360ed79f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index e80fea5bd7f694549da1b45e9f3db056342a95cc..089aaed83507431993a76ca25d32fdd9664c1c84 100644 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj delta 3373 zcmV+|4bt+63$PlHB!3BTNLh0L01m?d01m?e$8V@)000c)Nkl&t5+qUh~Pe1kb%P+qiK74rb;>F*5^UY#^&k-Zg_3PJHty=Yq zFTU8bXOFKHD^`5{_1BY=lP|d7g8wg|8#it|{q)oSk$;3MC!KUsnKES#7c3F$+_|$5 zjUGLE>(;HY1Al$%t+&oNX+F+izdCY*~yzmoHy_{PD+s_uY3M|Hd0{xbn*{zXT}sdf>o;`Sa%|B_*Mx zL4yX}yLTTvc<}V;)0Zq+^4@#z{psY{+1clwd+wE2UVkYKTD59rwh<#n#0a!mvt}QB z@Btj(fB!v*vu4eL(s{35z3#c^o}-UGIz2u8%rnnixpL**ci&yFUOiII&CMM)Y*<1< zf>F2KcH2`=J@xIk-~L_#RIgsWWXY0c%a%pQC6`=s(M1=<2=ubcF8k=CkB&X|*h7a7 z?b@~Loqu=UsaCDpX{Vhwb?Ve<)25}SrZ#Tem?u2^@WU^@_~L7?y|!xADzbL76`}`- z#~**ZQKLr47TlkH`l;aLix)3G=9pu)Y}xYu`|p4F;fFhR>@ZJ^Krg=dVvO?9N|h=t zUAh#PzMgQx34S8~#~yp^<(FSR@x&A1AmYlED}U$YB*$dDo9#*G7-QHbp1 z$&=v@e(ZSZrI&b|Xk2~u)g~-my7W;;9aX=6{TP9Yg|ODFS@Y+ge?IcaBN2ebv(G-; zPcmT8pg~otRLRTB6Yk3|zdR)+rBuO2Ahu9aS*xq z{PWM>dh4xEKKW$(_U&JI;RR_S)6>$@`hWNDk1!4pNXx?dDmQX%`?wD zgJWE}F5*z`-Mg0+cJ12rzd%Fzj^<#BJc!`F{`zadD)^HKUU9`0PCohM6Awlwlq^Yg zUV* z7QD{zgfdi7+zc2nK;paS(MKPp)aK2bj~qD?$CO>JTsaFMk1#OgBDbnrw{D3NC9puX z5L3BT2~O;lDK0L~im_7gqWlfQ5LKG)8sr?T2HtejO_70IlgH8G zwbx$D@*oBs&B%^SQe>bJg{)b#rhiVIIt70kJ9cbhVxnwN@w6$DBhbwAu>yo;C{dD+ zE&OIq;5gl9pM56HNndyk@6e$`Ax(*&qrqp2;LoqT^2+Slv%^IQh|gn2jT+_a%$YOW zwQCoCl4>-qTnR!(Muz42WM|=54z*&%3dMpYfAh^ZQqwY;G-;x?v82Mgr++^NT2R3r zdGO%D88c=?uJBY#A{@ioV`8sLRq{t|#gGEvHXw8>TH}f)G zm^IUD;XtLxpVAo)O_N8e9O0|nlo>2jY4%m~A#Wgd-l9bdD4D_{4X;q4!r|%ggTiz-Qhz~0C?uZhqRzvE z*@`wwFQ5H3mLEVY+3@mM$^`ImCK>f@{IH1H>Su!pi>= zP$f?FljdMC5la_naDS=rjyvv9nyHQ5P#);;DK;=ctbdcvdQ&;hCW*n#CZq>8!b)+; zboqe1^pw8}l$d}JcdGZ5@WH}|l+x!CrL@JdaG!VHc_ORiXbatGRX9^)jm25Vjvb$P z;t6!xA#nq?qvVpWB=alP78z*7o}5@ZFI3YhuT7gaAAkHY!hc+Z20ox$w{EQv=s#R% zJ-i(P3v+VkopI7FnX%Q^-#&zfP=c0|2dTXp&Aeq>fO%Igsa-y)g ztDJubI%(2kjei<7L|?=SHxvaVfn`XjdHftuzq%P#!KFp`D3vR*0{0Mf$UO9iR~tnD z8rgY4>|EQX2URC8yzoL7Wro@lYM{8o0YorxmabhQJ7m)UvGPdrP$A6O@W7kG8OObW1Jc2Ib7K7Tr zu^^BH3bb=*p}qb zsLOO-D<&c?ldKJ?2AZE7^?^F9z4?d4H?U}o{zjm7#u^UHS-po5mQ+Oma6-nO2*Wc% z9g-Hp8h>sON_s7qf4DR3-HZyatH=YdT50Py98l0LHO6FONo)@S)rceN9vHil=qH;E z1n@}u^LV0ET{?B@l$4aj#i&^u#VkGh?YG}H5PC~RCqPj05c_(oQZE>SA>trAF%m7% zh&sVdNJvl{@kDc%UV5p@#E*Q1JCrLp>_Q0?e}9B1ru0fGQiNJ)RCYrk=&-uS19UmSe=stCN3=;Q}Lz**G z5GmCOWQJ?CcrXF5FckFe-CG30`!hgUFBSQek+B#)1Oj9mF3O>C1`9E!8$Nuv$TC-| z9e*=s387=b1bl;kBp$g(eqJm6HK^HgLJQxwdSA zpf73LwyjwA?Aa3;&O4AeBSjYXro1B6>kYM}5)URJZnZZr@vmLYjT5x6=ieni@e+GJn#Snvm(i~i$ z?j&dqC5O<4GXn)jXQRb&e*gXV`wZ8a6w|PS_%~4Ni}LkTW@aX~n3N5M*vTaOcQsmk zhYT6=;DZmkh#P$9r^s}B{=tdd!++;VTx#d!~nU=>-tVTZKuVL@%;NPmC8dA7R3 zePoZ#9Sz7N8RiBlym|BHyu3WB)oTi!)O5ex;#qu6{)JciX=!QpC-^3n`1p8ClbMPb z7tPy7Pm%=g^qHEPDz8P^MSAB!f(sve*I0!=$2g*=N-`RN#wbC=S#Tc^(SIQSKsIp) zY6;XboS4x45=(=IRI8<^IDf`3aLUMB2e!1jBa(^_os>e4sX!DE4GP>; z6yBlgcwh>CM72n@9za!K@`#K6MRW209LSK-@QIqa1RX1w4oc;M5NBj$*f+Zvw$xHe zp;D9FV|ZZ4D4=}?X(p=vtXC%mw556kMpX)J0Jr%kpGhJ$kP-%s00000NkvXXu0mjf D!U>8P diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 93a7ad79cd95cdfe7789366edd8fadb3355e467c..46f559f65f11194c94c15bb7f195a1d72ef2a295 100644 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 5390 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 diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png deleted file mode 100644 index 46f559f65f11194c94c15bb7f195a1d72ef2a295..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..1783531285bed514517fc0821501e718359dd765 100644 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 4224 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 E4{AO|uK)l5 diff --git a/assets/icons/Passport/passport_DB.png b/assets/icons/Passport/passport_DB.png index f813f2afd345e57924b6a46aaf2172f6823c67de..69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d 100644 GIT binary patch delta 706 zcmV;z0zLiI2JQurIDZ15Nkl;5(?#D zv~htv4nV-d9K@^)0%me<5#Ize&B1}Y*n?0E8-aj*5C9jnJbwWQ!k@UwfQ>*v=8?-P zj+ngT2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${ z1wa7+6aWPPPyiGFY+3Yxqo6!F~AV8 ziy!4bb0@qb0)L%rnJVx9FxfTB%C85YsXYQ1GA_C0Ux~>NW z;gaYrjejUTz>+kl(V56PIstBbs(?Vete;J!O8L2_L}#3UxB|w#F_ik}$qCS#ChG^) z*q|Q2*W0kY+^i-e5l>`}jgG^zpv2kGLf+wzflj@H9aiY0Tpc`_otNG&=#FeMs_Rvk z+9&36QnT#*Fm)eL7-zH&v@fcMpn5_28XQPmE<2ao?RLN4hdxF3GRGTwggg&ZvO}6` oC%mhx)7!$^)2FsIeteJM4=9genX?&?VE_OC07*qoM6N<$f_@u2jQ{`u delta 809 zcmV+^1J?ZR1=I$RIDZ2MNkl8%vq7p(O{Y^mj-MMz&IRWW8dlklb>M}APO&ha6JbE))xS3fLx{D2mT$PcjCsp6u75dCa(gJ zg$7PRSPT#B04M+ofC8WZC;$q80-yjW00cn+PyqIVdff}UEd^jN2tW-$4FI43C;)&0 zpa1|0fC2z00DlSqpa3WUfC8WZ01AKt04M+o=ok0&h4Aeu;6Acq%a`(~0jL3}0Z;%G z@Nf=r@h+O=aF-1JajVbpY;e=0xBM_mlE#+d@(I zXy`|>Bpb)H<`DiDYv&AilG_Q`jQyv4b+;EGjywBwI@!=Wze7mT)a63NR-^sKE^VoI z)R?ikoq$ccROX3yh8r`V&u!>kr%92RWrT-uFLDyYA=T)9ZL^C4M!sP{{#ip~od{G5 z!KOYGM1SiRGrZ}8zEHd`^&M`hr8p^-6^ch+P4@%l#%Nb* z_9w1klb8`Z29rAYwoV*V_sU}`x$^;k2=>c}On+r(1iqr(p}lcf8 z`1(t)=;A?jh;Tj}4!hm%BL%xZ_zo+ZoQUJ`xZm%$+imK*R977gDhrX7tlO~0!6c@x noFc(1Z$gauzl;~WM{(gdOXY*sI7{g=00000NkvXXu0mjfG$wR{ diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png deleted file mode 100644 index 69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 zcmVpHph-kQRCwC$T+5QeAQ1GhYL)A4uO4&snE(GU*N|mHiNMSd0(qFE z`(PmjgiO;j1C|`x4u=k=;C=#a8nOrU3@w?y-76AbKix{SWoYQ^C9rJ{#u5tUV6<_8 zJPts>!W_h`3<73yZV}%EGtI$)yV!$J3>$%feGmW_v^)U_!k@UwfQ>*v=8?-Pj+ngT z2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${1wa7+ z6aWPPPyiGFY+3Yxqo6!F~AV8iy!4b zb0@qb0-yjWVBZ9MdfKp60@m_3)t?S2FL}LQ&*$@32VMB~p=(a3)A4vT1r1$TG7DPu zzlDDZjMuVQ!9IYw&TQ1H)>JZ5IzBrzzfUy~t?;a%Y`)S4`l0TW+{Qt|W>)~MXDrNG zypRUgj(IsKeM$G_sX1*$BO~ZU3vAiexK_5GZX0PFkpNZ!ZPv{xY!|v@9*>8aI-Rkk zfCvHa_OIn;7hsPj$ij>$fF+F1hpstx{T+JqADSi&i0?iJTWB0r7@oLNYcQw}#-;%?Odn!uXXn1NGwwDdducXilkH|T3sRc7oXHN^otex` zx|<+gYM{iXSQ}A_6)Dn|`cV5&MN!i>u~u!cO@*R~lwP10iV7+ejJTf3W@8^h2WHMW z|M|cF|Nh&Ir@FV))vm8a5Tq{A5lzF@4_|7n4}SOE9DEKg4Q?#!_L`bolx%>ssiq9j zL_rz=X&|X1L*IcAg80VtOxDdNyF|q-_$5!rUn*D-jUb`ck|imFz(r*+pc`TA_b*Oj zsIG>wJ}yZntq92J9iukr9qrC2ql1c|Vy&CeP)URa3c!`nQXy|RVkwL**%jg56B8J^ zq~Z>Su?nedGKEG=8=$oDEeAQQ#=HZn?T? zpq@oZHiz9X2B}tJC|JqliebZ9&J@fUQIafz@{>fN;03m%?YL?1KaEwjoy>>@h%|7_ zVOxO((Kb}UFnRZPXBk1k zC6459PE|OC=Y#+$D^)wI;Tb*{V50$=4Ymg|S27e( z^{qqS%UJ5ZSTSM)$u;eaY38eKkjj~^>Euicr5QhoKAn^l-S8wWUX?0AM}e*H0BYPe z3+PgsMST?t0TswH3yUq#8jZ6or{IF1ayYLBfJO#cn$ckR6});+`YL@2B`^%D%0^Tu zUgH8v0GBAB;grBKcu-aVF7YH6)Hq&c84@EPMZ)WxdpfBqg6-*TR|W$fRz?dLaGq>9 zIQw7fc?AyLnp9UNRxX#}_W9`_Uq+CcM-$PuOlkhaJ9G8Z=p&KIa#;^Qu&uV`_(MOx z-gw!?$2OjN()aN#!o((qUVBD;Fx$UpYHfd0XU)a0YfKzf6rOfXRcytG#ws-q!wyIYd^)Aa|e~?ZxbMHO( zobP<+HBWaY+c(tQQG+1JhWO@43a(!GQrCIlclUh%18}+Bjb_{~Gw0?d8z3RolmQyo zr5=z1l3E=68Uzr;Gp?mGZYI$oDyHt0$~xYHZb54V3A7e0N$CeJDuW))2x5z~pJ1q_ z2C;4~K_;v)=+!ol*r019nN~*n6+y*X??VFx5d!GImC%AdU^rqSh%MoYa93L+BC!H&ILn!WIU@>^MNnRn(Dia)OWKZ`0{_!kRoh7yEkLAzV-DF0 zEJ&`gY7CQibw_1I$VPn7)?ihnfrzOL>A-N~kstYg#DHcuBM=At{3xdkwyy^ov($CUN4u)T`SFcE4rB9&*hGA9N zhziB$IG^IfB?{zlN?;k>FDn3-c#`wyI9_EL5+fi*qTD%GbW&9W+q1k~84P$>87*MI zd9vZ)@P{U!fJ3*gvm+fXl}d2?(A=r*2(o5lJQ7M5zJ2AT_}b6V^`7a{=!f+aKwW#{ zK#*+ubL{$0C$8n$k^m}!+ z4aqGxKUFwYO9kHiYvbahk39Cg+BCCo%lQ|M%n6$g@0tAQ^Oob^{JwL>IUGgu*GwPc zUM;p;E48 fV|H=hUc`f(xyf_n$$93k^1mt`O-5$gvSa@Nx4y<4 diff --git a/assets/icons/Passport/passport_bad1_46x49_sfw.png b/assets/icons/Passport/passport_bad_46x49.png similarity index 100% rename from assets/icons/Passport/passport_bad1_46x49_sfw.png rename to assets/icons/Passport/passport_bad_46x49.png diff --git a/assets/icons/Passport/passport_happy2_46x49_sfw.png b/assets/icons/Passport/passport_happy2_46x49_sfw.png deleted file mode 100644 index f64e770e5a038162d1ceae38d04ff043319ed581..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1328 zcmaJ>Yitx%6rNHagjluO2sA~8L5mM&=dm-pJCklnyW7Xw?z-8ow1|+-+_|%zwDWLg z*4=JQ4T2E_{h_2)G0`ZMkTi{u5Qw0Lw#J|dQHh8c!L(6Iq@h|9+oV|Uw%y7P!Xz{I z-gD1)&Uaq3Cmw4kSy8?M!?2P_V>p3U4|IFeIR98utqxMUo{T%Nuc)W*Lg+D25|xTJ#Dc$Ki_)f!x`O zDkj49i_Xv~NOZWaB~nx-lksG{9@9=yj35XU%~C8&A`Q~%He4x78qWHHg)nr0ty!*S z8hGBKB%5hBNFb^UG3Zt_x@6dJ7Bhu%Mr9?7VmvgZ>-oUuwH-GB|EFWHw{0D zm3rIM@%c21+AS#f6e!CaDz?C?EXi>^AO%6;Nx$NQDjnchuZqH7z$-VUZ=p|-1chN0 z*oc7ftGo~RNQr?e$q1a649EbIlAq=SD(_b~FHKO0B9-r)n>wi=LhYH~E)51cER7a4 z&^*~_aK^uDI){etu6T2@Zfd*YjtfxXp%eGRQg&lXYD z>C^k^p0%q=)2sVF6f4_;?|fQ5nD}e+WMi(s`rs$KPR)#LId=cV_R~+^*cMxxJJdgV zw0&8_+g)6A`PQ$~Hy$Y)&plLe;h6CKop*03e)#I^J9|&;h*gA2hyRHmn118qjZ-DT zYs5z{4S%Iqzp~Og+&k7g_(c1f%Bs?t%uxEZ{r1o=vmcyD_N6DLe{8kRrvkelywvq; z`Q^g{dk0@NX5Y;``_suk`^t}iYXe6{)aRPFZu@fLk9C(o@0F3?Ce_;EL2>Mdvm3V0 z_HUjky{>&ex-K?+CR4f6soHSh^7(%sEv3pv->1q>-LVRrpo*u*!||g0KP*xg3lG+| Gzx*E~RoOxS diff --git a/assets/icons/Passport/passport_happy3_46x49_sfw.png b/assets/icons/Passport/passport_happy3_46x49_sfw.png deleted file mode 100644 index 7aef17674336e71cb1cdf323c1fb039e0e627d33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1348 zcmaJ>eQXnD7{7&~lejEf6dZ`TCJQE*5i}V=Gtg{i3Pxj)$VR4uj%5Br(2>QcxJ9tO-PXw;3zyuz@B6&Z z?|FXD*S*$I_h|8o)hiGLDGt_15x6?wOBF4H-?y&BYT;6D`y1`erf#QY3m{(2Q~(-` z%S|8xWUYP2H^7Y`%eswdqum$|iK-cQ$T=NHCZ2?71aWW5BxN-QY*YbFM#6(l4~<}` zp?R>UxG)(``arW$(_w+l9d%K)Bc=)(wrL~k&WO-J9N03NiMJ$DV#b5b*%jeFCnhj- zPQ{LSuz6CA;Re)aS^(u86t0paiSmL&lNDK2lnp3N(iB0m1jXVcDKdh{vgpEtL3fs> zixDZX;0&HTShH;>MS@7D(~dObFs&wn5(I%DX@aJ4sDY>26Skbe6RGui3ld1FmXWj# zGlAwT%8J=)doW0KK8AQQ99}e>NG)Uv=8VY5NrG~aL_D4gY)(66N5KCymefu~+mnEZ zfRx#4sjwjW`aBpW@Ai&zija+1ZyB&Ea*JfDt#OdBgOUe>HxA9vM4bc*$0-`F0Gh{H zMo@8?BRQPYR8HkN!AUA=-p*2ZUSj9~!%3{G+DlP>pNr)J66584924*d=;}N+m`K@j zLIru>2K2pv_1zXL`Ya&ZrWG~KmV6sDG@G`WYBrN7%{WN(p|GqPiJYV|SEc!&C14qC zKnqxA9Gy$EXe>d&sR2b{VX*~Tr*W3$R9p}=4(Bx|&`B3dGdc`^9{kf_iiuqr*cMf-r|D(OMvukwvy8`o1`tw&u9$(pU5#95~ z%oTI>8uxp$)MLN!$|u8Ts>&uiuO7ZWBpiL98lP5}6NArIJQJ=spqB+Br+<8N>Vw_S z_Y<{yCf_|@e7Vx~+0Lm4rC5)>;nUic6RxqR?aGg??}}}We<5Oh`o+G7&-IS&?LRe} z-r_uRFyy2dJyS4Wz?o~hpirfB!b{SC`cogKU3Sa;{l$NW9Rh4&#f XzZTxR@26EIx&KeP|nH7{9gXN~Cj8-Povj-o(x6YJvi`=jjO?tS0q zeSXjLd%kXOXGd$jZ>*tz1;sM_Qd!4eFl>lMknpC0Ehz)QLuJsfn-T2l$x|4r zs}byJHc2M!D9Gwv^_75cz!GpAYygr=OsBo+n6}plKXx;O?;LNd??=*HtZuz*QXG_H@fc z%N8Zs8uB6-q*{r=u#?F}!=}5CDVQ^&AlU@vCkewS2R5(mdMWTfjU~0+^so(x6mYE} zM}Y;=)>XkUd3SbHR)lPXryLztMaqemGGqYLONbE+-uP8r6*LmaEGw(H3}_B#0=$At zEXm@ms;~;j@5raV{)DohoSTw}2aWT-$McF_+7N=XP*o5hNlBs~I zZyowxz_NG53Q-40p5>%1D_3oU&aCBGZq~9;ARjzSX5S z@A;>FOUxLP5U06J+c1lS4&rqjjy=>tKRJ^ds_R>{c8{$}HYhrX*>`{7SxW0ObGYd=rz{>|;) zcDd&GWRr6A;8aunL+7upZ+znLY-x|L5bAlUr}giN1Mc~myY_rmr*V6W_KxGvY}@kA zrz^ML*p=RQVps7>VtDoM@An>K4i85A-!0{Cta)91@|#yO%V&g!>02KZH?Da5R9nNw zM=qSXxay1X?wu!p9KG=E>G9bk|C)onm&TrzuNNN}x&Gpx4SmR=iQ4_OTeDx5e@cm1 KhdAESxAQ++sKGP< diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png deleted file mode 100644 index e65da5b0e586ab706b40263b637f0fd3647ac5d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ diff --git a/assets/icons/Passport/passport_okay1_46x49_sfw.png b/assets/icons/Passport/passport_okay_46x49.png similarity index 100% rename from assets/icons/Passport/passport_okay1_46x49_sfw.png rename to assets/icons/Passport/passport_okay_46x49.png diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index 2528ebc95d79335975511a384c70c010d476a8c0..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 100644 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 4862 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 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index fef503263fd962534e7e5543954612bf6c1faefd..380a970d9004cba5520560fd9aa24aa42924e2a1 100644 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 4882 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 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..34199910945376f054daa0c1738d7e64dc410421 100644 GIT binary patch literal 2681 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 Ef`SCamH+?% diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 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 M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/icons/Settings/Cry_dolph_55x52_sfw.png b/assets/icons/Settings/Cry_dolph_55x52_sfw.png deleted file mode 100644 index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3898 zcmbVPcT`hbvp+Nil-@xk1i?Z{LK7mPg%T75LRS$&2oNB}5K0gc3yLC5np6=`L<5$K zf=E+Az)+-$6zL!Wh9bRv;oj?g?~l8__0~IUowH}}J-?aX%$`|mpIbJk&G~qxc>w_6 zvp|`kS>timtI5s5`bs6(^a21c5|2dMSRj!gGKJ)Y_s0Q1NN<*1uyf*yxc=Y@PQcN) zXYNI+{}lkNK<91{bjcOG3t+Ab3LDl%M)5j|iV0<+9BQw2@uIS#Mskjnc^lo1Iuq@1 zPUojwf5EFuq0HIYjV~J&lbMs7EwomF2q9smgo#{VKo=dU2k()Kvqsf6Rz|Rkp@uj? zf)kAQ-duG6Fvmd)38y|c*kpS~0@wj^m)arW`r(~xV~a-v4Qloc zqCkTr@Z9bXPhKDi0Q_lT#$ezHA29RFLZ1uhDNO8_0D5u{6mkPGY(Ux(^F!>0Zvl6Q zD`*w=n)g5s-4LwCSyRjw;qqGDoIRqF3kZsJju$X=2O`ppRhj^h4m*H6DALRUvgF8t zwpR}ox{)KxP;5XABe^;CRJ(>~S@LJ;;Dx!N5&4n#{x6I%5=RBSc*ek)sTm3)s7VgX zmi+)Az1H?_TgM=Na$;`KvB>+i#8p)|a0j+^)F8&It&fZe{kmjgNP8kn7o= z2UhzPo&|ax0iVG&SB*ZNoHM+iL)W5dI-7SW2*rRPDnlH|&h~1ud zzEx%ewyLMSK_{5VHztn=PhD1@mrL2=Net!#=r^ouPQTv!is4@q{*#S8n%}uS2rvv^ zRJ|j;F98tr)>Nmh06=2(TAZOW8_=AZKMDX9-zCpIPLivaf;naK3uZA)Ocz3+&RULfT3A`<<3VCF zHQ5*@_aXM^2$31?^VbF6`(KTZH+QylMm|=YQ792E=XT<`b{+QqFgWqCb%|oA@LFw% z2mD36vl(6Kr~E!L<1d2$b-^OJ;6YT*pVIUn9vH%`lWV%uDp z-O2qV3V#2L1tBNM24Z)!P((8@U9mYs9LdO6&FlJ{j`zl|_&}zpe{d1{d_n0uGOPFj!5}QU6d-2ER}+!Sw7`00)CE+LL|uPaOoFo_Dn;yo$n$ zj`xQ4R`q(!Qf9?xr+b-!N=0lpH*Q?6ZZTOL{77ufnkzXLl>o8npew8TF( z&}P0w_?5RL@q^jTY?C_<)#TQWm-2_{VCS0RvETjf;mg|Qt`9#JV)&FN)cWMLS>zoq zQ~G(+dsHh#Lf?x{I^aRq{D$lI)5Cb+)%)^m^XtZ{jy5OxrF)gPm2^;>ni^F4={@wu znONPfOuuYQ)z^x?&6voCrkQmoqqU>!Q+iXv7+fh8So6o>&HbIMYTa0gh9~}(YT}SN0;n~y|9_h8Qz)nnLV?*%a~=wWx{f+ zY|{Z@+3pk66JZo;U{jDkxneqhSo1?+ZQ+M0CvO?=2LV}&S`v>=#WRFcgHk2z2nDwb zi1$Mo$>Y*L%iCXdEahdq&FC-sO6VMDLeyP)x?!c3A=Bvk0l_r-HMinCa-nH7t01G` z{bKwiQB^u*p}E)FHl!hy6Iys8{U<%^KpfD-kj+3eN74S zru+J%{joC{Wf5gh%C3amTs56f6{dhv`J#6Ha|;Fn$eWYy*+B9xotT$%-so&xo`(t;&tbwS7@qc2e)| zM3!Unvd+MXOUQMf8`frlA2oQa(aN+0sk?6!7Ofmjy1&s|zh3yNs+*LVl$nR-!>5!e ze}R@BDYf=eR!-y_qfHP#$t2$Vv@)i!7O>bS<{xROh>dhfI0(MEF3Nd9Za|cHG5>7xylkGN*m1B>(T}>P<@GQvA zI&FaHOzWquSZ=2Ix!Sr`dLvu24J4>PElb^28W+D!#<|W_kbi+!Jo*$%PZc4G5$56K ztzp!&v!rR<3S-tuubBGFYWhSQv*Yr*Z%tRgm(?$7uJ>t`%#SlNr9`EH17(WkORGW4 zvl79)KP~%RL*qi8ZC3Se{n)!M@Z)mH_6uAMVQAI8#=Z4}ivW|K=VtbH(oVBg6h0Xo&eon?)^smzZVD^1=E_xwIpjhTAraZR^%owTf=R%9g32agJ+;Os(A z+harhu)3aL1AUNQ2%KerfTLnSAq0P706YW%{?jg;CGRdnz@R@>sD23WU!2{qO$ba=6$*uG=)yHML4QABmOF~47aVP3 z_O~VnS4j0Sdn@nBW4i z6g-)VClNur7BL>AKq>;vqWV`11hSRYKZc0`e`ku7Ge`)A41uXZAq2v1V1H@{P|>*m zq4BTU0roU94uZx7kOC=K)`55_{06g<_un1uDzezXttqVQ0}vll+s?n?O?6F>+R)=B zFb$**3}%Wn)>7BjHPMFYXdsPE%(QiXV=agQR16V|`|TUg^8Gg!`CqYcBn5||k|_2h zlK<~Au<;>LNdZ12GDuxrO$&6y3WLQHcUO+?p3+}Io8Tz;Ae^Teg+u`TnPxctUnuWKDPU{wCBG_`5^SrDXDDWsPryOAx% z8Qb*4J4V#bug_E`4$vj4JhIE)WvY#0Z6=Ogt`Q3w6`1fCi~DkUb-dj(=?%y0vCrfm zJnG7Iw>w1ai@*ZiKV0Jrg(|w1gpw{4q0d<=?Kun`O-ejQ8A;&nJdlp?YKe)^eww~& z_*RZrH{)=4+|xO8-gQRoLv%;cVVY;>)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index dd220bb65104666cb19752a96fe716cc44dffb68..40f094ac9ba758ea06838c2bb8376cf6f28f8050 100644 GIT binary patch literal 3761 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 delta 1860 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 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 495e8ab55c1e2dabb547151c2319f321f4af13bb..68c48c0e68142548919d6a4b02e40b48a243b04e 100644 GIT binary patch literal 3767 zcmaJ@c|26@-#)fNSrR5BW5iQgW`^N*QF&1S0K}{@ z<~ZJH#Cw#41$ggBZpK#Rpm%ERm8u{5b*UVy<_v)2fkBAW_%^;c9MGWU&>#&o z>;fL!KlKm=5&^)Ebepnj`0o?@&{UD$_XLo@x5X}dq?z7sYz;sNp2Fq#WtcmM%+rCm%F7GdQd~{MxVBi~!m%=_xV-$w*08*@+n!uxZ^6 z0P@T2uQv3IU{{kT8g=4|-R&>%+2vjZCyH~0ks$)j!DUboTFE&+Ny-{XL4 z-+~iBuM^-su>Cx=2Eh7{I<_S9A3F)@K~Qny~MD{88fgjzGX9Y+mz* z{I+;N(n|-yS^|KC>g8Aybv~daCGQIWRD9ia@_r)7rcM|D%(E{Xcx<|L`zvX7jd;c@ z#flmbKVgfx$@aZ9hLUDmM#6WC-r1FGD(6sx>=u0XSU^5|N7|5N$t$>?*!ZB-$qSQo z(3aD?vR?11xFRBG+914>pp1;%;U3csX%z;g#t1?P7@eEpdEkji>0^+$4U9bg0yCd@ zSB%P>CPP|3`lQK9TweniaeeK2g%v#U!joeHANT`IUffrbzh0N^-_F#La1Bq+@okqc z*@Fsyo|E5x;`BAVyjs01aTKrX6st>A#2*TjQK-ox zaE15sM}_a45%Slg_w7nlL!_2#gWZwE zad=C%+IQtW%2}$X{u1BmmVntS-gtB5sHn1P9Kw2=wJ)X*qnT%)XVf7{&ahFNjH5Ju z9ua;L&IYH%m&QrN+1*@id^1uijeHw9Y$KAh7nhePby$~VJSuqiHo`15`@yl6f~*h1 z4|!|>CzsHly@l|zF)Ua zj89zp5wud&EcV=#KTigq4z8upJKlhh=9XLKwtjJiDE8z^gk|;>Os)g5v`#KsS+)*An#G%SF`NV@HGFT`x}SKmJY^Fo zw5kvYJ!@l+_J82X_Kf$;96Qs(Ctg=PIHTW_aky2vRjl=_rhulgW^tEKp=V)O;i2A; z-l|^rX~wkd^kgqLfSzfRsgb#Wq#&09M9L4AA1jY2?+a-AX7rvo$(j&ZXj%}T_|$H- zK>3lo0Q$!5;k(&~Lm`)jx9+r;X-?4wep4tnaTALD`N}Jj;t-nZER9&PjwkbC6 zmQw68U>clbFaw$c{pb~wc|$sH3Tg}9OgMWfc-`^OoYw{2I}*o{N(oE>*;Deb=TmQo zaFRylzn6DBd%uvI{xZG4u!r*DQ#0yq=)+Z8t#pM(*EcAx>5rKu{P3CPFPZu2`LE~W z#-XPIPr000t=wbxG(#<`3r1<^t35GT$vJx(X@nxJv`z>Z)fw^M4!M1`>RuIO09B;G%^{gdEE?_4c>x-m zXys%D8M^4Y#GTWNxFwbH#CY@)*bEEZWjC5nwN>0XcR4-6&Y~(OI_Fq%Y1v`Jn`4-dF4_1M0Lz>~QL=c)6P|ROW4+%)36b+j`FQXzC|sBxdB|#K_4d>K|dH z`%3LR)zuTkMp$E%CWVBnO-myx%l`9yGJX*@?YSkmBCNQq4fMWx@QGa~g3B>ef6<6*NOrGfo<#=Q6yu=fy86${2j+ zCQem6QxA6bt+WVTpmuz}_$gN2U7@^sEbLR6Rgm4=tk|=6&u(3`;7IlaPQpNFW9elTHo})X&Fd8U%DVXiD9XC zAgcq|nz@v0)SXK1*BiN+il-E44j zM8DhgyN1LDKU%BmUH`UqMdI7}l8s_g4Q24>>6+7RZ(Jm};v!eGHWGimzdF=m)oj(N zVyO}xo2jIvASPcq=h2!ll>a1uXIoR4(=XPf|HJWt_2JbcZTPkBnQhBaZPi7rf_Vn_ zVZkG0y-K}OMJo$6dvC3{^U z4DluEdq9khz=pv{o&bf!B!Gh{epG*CFbeXwE|NFjG(#cazadOt6y!fa;cc;CG>t(5 zAJT$r5@9eH_>jI9$%BA!*Y+e3HNXfM0uF`q?n9dJLr8=+60Q&a=YsGYGCVwyICIN? z-0@Z@h&Pi-M?#@NK|xwU+FCS*7Zk3quMdSGpa_H}525MLqB04=npA(4-wNg=er+JL*ZJm&8&U{ZEgR5D24Jb+MkId z{WsqKQ`p~uMJGXVB!5}}gUH)EPnAtqbR?QVA~0zT2O7=qcOkLfG$zg8n??sCw6);i zeYON5nYwA&|2M_f7HLiOXA-DHlC?Ps!V}RVlRc0y3sbbEzApM8OdAe|TcAx3>FVlR zAP(wUSenCN+WNn_<}_jeg+yik=6d{>YyL;>Wy=U z(?9C{!}a)cEM|Y?LV3YJH~02`?e%XHuYoqF|28jg@o)Q+sJxzM@S0mWr*npPFt%WA z94vla-#jDb3*|80P-|^&>JZ$^PC6k~3lp=Yhi@z z`51nv&OpWmcVI2xti+M&Q!C)GhLRd3BUbIfdT38#i4Z#bTk!`2$?ZoTJ;U-8%n|y4g+cwH(>YR7vd$GeGzYI#^JouGlr|_w0cCP2=dVlPi aN(~_Yif=!tN3{$L010qNS#tmY4#NNd4#NS*Z>VGd00$XKL_t(| z+O?QlOjSn|hG8oRh>GGZ;swMCiVCP$2m(U5h>bOddI{B*1Oy&HNv)*;Bx*FKeZkhI zB-%dM1e>&xrj7K$)`!xj)v9R=_C!b_pdvRBLEK z*T2@F<>ch7UAs0lHPyw%W%1(0vu4fWapugKPEJnFFPgqDO&e_bx7Dv2om?gSDrp=gE^NdIBg8sLrZYs~}Qxax$H$sHm{8u)Msypr9a30==5a zBGSKg>sDb`!T3UNZ*QM6WyTNU-=G)7 zYFilOUQ<&uzR)2$tR2p2fv%WSS69dWba!{Vy1F(sHO-wnH}-0rkB?7qa4qP>Z+C&CQU9LAEP4HkJ-sfu~_V z#xQ|tn194ZjmD`;NvwJutRN6S306k%AA?JmE}b)Hj-En*yuA3Bc%7INGuVyUvu8hk{8$kQU-Am( z?A*E2_gg=;M0|WaQV3$W#INShpO3-F6#Y?pU4PvzLq~%F#K&+OlmP3qXU}jjzI^!b zA$^&LNz6p`=|o0Gp7kkvzjc3J)4r_#GQ-2enNI-Rym|B5wQC_EAp|r(ApnMEhL_if z=x!A{+yE273SaRP(sAn4DUvI=MK`erNnvN0jbX=+A4ea3eSO!hTNfG{x^?T;O`A3y zK7V`|BZc~%ot>gEBxXHs!d_jr(F4~kaauG$x8h zK>$3BB_uB%OG`^3dQnl)>C>m*ZA;r&o$Oz}hCH|~m=%8D%x3@Q9 zpPQQt{n)^a&OM4J` z;6QeEHXMujbHnhp3SEo72>3GouLSlGo6QwtC}J}Ty$W=pIQU=-noS;AxpHMfLIO6d z|0623>1_xD6DU`hom90k;qSx~KA+fL6xN~fX1L9`J_cS#1c6SZQuHAa7cN{VDJcPc zMMZ_ofH~|5l2?`VkAsU6B)gh-TYnW0=P}|s&?V!b*^*Pr%F2!%I|jLB1}uWRk-x8( znRZ*{>%ZWIQQ~%}hJg%x34G)~qysTQ4ats3d#UH=KL%AGc38#KfFA-K`Re)e=T#yz zqqu{(Rq3Mc7IUgb{Pp(s!U{M8ttp}KG4_}W?!rRu;MPNN1iG9;%oJvP5=&i3>3!?xNjc8YH;|2Z^PqxcysetUaZ|03GL9}00m)2BnQ5|^li z6l6#^jwh8`gsZNBk4%#(X<}kxEO5X$;u=O6-_c_oI#~I0_wHSMOqimNkej+dk>p%> zIRQXX@R|Eb!yEtVe+*o8@qZZhjj4!Vwrp8jTbnIF4l~|P@K<-eW^aB&TOHKn)K_}uPy*Tkbhhb?3!P=Xj2!XFf!3Ut%seSt+u^c>?GJv9N2wf(2uK d6tdy_{{j6OA(WAZ17rXI002ovPDHLkV1l_ti)jD= diff --git a/assets/icons/U2F/Connect_me_62x31_sfw.png b/assets/icons/U2F/Connect_me_62x31_sfw.png deleted file mode 100644 index 68c48c0e68142548919d6a4b02e40b48a243b04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3767 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 IM6N<$g6Xe=bpQYW diff --git a/assets/icons/U2F/Connected_62x31_sfw.png b/assets/icons/U2F/Connected_62x31_sfw.png deleted file mode 100644 index eeaf660b12ea4647154c8d616b468a3098203356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 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 diff --git a/assets/icons/U2F/Error_62x31_sfw.png b/assets/icons/U2F/Error_62x31_sfw.png deleted file mode 100644 index bb280e75121e12eae045d87a33f61b1713af6f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3751 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 literal 6876 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 W#^eyOwOvyH0000ZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index 43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d..a299d3630239b4486e249cc501872bed5996df3b 100644 GIT binary patch literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QpZ;7LS5RCwCe+j;nvQ`ZM@&oL_zkvT(2Ni=$qArhht8KQYqx-Pj4 zDGf9jqBJ8kB2B1xl#oJ2RLW4OjERJV*ZjWkr?Vcn+i&VeMeqLOT<7ew?|tv}-QV?H zYwi1-%>VwsZ7;s~VxK;JjyvwS|4OxEk3IJ6v(KI|VZyX&)22_K?sdU}1*=xAy6UQ{ z1`i%wt5&TB4H|s>@y7*B`}pIJpLEhmojZ4a{q@(MdFGkSHovuhrP?-|(DIvMqca!& z){i~*n32%=@y8#Zc;X2t{5#v8eDcYso_gx`+i!p5kw^OV>({1Dn@cXaE6A2 z#flZ1H*em*fBzCCO8EHx`|syD^Tdf0%`;%YfF@0v{PfdLM<0E3CRtpLwQjSyTkOxK z%<#)Ezr66m3r8Jw6gsqR+xEWu?(5dA+a7!DvHR}3?~!fAix)3aq{zVsAAIk<_dfja z!y`tF7&K@Quris<{`>DgckWzcF24BUU3S@}W5I8l!tJxZ1=nFGFWzWL_s zufP89Y~z3&s#&vU04iO&^!V}PpL^~(NF9Fo;k@jF4?bABbZL&u5pbuScJkFLue|c% zhaaM+F`0t5I)_Px>@18M7R=UkC!3#r_SqIKS}?Lzt5#jRcI7EM?6AX%6)To6UmliN z@DD%y@bk|<|N85%`sv8|=bt}&_G}JVrc4=_?z-!)M;vj)JMX;n-h1zXFCbpOetq2H z`|rQM>#n=L`|i7a_SwgT$tHQt6oxN(x<=tU3vjxPc3O1bzI|J^Y`JdTI+izX+?b24 zUcK6G!|S1k9-1;`%B!!wx@gg&nKNg?1tO%H`Y`yTk3ND9kE1&S2M#mW3Y67#^{F%$PA`g@wRkIZpu`{VrClSktCWiC2dX9on~Vk2``CCf;<@P0%#0 zySSD}*^I<|Z#Xr&WfD*d2w|JpSr|vl-D%y3bLX9RUVr`d0@jW@?)dGu-&!gZphIXm z6q7#v^wYQAdaGQya^#6f5uN?^+mFg34lg6;w0z#Yd1cF%1%app9pQ=MRIOU|^2;xW z(df~my_!L1h|yPHeMNoHhw5kYBu*O0ztH)ooGZ@@S$M`7XON7y-+r5&A-y<%&_M?s zdg!5Ezxd*dFTeb9=bd+!2O$LklHI)Y(o5WmVXoCFx?zgUh@<^aw)tw`efO)+%9ShK4xqk0(r^k9iYaxg$ozPFs6$7-oPbh7Nm+6ExPyK zd!r91KmYu5^J6HW#YDK%bwo4&UVH7O(;6~=r>3OgD8LzyKmK@@OZkXSpaV_)bec$W z5H|0WZ43yYDUqCzyGdSP1R5cq5{XLUmodQQmGo%)?YDQKgh;%9^UXKS_VUXw`)Err zYq#BYgQj^#jT$w4_;49{=5K?h@IH9tkw=O(wwKTn8c+hl<#XoD@yf!$D@{gL@uM@( zJdYTrdl)I7rupLav(mXXBI&WSiHd zr%IJ7>L8s59B_bS$F|#EdyYpuPQ}1Z{h=w$_UfZI29ONWL#+)nXTydKiHrD!dS*sO zK1j>L<4}y33HC~mH{N&yOhTR+jJff~8?U|gS_1FQKM4@qY_iQvNP*`xg;i1&vSNoh zh;L&&GiukaZT|%q0t^?v20>$jBYF%*BEzK1rAn3Js(67@;+-2sVg)aO4|nv_9n{ll z1~Fmn+O@`m^7m}$ALP!z{@6q!!39QyQ|@E`5r>e;g=RkjC<7cZ7N zOMjGUBS(%@({eg9kYu3{?%}+7095z`hd)(k<~bnNlx})>$605cr4~Q!wA0YUDoI9o zhx}OH9MlWLvS^*>a~71-DSvbc2Q6wqgMVOx{FZ69TMmz<>4|<||pEZ$+rcr}Z1t zuU|ijC3He=Tu5$st6{PmPRC~je%ny3S~aeu-;F4`Op5df3$o1t0|rc*G$|3jOn#dm zTT#d}jXaMsHp;Bf7hLe_y-i~znSh;C=+|u1=_?Tjr=rfrAasZdUccPeRr-x&@ICk3 zW5VQ)L`t*d(9hD;Tq6o z%(1o&NpmJ6JEu)H;P+k3uO7*$ThO1E4&;X7kF>Ms;o0tXY6S zgmkX#JvAjCDgb+Hh6^sZ;L%4PB}QpW45KXg(!YOy97%tLI46&!i&vB$t+|I?16$#=LI6sAs{DpavtEZ2iX zcD_%aJ_`4F^X73bo<+_DK^e`(7hk+`__!knKzN7k=_(}hmy#t*@*1QNeSmV|g%`p?(d3OBPYAy~+v?S;r*;J& zm2n|OA2@Je_wL=1?K;pZS0q@aF!-bn%HnKfg_%;LV^1F7N^FJ9gBC9Cr=jC zWXZA~gsM}g4p8`@Y-`!FWv)d-(&I+LKZcr0$kT62c-)4gwXFo8cs{{kYXz1)hZp&z zHJpphvuDrdFM|dR;t>XeiR&?gF|I1{TLTktU4?Nx1vKW75Z10;TLg&83n;~l7Z=?i zNP~DBbQB`WT9HdZN4&TjH^kYk1t5DRORQ3*O5$(;G{xx~A9 z^X9^(Ox=V$ZfgOERhSf5)kn2jK2!wW?zj)2Ci>ZDn-X2$4*lvms#_FzS&tp%W{=HroG(kW&m4ixZ9 zsBYS{X`@DsSON>3cieG@qMx(C6xZMq2LmBq2~yk;@80{ZjT|{rDCC29B#LuYcty#Y zPW0tjCLcF$+|~k6gm!i&0D;Wjasd&iOP4MuG_EJ4az;Ua%iXOL=!*==w{ zyfcjA$d8lqs(>U`oHE<#r=JcZx63bIyXmHzp={MeZBV2>5h+;yR$i;({ z4#ZNVryyi?@ZiC6mgHe+TpQ;*aljOUo!@Dne(J91=XwB5pK#}B6pGkq{?w`S7HV$9 zFlNiylAAJK;*0-EDiwyPWX~nIyeY@R^oZ{CHJ;xmK*S?}FhtF$^- z7Y544%OUBG%)mzxlY>{TTzM-J>5DG9h});1cEmGP=+M;MZ~Mejtu67YKa-#PY?dFD|JiEexZ{pv*y6>D!=WIeWLHcf z5mG$+?6bB#)Yky!32ibX@{BJ!<-`h+h7B9aAz{7${`;#=5hwk-Of#%lu>w-uC+WmH zF4nPQ$M}iF=+UE1q8~#uc|OTVw=_&!6tZv}pd^n*L7+gR2)EvPt9+W5eeuN?X))q5 zip&-*S{!-gk?}u-)3VLr3Kc5wHB3p5k3TTXojW((8RSLMh+p1V0Eg8nC-}p-{0^0K zJU-l9r;fRq8$TKpgT(culavCYLx&C`ksvKLrE?{iD$`oFY^lUgT*e!LoK#c&^eMX@ zKASFROtpUfdZ?jZ_+!ZbHrZd1jv6&8lSZd7k9xLfdTnvyh)B=TVoAwX;}>r#RjQOa zW8J!S>BoD-RbW!AG4iZzyX2BfgtpS9OQ)ATXU-g1xZuT+-6c7JQSNB(XR z|9Qz2Qb9K_I{7`b)hU1N+qbV^FViKciq&Md31=KFs?OCM5uV~JRU2=5{q@)9Wt)V^ zJn3b_OVpRC%Y953XR`5=2dLq!T6df5uh67RaXLO>!i4hW%V#!9pUsQTM!nTn1O+2T zj2L09!-fq@|1$?hcqF5V^D|qWP>iop>oVEK{^YrS98M1(K3v@p#~RV%cUjOR|3ilk z1q#+kFX=a@CP1Hk_8D$&ko@2pm> z8i6-0529`~C{jVLimmP2w?`Sf^3FT&;9!XoB_u?^LcSn30f^*zI^uV+10C8msMic5l0*mVSdAp z^>W9Q{QiEi>A!|aUrrRSx#k)PNOJTQ&BqsBcp*t=xWJ>2KFTT7Ty3*W)+42kN>$9} zXs%)yKjKg~WLwv+T_unk{)&<<(Brr|e#gpvphMjdlQ5x}Yo|?{rqJIIv$8*WGc%(;Hkju|t?cEztSRyK-oQwemnk?g*^Kl_8t1p$ ztQtFZET=g7=%a15#0TMVTO7IUvdg#z9&rbFHE!Ive*O9k5cOMU+qvhS%g)Ff0Ys|J zmRrT+ry5k5{S4-+UAlDPO0KO~v0`F)@7}$yyz)v}W&Cd;{=EVEe*OAM-!mIc-Zn2f zJjJ%~6j&f|$&w`%P<86m5qhbqeU{x&5Ye}L_wKUWTD58!08k?>J)%Bb)Jl;klGwl~ z{S#{a3Nf=u0^q2cLqML@vu97luy_*xvH0hByT3+B(CZ$j0dJVOx3TLT)m~AiJB?_V}v*RyK=`{cXryh9VfjCHm5e|mC1xD7q zX3ZL*8cYh;s5#UhC!TmBpG~jFVz8Ccg_bEtVBM!rAG7_3iFv9|a@MhV#i(e}qDoDo zXlBNfUZF>AEr?(TJL3$=cCkQGe<)lJ}+cgtCd+U5R+l?*RUWL z;NoY)8bqipZzbn8-&Js7KfmxQJX3mJ>0^M4CK#t8ORr?oq)9H6L&Pu4q(a`?w)B|) Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/icons/iButton/DolphinNice_96x59_sfw.png b/assets/icons/iButton/DolphinNice_96x59_sfw.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QmC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 5122 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 diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..2b4bec7c6f14f53e7362da75f95b83bf387eee54 100644 GIT binary patch literal 2157 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 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png deleted file mode 100644 index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2157 zcmbVO2~ZPP7>*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfdO9l@s z__)F1Bs4IC%YIQH0Rc>WAo4yCJ%`2*B+|!?rVl@O_`&A_2d#}Vq4$T(!SHS{c%BRZ zO#&yRJiMU!FAf0qZvz*)Y{BsXz(M9f_Wv8q3@|zHKR6J0U@VHp4_^cv_CWM!pd5we zfCtGv0rw0KmjEAYvj@6tMdJ_$x8NRu0gi+6!2pVb>i&Nzh(D+P2pB$v;~)4(`~dg= zz$g#|c-#T?fPx>#fDgDla1=fydxORPGhi6`AmH-;gV4Spq5>d%?e6vX1Ke&Qp=XeO z0OC&&`2*+xGo^Vzz~%l2lnxDH1dxzH@gMNP&twmo|A!9^1&}^3_8bHxAAk4`0)7i9 z5Db&X4=4aXMJ5kS06kkK02nYn1*QNLxo`pM@gRFE%pODp2bF*x!~_DA!2YBJC5(On z5C^dU$zwf$MhWcmJ_q$ICiWO50{&GKdyEeOV@JRMej>5(P+RZ-7`SMEs6+e`%m{D~ Og3&$z^CF>$-U|nYD9Y9V literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..843aed27c7df836480f795cd649be62ae08f6ad7 GIT binary patch literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm new file mode 100644 index 0000000000000000000000000000000000000000..ad87a307189f29491e693d9665e9c9780584f1ab GIT binary patch literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm new file mode 100644 index 0000000000000000000000000000000000000000..78de0389efdf70991b33b41f855675bea8b3248f GIT binary patch literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..32bb893cae287bcce9d3d5d3590bc1cd890ac96d GIT binary patch literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 93% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt index 6503043bd..fb0fdb228 100644 --- a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt @@ -1,6 +1,5 @@ Filetype: Flipper Animation Version: 1 - Width: 128 Height: 64 Passive frames: 21 @@ -11,4 +10,4 @@ Frame rate: 5 Duration: 360 Active cooldown: 7 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_52.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_52.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_53.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_53.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_54.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_54.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_55.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_55.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_56.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_56.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_57.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_57.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_58.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_58.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_59.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_59.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt similarity index 92% rename from assets/resources/dolphin/nsfw/lvl_7/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt index 43dd6af5a..f169de719 100644 --- a/assets/resources/dolphin/nsfw/lvl_7/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt @@ -11,4 +11,4 @@ Frame rate: 6 Duration: 360 Active cooldown: 0 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/resources/dolphin/nsfw/manifest.txt b/assets/resources/dolphin_custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/resources/dolphin/nsfw/manifest.txt rename to assets/resources/dolphin_custom/NSFW/Anims/manifest.txt diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a GIT binary patch literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx new file mode 100644 index 0000000000000000000000000000000000000000..9dc1e5dcf52f7721ffe6f002905111825fcda8c9 GIT binary patch literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..03c6304a2d4d334ac519d1861981fcfd0393755f GIT binary patch literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8af359b940e9a14587bb8ae598aeff8e53b6542a GIT binary patch literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..cb92216c4e5943d4d845c979c7d8c59df040b95b GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8932c5ed3ba499c5a9259d2d228330e5f4a5e94d GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx new file mode 100644 index 0000000000000000000000000000000000000000..3eefd86bc994996073b05eee2f627dba8b8bb9cf GIT binary patch literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..22a76dd4daaa01bda8be01f522bf58c0a9e22e2c GIT binary patch literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..75f57367cf539f9f075e06c983de83f6fb826cbd GIT binary patch literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4a569c1eb27507a1301fa34d2ec5d96d62fe43be GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..582247d43e0c1cfbed6e9d8061ec26b7ad417339 GIT binary patch literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..fae05bb57f5b883c6fd532a150899d556ce750cb GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c91714b3f1114d842e45cb614c456d3b40b164c5 GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c37cd533d7a3df21e738cf4cbffb4483f9a551ca GIT binary patch literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..47b611a6ad2b3c2fa21cda23b155e56742d70c77 GIT binary patch literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp Date: Fri, 10 Feb 2023 03:15:08 +0000 Subject: [PATCH 148/231] Remove unused icons --- assets/icons/Interface/SmallArrowDown_4x7.png | Bin 8340 -> 0 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 8552 -> 0 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 3692 -> 0 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 981 -> 0 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 3740 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 3611 -> 0 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 1690 -> 0 bytes assets/icons/StatusBar/GameMode_11x8.png | Bin 3610 -> 0 bytes 9 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png delete mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png delete mode 100644 assets/icons/NFC/ArrowC_1_36x36.png delete mode 100644 assets/icons/NFC/Detailed_chip_17x13.png delete mode 100644 assets/icons/NFC/Medium-chip-22x21.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png delete mode 100644 assets/icons/StatusBar/Alert_9x8.png delete mode 100644 assets/icons/StatusBar/Attention_5x8.png delete mode 100644 assets/icons/StatusBar/GameMode_11x8.png diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png deleted file mode 100644 index 5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8340 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 diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png deleted file mode 100644 index 3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3692 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 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png deleted file mode 100644 index 9aaa1c5552a2d115b7042a4bee7a0c636ed00f01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 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 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png deleted file mode 100644 index 4e0ba8f05921ccbfbaa4390ad07e98fd454df00a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3748 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png deleted file mode 100644 index 137d4c4d054227f27ad6a7e3e374b100d975d9af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1690 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 From 45809b28b0ad139c9bd855ded2e354a3313c6737 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:15:31 +0000 Subject: [PATCH 149/231] Fix dashes in icon names --- ...ghtning_9x10.png => Charging_lightning_9x10.png} | Bin ...sk_9x10.png => Charging_lightning_mask_9x10.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename assets/icons/StatusBar/{Charging-lightning_9x10.png => Charging_lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging-lightning_mask_9x10.png => Charging_lightning_mask_9x10.png} (100%) diff --git a/assets/icons/StatusBar/Charging-lightning_9x10.png b/assets/icons/StatusBar/Charging_lightning_9x10.png similarity index 100% rename from assets/icons/StatusBar/Charging-lightning_9x10.png rename to assets/icons/StatusBar/Charging_lightning_9x10.png diff --git a/assets/icons/StatusBar/Charging-lightning_mask_9x10.png b/assets/icons/StatusBar/Charging_lightning_mask_9x10.png similarity index 100% rename from assets/icons/StatusBar/Charging-lightning_mask_9x10.png rename to assets/icons/StatusBar/Charging_lightning_mask_9x10.png From 8376e866e380d870b69c648e85415c3769e9e4d4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 03:41:07 +0000 Subject: [PATCH 150/231] Use lvl2 sfw passport portait --- assets/icons/Passport/passport_bad_46x49.png | Bin 1237 -> 1295 bytes .../icons/Passport/passport_happy_46x49.png | Bin 1296 -> 1328 bytes assets/icons/Passport/passport_okay_46x49.png | Bin 1244 -> 1281 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/icons/Passport/passport_bad_46x49.png b/assets/icons/Passport/passport_bad_46x49.png index 9b0e7c74ef20cfdf8b046d7597bd857ff786e56d..d11682ab897877c902ed5d77c9804cc27b85c529 100644 GIT binary patch delta 366 zcmV-!0g?XI36Bb}Ujhs>H#jylL^d)sI6^p+YXUF~GdDOkG(;5ujnstB8+i?D`W&%V2!>AU>`~W(!FAFSKyF5Q~--7`iB`F743PH zw9IJGinig;INPPTzF5|s!4`NY+eiCi%6}r!X*(O6(qC`}3ALxgi2*e}-blrvrJm3%%mXcOM)Wbqb9CI|k;4$eu8eF_=}(9Ekjz$4lfi5%!wSo}80&T#Pcm0H#{OU8 z;N#FVU8!5>DR}bB8K2us=24teC)nXeu*V-k7{lY*Q6D#S0c@!(N=4C<7RAVf8$Xjw zjlRS&|7=CiAkMs;N!F%<>FYe&{1%VqcZn|z`+(=F|BS!;Kl2e_0PY(Dz?oR1a{vGU M07*qoM6N<$g4ctiQ2+n{ delta 308 zcmeC@y2`mBo{7iE$imdz(8AKh!pU)RHj@ENzyc~Dx%mK-5MzCZa!o%20|Q%tPl)UP z|Nnu^&_kE&fYen_7srr_TUV}m^Bp$eV7V~!cl<8jkkiL__NUJE)v^^RJji+A;31Ft zy(cz=HP828Iq=YUH}`?B%xOCrW>oKZDxCI+#v_|ez4CU?%H`~vG2@L+PnX~OG&+G^KkipZ{kJV+JQ$iB} DZoi8o diff --git a/assets/icons/Passport/passport_happy_46x49.png b/assets/icons/Passport/passport_happy_46x49.png index 56ea000cd03bf445ff64ac2a9c01318a4ed280e3..f64e770e5a038162d1ceae38d04ff043319ed581 100644 GIT binary patch delta 377 zcmV-<0fzpN3a|>WW&!~=lV<`u0X36n0v)r!0ulj#=+R5l00006P)t-s|Ns900033O z(|!N|0We8KK~#9!oR)zSgCGn;lmGwe?KVo=r05*CQ>PC|ONjz}(C~uS2U!Ci z>5YSyxPeX_LlMLGP@DLUrY!RfJ?tW*f5XRZK7|)>sg~oGt}0+1COi@Ocfxgwlybt| z{Ghm0mv!y<*H>`XXE=xx-fZ9+->IyNu7_@5{F--K*}L(V8nI*Stg*yqTufVC#qH}M z-eZ>M#O3g2Lc^{)AqdjE|Ju(t?||ENz~4yr)+j1+N0_a3klMvCwC@mATa@ zqW%)#+A(i$3h>=4?GORmW>2q(DmGFQ^Mi;3!}&U)=Uw_q{}`W#RXGoFR~hOl8^6|F z@U{G=30TfkG(5(|hU3NV@&}&REYFEHy-t=uMpb)!X>*%ijL4W?#c3Ve@krJohE0PP rM@0QR_^);SlenCg_~#D=F98Ms0>uN?-Jm^600000NkvXXu0mjfAxWH1 diff --git a/assets/icons/Passport/passport_okay_46x49.png b/assets/icons/Passport/passport_okay_46x49.png index 198ba5436018163b26671d31780fc5b8535c0f1a..34fd3767b92bc334f15e4e13811020c1e0211078 100644 GIT binary patch delta 330 zcmV-Q0k!_z34sc*W&!~=lV<`u0X36n0v)r!0ulj#!%b|O00006P)t-s|Ns900033O z(|!N|0Rc%wK~#9!w3bT(#2^Sm!~HKktybj|6c^*73;Rq_0v3>qfh+pqQ=L8vxW?QH zB=NN;)RW`ytF8_e&s=~ITSs8V1-j$Wfr6BO)(}d`AR$k!(v&lYVrIyn^4I#(yCmcJR@#BaJcsN^z zTSxh{l5@xG1&F=~W;cn=aDlU5UQw#O5P%_W�lV<`u0XdUr0v)r!0ulj#>m6Uj00006P)t-s|Ns900033O z(|!N|0NhDLK~#9!tk&xe!ypI+;KTdBbk~MjP%ejLWIvYp>1+iVAcMe2|9rZR2EncJ zGmvc1u5Kqs7(UDdoX>(YmOCB=aVP|*Uk_5N2dk(2@u2mT-yXbfxlcCO%|v?lC0}C{ zm*)e2?>jCNe(dI-9Q#Q9X1-z~_!K_@0t^7cc>`>cnJ2Ui00000NkvXXu0mjfjnjv3 From 8cec6ee207f607c27dc6a638582c350a77694a63 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 04:29:54 +0000 Subject: [PATCH 151/231] Cleaner asset system --- .../settings/xtreme_settings/xtreme_assets.c | 164 ++++++------------ .../settings/xtreme_settings/xtreme_assets.h | 11 -- 2 files changed, 49 insertions(+), 126 deletions(-) diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index b40f272e4..651e680bc 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -4,11 +4,50 @@ XtremeAssets* xtreme_assets = NULL; -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); +void icon(const Icon** replace, const char* name, FuriString* path, File* file) { + furi_string_printf(path, PACKS_DIR "/%s/Icons/%s.bmx", XTREME_SETTINGS()->asset_pack, name); + if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { + uint64_t size = storage_file_size(file) - 8; + int32_t width, height; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->frame_count, 1); + FURI_CONST_ASSIGN(icon->frame_rate, 0); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + icon->frames = malloc(sizeof(const uint8_t*)); + FURI_CONST_ASSIGN_PTR(icon->frames[0], malloc(size)); + storage_file_read(file, (void*)icon->frames[0], size); + *replace = icon; + + storage_file_close(file); } - return xtreme_assets; +} + +void swap(XtremeAssets* x, FuriString* p, File* f) { + icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); + icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); + icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); + icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); + icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); + icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); + icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); + icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); + icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); + icon(&x->I_passport_DB, "Passport/passport_DB", p, f); + icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); + icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); + icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); + icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); + icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); + icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); + icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); + icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); + icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); + icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); } void XTREME_ASSETS_LOAD() { @@ -43,126 +82,21 @@ void XTREME_ASSETS_LOAD() { xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; FileInfo info; FuriString* path = furi_string_alloc(); - const char* pack = xtreme_settings->asset_pack; - furi_string_printf(path, PACKS_DIR "/%s", pack); + furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && info.flags & FSF_DIRECTORY) { File* file = storage_file_alloc(storage); - - swap_bmx_icon( - &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinCommon_56x48, - pack, - "Dolphin/DolphinCommon_56x48.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinMafia_115x62, - pack, - "iButton/DolphinMafia_115x62.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, - pack, - "iButton/iButtonDolphinVerySuccess_108x52.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinReadingSuccess_59x63, - pack, - "Infrared/DolphinReadingSuccess_59x63.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_NFC_dolphin_emulation_47x61, - pack, - "NFC/NFC_dolphin_emulation_47x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_bad_46x49, - pack, - "Passport/passport_bad_46x49.bmx", - path, - file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_passport_happy_46x49, - pack, - "Passport/passport_happy_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_okay_46x49, - pack, - "Passport/passport_okay_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinReceive_97x61, - pack, - "RFID/RFIDDolphinReceive_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSend_97x61, - pack, - "RFID/RFIDDolphinSend_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSuccess_108x57, - pack, - "RFID/RFIDDolphinSuccess_108x57.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); - + swap(xtreme_assets, path, file); storage_file_free(file); } furi_record_close(RECORD_STORAGE); furi_string_free(path); } -void swap_bmx_icon( - const Icon** replace, - const char* pack, - const char* name, - FuriString* path, - File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); - if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { - uint64_t size = storage_file_size(file) - 8; - int32_t width, height; - storage_file_read(file, &width, 4); - storage_file_read(file, &height, 4); - - Icon* icon = malloc(sizeof(Icon)); - FURI_CONST_ASSIGN(icon->frame_count, 1); - FURI_CONST_ASSIGN(icon->frame_rate, 0); - FURI_CONST_ASSIGN(icon->width, width); - FURI_CONST_ASSIGN(icon->height, height); - icon->frames = malloc(sizeof(const uint8_t*)); - FURI_CONST_ASSIGN_PTR(icon->frames[0], malloc(size)); - storage_file_read(file, (void*)icon->frames[0], size); - *replace = icon; - - storage_file_close(file); +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); } + return xtreme_assets; } diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 6904a36ab..6a9b48dce 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -32,14 +32,3 @@ typedef struct { } XtremeAssets; XtremeAssets* XTREME_ASSETS(); - -void XTREME_ASSETS_LOAD(); - -void swap_bmx_icon( - const Icon** replace, - const char* base, - const char* name, - FuriString* path, - File* file); - -void free_bmx_icon(Icon* icon); From 1a9f098936beea11964ea71de9cf5316f22a6d2c Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 04:31:03 +0000 Subject: [PATCH 152/231] Move lvlup anim to asset system --- .../desktop/animations/animation_manager.c | 18 ++---------------- .../settings/xtreme_settings/xtreme_assets.c | 1 + .../settings/xtreme_settings/xtreme_assets.h | 1 + .../Animations/Levelup1_128x64/frame_00.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_01.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_02.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_03.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_04.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_05.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_06.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_07.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_08.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_09.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_10.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_11.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_12.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_13.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_14.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_15.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_16.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_17.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_18.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_19.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_20.png | Bin 1071 -> 0 bytes .../Animations/Levelup1_128x64/frame_21.png | Bin 1151 -> 0 bytes .../Animations/Levelup1_128x64/frame_22.png | Bin 1008 -> 0 bytes .../Animations/Levelup1_128x64/frame_23.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_24.png | Bin 1029 -> 0 bytes .../Animations/Levelup1_128x64/frame_25.png | Bin 1028 -> 0 bytes .../Animations/Levelup1_128x64/frame_26.png | Bin 961 -> 0 bytes .../Animations/Levelup1_128x64/frame_27.png | Bin 1109 -> 0 bytes .../Animations/Levelup1_128x64/frame_28.png | Bin 1004 -> 0 bytes .../Animations/Levelup1_128x64/frame_29.png | Bin 1080 -> 0 bytes .../Animations/Levelup1_128x64/frame_30.png | Bin 984 -> 0 bytes .../Animations/Levelup1_128x64/frame_31.png | Bin 2091 -> 0 bytes .../Animations/Levelup1_128x64/frame_rate | 1 - .../Levelup1_128x64_sfw/frame_00.png | Bin 1326 -> 0 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 1597 -> 0 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 1754 -> 0 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 1828 -> 0 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 1686 -> 0 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 1672 -> 0 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 1659 -> 0 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 1540 -> 0 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 1557 -> 0 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 1551 -> 0 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 1604 -> 0 bytes .../Animations/Levelup2_128x64_sfw/frame_rate | 1 - .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../frame_rate | 0 60 files changed, 4 insertions(+), 18 deletions(-) delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_10.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_11.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_12.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_13.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_14.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_15.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_16.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_17.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_18.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_19.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_20.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_21.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_22.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_23.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_24.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_25.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_26.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_27.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_28.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_29.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_30.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_31.png delete mode 100644 assets/icons/Animations/Levelup1_128x64/frame_rate delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png delete mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_00.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_01.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_02.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_03.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_04.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_05.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_06.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_07.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_08.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_09.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_10.png (100%) rename assets/icons/Animations/{Levelup1_128x64_sfw => Levelup_128x64}/frame_rate (100%) diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 14e06cdf2..0808a3618 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -569,9 +569,6 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -580,19 +577,8 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_ASSETS()->is_nsfw) { - one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); - } else { - if(stats.level <= 20) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); - } else if(stats.level >= 21) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); - } else { - furi_assert(0); - } - } + one_shot_view_start_animation( + animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 651e680bc..5ae22aab0 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -56,6 +56,7 @@ void XTREME_ASSETS_LOAD() { xtreme_assets = malloc(sizeof(XtremeAssets)); XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 6a9b48dce..8fa5d1cde 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -8,6 +8,7 @@ typedef struct { bool is_nsfw; + const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; diff --git a/assets/icons/Animations/Levelup1_128x64/frame_00.png b/assets/icons/Animations/Levelup1_128x64/frame_00.png deleted file mode 100644 index a6589b0c7c4d1672fa7a31dfeae856d298b4a865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1071 zcmV+~1kn3ONk%w1VSoTY0J{JH0002R#l`FE>!zlrs;a7si;Kg_CX>@2HWqN06W-eoIZ6HTUMj&lrYh`i` z3IHqs0001h06+i$1pg4oNvplM7Fv1-+Z1}^6@ucIt!(SQ?}>8sESk41!+h_%g~LQa zImk*ijYxP3{+`gNLMR~?kv7S$y3|pl;4o`C^hk`_lxhpGf|vybTTG{uxfN?uX>mK} zb%9|rZWBgFhHW@$S%Qv(9aAKR3WX$aG6!-hjgOw6bYGyOq@|{(sHv)}tgWuEurLRr zv9(YK4wVeHygUaCxdaOV1G~M)7{3k;0|^1o#Ky?TvkVH%!_d$Tw9~Q53Io~11kTS3 z3Jl%ethfyX1KQ*b=EMU7>FeyK?(y>H_p;rCHlTq6efvIH^Y^czLwxEImU2MAp~Z^? z4jP)pQKA?J4F4~Rl!woO$p$D<-tqVZKq;}AI^jKwDm9q`tN{=R7|9gofQkXQcD29&tvms6t&)wm zKFe4WKt}!@*|#BJ8RO?6bmS|2gG5z%F0_Z7+&N;7}{yLYF51 z6?zsiVr>K#cU54unE}&|Pcpt< zq69SXUjIl7yk{SC(U|7|N%aJ-~bUyI9y2G88D1% z%x!p?16U1&p@@(XM+}4}1`uF?8s;a}0pl&j5CI{=NFhN8DX9d{c%`-H00Y(tm_VO} zy{0EU60Qf9n-XzU4NiwbgARNKJdh5kz$kF$q+(JSB%U1tYThsKoHbK`0=7qh0wyT{ zq`rg{3YXij2{5%KU{o18pR6~!2Vs#g<|gg0xoH>Zj^P;UB0=ss_iMN)(b^tS)*|PT zChFevC#|I3sh3~4S|cMg1e|*mMfn=~PdV?7DIc@f)JKf4)-k|L!p6$>tiMv$S?k4L zR%Wk9AYrWUVb5_4*QmwHM72son!zlrs;a7si;D~l43m?S!^6YllnN98N>3M?tJh6zZ;H4 z9Lj7*qY%h!I(-GhNI^0PYekhzdW$~4;IKj}qE@j+(XzZ%lEd&gZ#;F0tm>R@4KTyF z1qys}goJc#C1zb(CVpOng^_VWej`>_mVHQhX_B6kEn_s63Y9c~LI#35ou96+gm193 zw6(UkDGmey1hPE_4hsqghq%T}4hI1N0}DUE3Iqeoy~ox*2Fl9~&c6x+4a?NN*X1M0 z;>!oY3+?R&@Bju2yXorW=GWQwywU^U{|*paa3CDN00;2pGlq`ew1WW}7!=1~0E2N0 z&F!OhG2%mr_Wltd5UyHAYpTTm4H&r%B*>M!5}vfS;9-M*A6-Jc8LrsOTJv)19GY%` z&!4bV)g#JO5}R}nOHy3Bl^!}S(it{#?i%;t681g)J9t1 z6{OpjLivG}3x{pG1b_tyu-9NINW%vQIN%#c0ad|}2iqg5^DM}{jGt%|7g;brX*&(( zoxC{n6Rq|lJ466F@iNPayP0l6_aNZJ30Om+;>+00}LQ=fN{A^lhc0yqM!g-|BTmJb=&>Z00PJnKpp}(i9=z9 zjSwXbbsIvKTZhImZ~%)hE_EV`BwRQY0U);6*?l}7$Kr^aIfvsEDr)FsWVt=)BXZ&d zcchLw5uoIfkTnRwlKVv@+mR(KkR+BHHpnDkfbqyndtj2_0C?va*W!>*5}<%P3Rs6g zTy2_Q0Gzw@xM5&RCa{hJ!SE=Jk$wK@k~CH_$yzjIB3H_T4`jz^o6-F?(sTWo^$n9HgGOF5fP zVW>W!Z@Y7X8ZNvK$=m2O&X%aseyA=}ppW)3oL<1#itr{*6o&+&eT2SCBtZ~IOpmG* zd$pg&Wp0OPyb;ecEXA-X>@Jos;`EeO%Fe3_%Lc0?Xt6a?Ohq=UMhrkQIOqHtj|dLh z;CwinK@hLK)>O1e&5p!cwDgViFwR%CBeXnQ7fl&q1v+?fssc?-z+&+Tt;^InAx4cr z0zqJRb_ImZ@>5^S^9|f0(Wi%-$ifQ=i7h>BoqUGw{SoU1Ox&%0X0Y6I8N$L RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_02.png b/assets/icons/Animations/Levelup1_128x64/frame_02.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_07.png b/assets/icons/Animations/Levelup1_128x64/frame_07.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_08.png b/assets/icons/Animations/Levelup1_128x64/frame_08.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_14.png b/assets/icons/Animations/Levelup1_128x64/frame_14.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_15.png b/assets/icons/Animations/Levelup1_128x64/frame_15.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_19.png b/assets/icons/Animations/Levelup1_128x64/frame_19.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 RB;%qqOs@9?-qwTw06Y1u0uKNH diff --git a/assets/icons/Animations/Levelup1_128x64/frame_22.png b/assets/icons/Animations/Levelup1_128x64/frame_22.png deleted file mode 100644 index 19a5121a94410868eb1172b4642f8f1d51a17948..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1008 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 diff --git a/assets/icons/Animations/Levelup1_128x64/frame_27.png b/assets/icons/Animations/Levelup1_128x64/frame_27.png deleted file mode 100644 index a7c826438232d81c98daf755105ca245fa37c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# diff --git a/assets/icons/Animations/Levelup1_128x64/frame_28.png b/assets/icons/Animations/Levelup1_128x64/frame_28.png deleted file mode 100644 index 79a86e03e1fdec65d54ffbb9cd39cdec4a550d95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1004 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 VH+Qd@rilOm002ovPDHLkV1nhs($D|^ diff --git a/assets/icons/Animations/Levelup1_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64/frame_rate deleted file mode 100644 index c7930257d..000000000 --- a/assets/icons/Animations/Levelup1_128x64/frame_rate +++ /dev/null @@ -1 +0,0 @@ -7 \ No newline at end of file diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png deleted file mode 100644 index bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png deleted file mode 100644 index 39c910d3a41314c985e4ce79337cbbf30d1ae79e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png deleted file mode 100644 index 4975adf8627b62a22d002bfc1d7be423dcc5e83b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png deleted file mode 100644 index e6c88df92b73b89f1ad31810be1a653c3fea5d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png deleted file mode 100644 index e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 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 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png deleted file mode 100644 index 32e864e9827420142930083d480d331024d033f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png deleted file mode 100644 index c692f48952c106373b56ecafd4bce7ba1295f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png deleted file mode 100644 index fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate deleted file mode 100644 index 0cfbf0888..000000000 --- a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png rename to assets/icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png rename to assets/icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png rename to assets/icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png b/assets/icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png rename to assets/icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png rename to assets/icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png rename to assets/icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png b/assets/icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png rename to assets/icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png rename to assets/icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png rename to assets/icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png rename to assets/icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png b/assets/icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png rename to assets/icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64_sfw/frame_rate rename to assets/icons/Animations/Levelup_128x64/frame_rate From 2b7f3797ee10e047dfe8899ddfd2e6fb459c57a4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:53:34 +0000 Subject: [PATCH 153/231] Support animated icons in asset packs --- .../settings/xtreme_settings/xtreme_assets.c | 43 +++++++++++- .../settings/xtreme_settings/xtreme_assets.h | 2 + scripts/asset_packer.py | 70 +++++++++++++------ 3 files changed, 93 insertions(+), 22 deletions(-) diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 5ae22aab0..13014b8d1 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,10 +2,50 @@ #include "assets_icons.h" #include +#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" + XtremeAssets* xtreme_assets = NULL; +void anim(const Icon** replace, const char* name, FuriString* path, File* file) { + do { + furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + int32_t width, height, frame_rate, frame_count; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + storage_file_read(file, &frame_rate, 4); + storage_file_read(file, &frame_count, 4); + storage_file_close(file); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); + FURI_CONST_ASSIGN(icon->frame_count, frame_count); + icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); + const char* pack = XTREME_SETTINGS()->asset_pack; + + bool ok = true; + for(int i = 0; ok && i < icon->frame_count; ++i) { + ok = false; + furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + uint64_t size = storage_file_size(file); + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); + if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; + storage_file_close(file); + } + if(!ok) break; + + *replace = icon; + } while(false); +} + void icon(const Icon** replace, const char* name, FuriString* path, File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s.bmx", XTREME_SETTINGS()->asset_pack, name); + furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -27,6 +67,7 @@ void icon(const Icon** replace, const char* name, FuriString* path, File* file) } void swap(XtremeAssets* x, FuriString* p, File* f) { + anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 8fa5d1cde..038372a43 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -32,4 +32,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; +void XTREME_ASSETS_LOAD(); + XtremeAssets* XTREME_ASSETS(); diff --git a/scripts/asset_packer.py b/scripts/asset_packer.py index aea755811..02ed0b94e 100755 --- a/scripts/asset_packer.py +++ b/scripts/asset_packer.py @@ -43,12 +43,51 @@ def convert_bmx(img: "Image.Image | pathlib.Path") -> bytes: return data +def pack_anim(src: pathlib.Path, dst: pathlib.Path): + if not (src / "meta.txt").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "meta.txt": + shutil.copyfile(src / "meta.txt", dst / "meta.txt") + continue + elif frame.name.startswith("frame_"): + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + + +def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): + if not (src / "frame_rate").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + frame_count = 0 + frame_rate = None + size = None + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "frame_rate": + frame_rate = int((src / "frame_rate").read_text()) + continue + elif frame.name.startswith("frame_"): + frame_count += 1 + if not size: + size = Image.open(frame).size + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + (dst / "meta").write_bytes(struct.pack(" Date: Fri, 10 Feb 2023 08:54:20 +0000 Subject: [PATCH 154/231] Load nsfw lvlup from sd with asset pack --- .../Icons/Animations/Levelup_128x64/frame_00.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_01.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_02.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_03.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_04.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_05.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_06.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_07.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_08.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_09.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_10.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_11.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_12.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_13.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_14.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_15.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_16.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_17.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_18.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_19.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_20.png | Bin 0 -> 1071 bytes .../Icons/Animations/Levelup_128x64/frame_21.png | Bin 0 -> 1151 bytes .../Icons/Animations/Levelup_128x64/frame_22.png | Bin 0 -> 1008 bytes .../Icons/Animations/Levelup_128x64/frame_23.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_24.png | Bin 0 -> 1029 bytes .../Icons/Animations/Levelup_128x64/frame_25.png | Bin 0 -> 1028 bytes .../Icons/Animations/Levelup_128x64/frame_26.png | Bin 0 -> 961 bytes .../Icons/Animations/Levelup_128x64/frame_27.png | Bin 0 -> 1109 bytes .../Icons/Animations/Levelup_128x64/frame_28.png | Bin 0 -> 1004 bytes .../Icons/Animations/Levelup_128x64/frame_29.png | Bin 0 -> 1080 bytes .../Icons/Animations/Levelup_128x64/frame_30.png | Bin 0 -> 984 bytes .../Icons/Animations/Levelup_128x64/frame_31.png | Bin 0 -> 2091 bytes .../Icons/Animations/Levelup_128x64/frame_rate | 1 + .../Icons/Animations/Levelup_128x64/frame_00.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_01.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_02.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_03.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_04.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_05.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_06.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_07.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_08.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_09.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_10.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_11.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_12.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_13.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_14.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_15.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_16.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_17.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_18.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_19.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_20.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_21.bm | Bin 0 -> 217 bytes .../Icons/Animations/Levelup_128x64/frame_22.bm | Bin 0 -> 219 bytes .../Icons/Animations/Levelup_128x64/frame_23.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_24.bm | Bin 0 -> 221 bytes .../Icons/Animations/Levelup_128x64/frame_25.bm | Bin 0 -> 223 bytes .../Icons/Animations/Levelup_128x64/frame_26.bm | Bin 0 -> 225 bytes .../Icons/Animations/Levelup_128x64/frame_27.bm | Bin 0 -> 227 bytes .../Icons/Animations/Levelup_128x64/frame_28.bm | Bin 0 -> 228 bytes .../Icons/Animations/Levelup_128x64/frame_29.bm | Bin 0 -> 222 bytes .../Icons/Animations/Levelup_128x64/frame_30.bm | Bin 0 -> 224 bytes .../Icons/Animations/Levelup_128x64/frame_31.bm | Bin 0 -> 518 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 0 -> 16 bytes 66 files changed, 1 insertion(+) create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..a6589b0c7c4d1672fa7a31dfeae856d298b4a865 GIT binary patch literal 1071 zcmV+~1kn3ONk%w1VSoTY0J{JH0002R#l`FE>!zlrs;a7si;Kg_CX>@2HWqN06W-eoIZ6HTUMj&lrYh`i` z3IHqs0001h06+i$1pg4oNvplM7Fv1-+Z1}^6@ucIt!(SQ?}>8sESk41!+h_%g~LQa zImk*ijYxP3{+`gNLMR~?kv7S$y3|pl;4o`C^hk`_lxhpGf|vybTTG{uxfN?uX>mK} zb%9|rZWBgFhHW@$S%Qv(9aAKR3WX$aG6!-hjgOw6bYGyOq@|{(sHv)}tgWuEurLRr zv9(YK4wVeHygUaCxdaOV1G~M)7{3k;0|^1o#Ky?TvkVH%!_d$Tw9~Q53Io~11kTS3 z3Jl%ethfyX1KQ*b=EMU7>FeyK?(y>H_p;rCHlTq6efvIH^Y^czLwxEImU2MAp~Z^? z4jP)pQKA?J4F4~Rl!woO$p$D<-tqVZKq;}AI^jKwDm9q`tN{=R7|9gofQkXQcD29&tvms6t&)wm zKFe4WKt}!@*|#BJ8RO?6bmS|2gG5z%F0_Z7+&N;7}{yLYF51 z6?zsiVr>K#cU54unE}&|Pcpt< zq69SXUjIl7yk{SC(U|7|N%aJ-~bUyI9y2G88D1% z%x!p?16U1&p@@(XM+}4}1`uF?8s;a}0pl&j5CI{=NFhN8DX9d{c%`-H00Y(tm_VO} zy{0EU60Qf9n-XzU4NiwbgARNKJdh5kz$kF$q+(JSB%U1tYThsKoHbK`0=7qh0wyT{ zq`rg{3YXij2{5%KU{o18pR6~!2Vs#g<|gg0xoH>Zj^P;UB0=ss_iMN)(b^tS)*|PT zChFevC#|I3sh3~4S|cMg1e|*mMfn=~PdV?7DIc@f)JKf4)-k|L!p6$>tiMv$S?k4L zR%Wk9AYrWUVb5_4*QmwHM72son!zlrs;a7si;D~l43m?S!^6YllnN98N>3M?tJh6zZ;H4 z9Lj7*qY%h!I(-GhNI^0PYekhzdW$~4;IKj}qE@j+(XzZ%lEd&gZ#;F0tm>R@4KTyF z1qys}goJc#C1zb(CVpOng^_VWej`>_mVHQhX_B6kEn_s63Y9c~LI#35ou96+gm193 zw6(UkDGmey1hPE_4hsqghq%T}4hI1N0}DUE3Iqeoy~ox*2Fl9~&c6x+4a?NN*X1M0 z;>!oY3+?R&@Bju2yXorW=GWQwywU^U{|*paa3CDN00;2pGlq`ew1WW}7!=1~0E2N0 z&F!OhG2%mr_Wltd5UyHAYpTTm4H&r%B*>M!5}vfS;9-M*A6-Jc8LrsOTJv)19GY%` z&!4bV)g#JO5}R}nOHy3Bl^!}S(it{#?i%;t681g)J9t1 z6{OpjLivG}3x{pG1b_tyu-9NINW%vQIN%#c0ad|}2iqg5^DM}{jGt%|7g;brX*&(( zoxC{n6Rq|lJ466F@iNPayP0l6_aNZJ30Om+;>+00}LQ=fN{A^lhc0yqM!g-|BTmJb=&>Z00PJnKpp}(i9=z9 zjSwXbbsIvKTZhImZ~%)hE_EV`BwRQY0U);6*?l}7$Kr^aIfvsEDr)FsWVt=)BXZ&d zcchLw5uoIfkTnRwlKVv@+mR(KkR+BHHpnDkfbqyndtj2_0C?va*W!>*5}<%P3Rs6g zTy2_Q0Gzw@xM5&RCa{hJ!SE=Jk$wK@k~CH_$yzjIB3H_T4`jz^o6-F?(sTWo^$n9HgGOF5fP zVW>W!Z@Y7X8ZNvK$=m2O&X%aseyA=}ppW)3oL<1#itr{*6o&+&eT2SCBtZ~IOpmG* zd$pg&Wp0OPyb;ecEXA-X>@Jos;`EeO%Fe3_%Lc0?Xt6a?Ohq=UMhrkQIOqHtj|dLh z;CwinK@hLK)>O1e&5p!cwDgViFwR%CBeXnQ7fl&q1v+?fssc?-z+&+Tt;^InAx4cr z0zqJRb_ImZ@>5^S^9|f0(Wi%-$ifQ=i7h>BoqUGw{SoU1Ox&%0X0Y6I8N$L RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 zcmV#C}%i;Ih`t*s0U43m?S%gf8-;+L&#>%Q-a za`Y^kw=Khb@4E!UL_smgN;Qp0cnbcW(5ONvAr_G~$*#K8QKR56YdZ8ujM|iH3$TKi z1O;16r<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&uwBychYaw?6Fo}YAIprfRv zrl&j%2LY@HqNuPv3=0dd8LF(T1q`vhJemOowJi(-tOy9KcfQOS3kABvh6dEs#mC6W z1H8@M6}Y+E60-^i=I7W02gw12-rWn>tqTeR#<}?61MIHv-Ui09Xh6%$CPb3O0^hsm$a_K?YW_oY~IhOFGwR z&g^(o5Q72*Fgg@Evt&=0^@tWEifrY9t?jClzxcFiTf$z5`S7<;}I=?UQHu=%ytY^5NcASN&oji*N4Wf%l*pHr!V+ zjF<(R5>CKX^3uaREw9MQav!n*o(C4d>(H%e7K~ROC>J0B0@Q0a?sfe-LrRuG5ui1D zJ9PkaU$q(kDzP_)-%f`MPJlbyf#6)(;;ZcZxyX?F1Wym9H~87xy7+eY{XLjt@q&wQ z#5>&X^z@pl}Z=vj79Lu(r{TCRVWEX5!g~ zjEZL+cb_qmMHD0jKN)sFeeJlozT=G2NY=#;GM*iv=QuYS^PHIgbL=r|J$yy}58V0k>q2cP1=rOiE%9TApsp^n{)bC*D4a033Fw0o|s+p|MKbQQF!k(HE0RTI!zlrs;a7si;Kg_CX>@2HWqN06W-eoIZ6HTUMj&lrYh`i` z3IHqs0001h06+i$1pg4oNvplM7Fv1-+Z1}^6@ucIt!(SQ?}>8sESk41!+h_%g~LQa zImk*ijYxP3{+`gNLMR~?kv7S$y3|pl;4o`C^hk`_lxhpGf|vybTTG{uxfN?uX>mK} zb%9|rZWBgFhHW@$S%Qv(9aAKR3WX$aG6!-hjgOw6bYGyOq@|{(sHv)}tgWuEurLRr zv9(YK4wVeHygUaCxdaOV1G~M)7{3k;0|^1o#Ky?TvkVH%!_d$Tw9~Q53Io~11kTS3 z3Jl%ethfyX1KQ*b=EMU7>FeyK?(y>H_p;rCHlTq6efvIH^Y^czLwxEImU2MAp~Z^? z4jP)pQKA?J4F4~Rl!woO$p$D<-tqVZKq;}AI^jKwDm9q`tN{=R7|9gofQkXQcD29&tvms6t&)wm zKFe4WKt}!@*|#BJ8RO?6bmS|2gG5z%F0_Z7+&N;7}{yLYF51 z6?zsiVr>K#cU54unE}&|Pcpt< zq69SXUjIl7yk{SC(U|7|N%aJ-~bUyI9y2G88D1% z%x!p?16U1&p@@(XM+}4}1`uF?8s;a}0pl&j5CI{=NFhN8DX9d{c%`-H00Y(tm_VO} zy{0EU60Qf9n-XzU4NiwbgARNKJdh5kz$kF$q+(JSB%U1tYThsKoHbK`0=7qh0wyT{ zq`rg{3YXij2{5%KU{o18pR6~!2Vs#g<|gg0xoH>Zj^P;UB0=ss_iMN)(b^tS)*|PT zChFevC#|I3sh3~4S|cMg1e|*mMfn=~PdV?7DIc@f)JKf4)-k|L!p6$>tiMv$S?k4L zR%Wk9AYrWUVb5_4*QmwHM72son!zlrs;a7si;D~l43m?S!^6YllnN98N>3M?tJh6zZ;H4 z9Lj7*qY%h!I(-GhNI^0PYekhzdW$~4;IKj}qE@j+(XzZ%lEd&gZ#;F0tm>R@4KTyF z1qys}goJc#C1zb(CVpOng^_VWej`>_mVHQhX_B6kEn_s63Y9c~LI#35ou96+gm193 zw6(UkDGmey1hPE_4hsqghq%T}4hI1N0}DUE3Iqeoy~ox*2Fl9~&c6x+4a?NN*X1M0 z;>!oY3+?R&@Bju2yXorW=GWQwywU^U{|*paa3CDN00;2pGlq`ew1WW}7!=1~0E2N0 z&F!OhG2%mr_Wltd5UyHAYpTTm4H&r%B*>M!5}vfS;9-M*A6-Jc8LrsOTJv)19GY%` z&!4bV)g#JO5}R}nOHy3Bl^!}S(it{#?i%;t681g)J9t1 z6{OpjLivG}3x{pG1b_tyu-9NINW%vQIN%#c0ad|}2iqg5^DM}{jGt%|7g;brX*&(( zoxC{n6Rq|lJ466F@iNPayP0l6_aNZJ30Om+;>+00}LQ=fN{A^lhc0yqM!g-|BTmJb=&>Z00PJnKpp}(i9=z9 zjSwXbbsIvKTZhImZ~%)hE_EV`BwRQY0U);6*?l}7$Kr^aIfvsEDr)FsWVt=)BXZ&d zcchLw5uoIfkTnRwlKVv@+mR(KkR+BHHpnDkfbqyndtj2_0C?va*W!>*5}<%P3Rs6g zTy2_Q0Gzw@xM5&RCa{hJ!SE=Jk$wK@k~CH_$yzjIB3H_T4`jz^o6-F?(sTWo^$n9HgGOF5fP zVW>W!Z@Y7X8ZNvK$=m2O&X%aseyA=}ppW)3oL<1#itr{*6o&+&eT2SCBtZ~IOpmG* zd$pg&Wp0OPyb;ecEXA-X>@Jos;`EeO%Fe3_%Lc0?Xt6a?Ohq=UMhrkQIOqHtj|dLh z;CwinK@hLK)>O1e&5p!cwDgViFwR%CBeXnQ7fl&q1v+?fssc?-z+&+Tt;^InAx4cr z0zqJRb_ImZ@>5^S^9|f0(Wi%-$ifQ=i7h>BoqUGw{SoU1Ox&%0X0Y6I8N$L RB;%qqOs@9?-qwTw06Y1u0uKNH literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png new file mode 100644 index 0000000000000000000000000000000000000000..19a5121a94410868eb1172b4642f8f1d51a17948 GIT binary patch literal 1008 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png new file mode 100644 index 0000000000000000000000000000000000000000..a7c826438232d81c98daf755105ca245fa37c568 GIT binary patch literal 1109 zcmV-b1giT-Nk%v~VSoTY0J{JH00020s;a%cz3c1i!^6Xkjg4GfT#Ji~3=9n8;+L&#>%Q-a za`Y^kw=Khb@4E-Yq(DK)N;Qp0cnbcW&}cy@Ar_G~$*#K8QKR56YdZ8ujM|iH3$TKi zhXPwnr<1uAYg1`)JLh$QVKQzLMn{HiIB8jej)NUjB!&frBychaaw?6Fo}YAIprfRv zrl%PT1p;?32nz<4t*EpxngId=2ClHHxwEyw8MgtryDbO?xdhCtqQcNctIPzfum;!$ z3kci*$+b2r&F=ozFZ(Z7k+OyaP~Oama%N9D%5W|lO3mR=e?c7*FnTnKs+>!yF>?wus#60zt3pACwX4UH zCv}Mp^b@L9uedbATn3Zs+83%6hD*SAz_^F3p6XmRcfs8-NvRE3y!fwE!Y3KdwR?D2 zfrkVbH-?*0FxR-AAr>%P`Nx8WxD+rR=T#wOndCwPK32NNNPhta3dAg6+-txRWf$97 zn@2LVnBfl3oFH(~Uk%byE`COC%zgq?4^YtHcU)h%OoF8DLX&dk0UEeVz*s>g&+oG% z2RxlR0@S#H^J@@2fqU@F=}g5OL$4WF+eql?$DU@Z z1?CO`2^6y#K*d;CngLCnG!2Iwj`LtH4r=EjWF}UIqHqprv>}U{DbSf;+BH_tP2-Wr z0*VTUsZn+d~Ra%LoR9Z%v}GV=iE}dBJ^2dKe{>EGE#v0)#G*+bT7dM$BNWFgfFrWrhe(13}3WmY&{N zH&_Ae`PZE`C~CQ*Km>HR3Y(Z-fZ}KLfe25bhL+hQGY%A>o&cz(TEVK1J;h1@3ebs7 z0Dy|RjsySXs_O*2_DbOYWp5Q5X_^e>xvT`uauuHd4IoR6w6gl;j1l(_SF}M?)6hhyy!sqltK!S+TfPpPSwu8*PbTueCedQZ@3-8 z(+;`1#=<~5Bo3Dy#XNF%8KDf|87~FcsPeE>>45BHXFL6bajM+>w6aqmg1pYQYN4ZS z!yF6D6{QOXx=+w10}#uln8qS>XTWI`lvV{*Qu4zL7zWG9MHqm~!_J*`Cy31zz`)sG z(`#5aTuY!b*iegh_Sx>tef7g&wQY#aa0B?&+*Z%cH-ro@jcLGe!|YW92Aquz1CL_^ bcK|<^n|9*SsrQ%);MqKWIfOHV5&-}^wUQ0# literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png new file mode 100644 index 0000000000000000000000000000000000000000..79a86e03e1fdec65d54ffbb9cd39cdec4a550d95 GIT binary patch literal 1004 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 VH+Qd@rilOm002ovPDHLkV1nhs($D|^ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate new file mode 100644 index 000000000..c7930257d --- /dev/null +++ b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate @@ -0,0 +1 @@ +7 \ No newline at end of file diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta new file mode 100644 index 0000000000000000000000000000000000000000..485a421b6e4585f9e7e41e4cc628e815074fdb04 GIT binary patch literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 literal 0 HcmV?d00001 From 0a43a72fc17e937f09537101a8d6b177f5f70e61 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:55:47 +0000 Subject: [PATCH 155/231] Init asset packs at desktop load --- applications/services/desktop/desktop.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 7840cd00a..b5b73668b 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#include "../../settings/xtreme_settings/xtreme_assets.h" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -305,6 +307,9 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + // TODO: find a (working) way to run this at startup without hooking desktop + XTREME_ASSETS_LOAD(); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { From a1d1e2a5a20865980dda61e3aa763758e2694b15 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 08:56:07 +0000 Subject: [PATCH 156/231] Format --- applications/plugins/nightstand/application.fam | 1 - applications/plugins/pomodoro/application.fam | 2 +- applications/plugins/scrambler/application.fam | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index caec3cbf6..cea02bd00 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,4 +10,3 @@ App( fap_category="Misc", order=81, ) - diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 27e73a0ce..750373342 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) \ No newline at end of file +) diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 8d87a4a62..4d48d7bb5 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP From 9b80cec2fa20e9e09e67fc55f7d529cd21b33b42 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 10:09:28 +0000 Subject: [PATCH 157/231] Add internal flash browser tab --- applications/main/archive/helpers/archive_browser.c | 12 ++++++++---- applications/main/archive/helpers/archive_browser.h | 2 ++ .../main/archive/views/archive_browser_view.c | 1 + .../main/archive/views/archive_browser_view.h | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 3163b8cff..9cb66a5e5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,10 +457,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; + break; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 5b13e98da..43a9a651a 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index dce753fde..26ed17d75 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a525a7db6..6e6582405 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,6 +31,7 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; From 3eacb0c715b09242fa48d6158803d6af09059a02 Mon Sep 17 00:00:00 2001 From: Pierce Date: Fri, 10 Feb 2023 04:52:53 -0800 Subject: [PATCH 158/231] Fixed typo in nfc_magic_scene_wrong_card.c (#2382) --- .../plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c index 69bf9eb50..4b8089693 100644 --- a/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c +++ b/applications/plugins/nfc_magic/scenes/nfc_magic_scene_wrong_card.c @@ -26,7 +26,7 @@ void nfc_magic_scene_wrong_card_on_enter(void* context) { AlignLeft, AlignTop, FontSecondary, - "Writing is supported\nonly for 4 bytes UID\nMifare CLassic 1k"); + "Writing is supported\nonly for 4 bytes UID\nMifare Classic 1k"); widget_add_button_element( widget, GuiButtonTypeLeft, "Retry", nfc_magic_scene_wrong_card_widget_callback, nfc_magic); From e7d01998c1ccefb260ddfd1f0e759457ea75006d Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 10 Feb 2023 17:28:26 +0100 Subject: [PATCH 159/231] Update subghz --- applications/main/subghz/SConscript | 32 + applications/main/subghz/application.fam | 2 +- applications/main/subghz/blocks/const.c | 1 + applications/main/subghz/blocks/const.h | 20 + applications/main/subghz/blocks/decoder.c | 27 + applications/main/subghz/blocks/decoder.h | 47 + applications/main/subghz/blocks/encoder.c | 58 + applications/main/subghz/blocks/encoder.h | 67 + applications/main/subghz/blocks/generic.c | 120 ++ applications/main/subghz/blocks/generic.h | 59 + applications/main/subghz/blocks/math.c | 244 ++++ applications/main/subghz/blocks/math.h | 222 ++++ applications/main/subghz/environment.c | 116 ++ applications/main/subghz/environment.h | 115 ++ .../main/subghz/helpers/subghz_custom_event.h | 1 - .../subghz_frequency_analyzer_worker.c | 57 +- .../main/subghz/protocols/alutech_at_4n.c | 455 +++++++ .../main/subghz/protocols/alutech_at_4n.h | 74 ++ applications/main/subghz/protocols/ansonic.c | 346 +++++ applications/main/subghz/protocols/ansonic.h | 107 ++ applications/main/subghz/protocols/base.c | 62 + applications/main/subghz/protocols/base.h | 88 ++ applications/main/subghz/protocols/bett.c | 342 +++++ applications/main/subghz/protocols/bett.h | 107 ++ applications/main/subghz/protocols/bin_raw.c | 1120 +++++++++++++++++ applications/main/subghz/protocols/bin_raw.h | 111 ++ applications/main/subghz/protocols/came.c | 347 +++++ applications/main/subghz/protocols/came.h | 107 ++ .../main/subghz/protocols/came_atomo.c | 598 +++++++++ .../main/subghz/protocols/came_atomo.h | 110 ++ .../main/subghz/protocols/came_twee.c | 468 +++++++ .../main/subghz/protocols/came_twee.h | 107 ++ .../main/subghz/protocols/chamberlain_code.c | 499 ++++++++ .../main/subghz/protocols/chamberlain_code.h | 107 ++ applications/main/subghz/protocols/clemsa.c | 365 ++++++ applications/main/subghz/protocols/clemsa.h | 107 ++ applications/main/subghz/protocols/doitrand.c | 356 ++++++ applications/main/subghz/protocols/doitrand.h | 107 ++ applications/main/subghz/protocols/dooya.c | 447 +++++++ applications/main/subghz/protocols/dooya.h | 107 ++ applications/main/subghz/protocols/faac_slh.c | 512 ++++++++ applications/main/subghz/protocols/faac_slh.h | 129 ++ applications/main/subghz/protocols/gate_tx.c | 334 +++++ applications/main/subghz/protocols/gate_tx.h | 107 ++ applications/main/subghz/protocols/holtek.c | 374 ++++++ applications/main/subghz/protocols/holtek.h | 107 ++ .../main/subghz/protocols/holtek_ht12x.c | 400 ++++++ .../main/subghz/protocols/holtek_ht12x.h | 107 ++ .../main/subghz/protocols/honeywell_wdb.c | 399 ++++++ .../main/subghz/protocols/honeywell_wdb.h | 111 ++ applications/main/subghz/protocols/hormann.c | 341 +++++ applications/main/subghz/protocols/hormann.h | 107 ++ applications/main/subghz/protocols/ido.c | 234 ++++ applications/main/subghz/protocols/ido.h | 73 ++ .../main/subghz/protocols/intertechno_v3.c | 472 +++++++ .../main/subghz/protocols/intertechno_v3.h | 111 ++ applications/main/subghz/protocols/keeloq.c | 1115 ++++++++++++++++ applications/main/subghz/protocols/keeloq.h | 153 +++ .../main/subghz/protocols/keeloq_common.c | 142 +++ .../main/subghz/protocols/keeloq_common.h | 100 ++ applications/main/subghz/protocols/kia.c | 279 ++++ applications/main/subghz/protocols/kia.h | 73 ++ .../subghz/protocols/kinggates_stylo_4k.c | 581 +++++++++ .../subghz/protocols/kinggates_stylo_4k.h | 110 ++ applications/main/subghz/protocols/linear.c | 352 ++++++ applications/main/subghz/protocols/linear.h | 107 ++ .../main/subghz/protocols/linear_delta3.c | 359 ++++++ .../main/subghz/protocols/linear_delta3.h | 111 ++ applications/main/subghz/protocols/magellan.c | 445 +++++++ applications/main/subghz/protocols/magellan.h | 107 ++ applications/main/subghz/protocols/marantec.c | 393 ++++++ applications/main/subghz/protocols/marantec.h | 107 ++ applications/main/subghz/protocols/megacode.c | 429 +++++++ applications/main/subghz/protocols/megacode.h | 107 ++ .../main/subghz/protocols/nero_radio.c | 397 ++++++ .../main/subghz/protocols/nero_radio.h | 107 ++ .../main/subghz/protocols/nero_sketch.c | 382 ++++++ .../main/subghz/protocols/nero_sketch.h | 107 ++ applications/main/subghz/protocols/nice_flo.c | 330 +++++ applications/main/subghz/protocols/nice_flo.h | 107 ++ .../main/subghz/protocols/nice_flor_s.c | 694 ++++++++++ .../main/subghz/protocols/nice_flor_s.h | 127 ++ .../main/subghz/protocols/phoenix_v2.c | 339 +++++ .../main/subghz/protocols/phoenix_v2.h | 107 ++ .../main/subghz/protocols/power_smart.c | 394 ++++++ .../main/subghz/protocols/power_smart.h | 107 ++ .../main/subghz/protocols/princeton.c | 374 ++++++ .../main/subghz/protocols/princeton.h | 115 ++ .../subghz/protocols/princeton_for_testing.c | 288 +++++ .../subghz/protocols/princeton_for_testing.h | 97 ++ .../main/subghz/protocols/protocol_items.c | 50 + .../main/subghz/protocols/protocol_items.h | 55 + applications/main/subghz/protocols/raw.c | 374 ++++++ applications/main/subghz/protocols/raw.h | 148 +++ .../main/subghz/protocols/scher_khan.c | 288 +++++ .../main/subghz/protocols/scher_khan.h | 73 ++ .../main/subghz/protocols/secplus_v1.c | 634 ++++++++++ .../main/subghz/protocols/secplus_v1.h | 113 ++ .../main/subghz/protocols/secplus_v2.c | 833 ++++++++++++ .../main/subghz/protocols/secplus_v2.h | 125 ++ applications/main/subghz/protocols/smc5326.c | 387 ++++++ applications/main/subghz/protocols/smc5326.h | 107 ++ .../main/subghz/protocols/somfy_keytis.c | 797 ++++++++++++ .../main/subghz/protocols/somfy_keytis.h | 125 ++ .../main/subghz/protocols/somfy_telis.c | 663 ++++++++++ .../main/subghz/protocols/somfy_telis.h | 125 ++ .../main/subghz/protocols/star_line.c | 780 ++++++++++++ .../main/subghz/protocols/star_line.h | 131 ++ applications/main/subghz/receiver.c | 124 ++ applications/main/subghz/receiver.h | 73 ++ applications/main/subghz/registry.c | 30 + applications/main/subghz/registry.h | 47 + .../main/subghz/scenes/subghz_scene_config.h | 1 + .../subghz/scenes/subghz_scene_decode_raw.c | 5 - .../scenes/subghz_scene_delete_success.c | 1 - .../scenes/subghz_scene_ext_module_settings.c | 92 ++ .../subghz/scenes/subghz_scene_read_raw.c | 11 +- .../subghz/scenes/subghz_scene_receiver.c | 19 +- .../scenes/subghz_scene_receiver_config.c | 426 +------ .../scenes/subghz_scene_receiver_info.c | 17 +- .../scenes/subghz_scene_show_error_sub.c | 12 +- .../main/subghz/scenes/subghz_scene_start.c | 50 +- applications/main/subghz/subghz.c | 48 +- applications/main/subghz/subghz_cli.c | 115 +- .../main/subghz/subghz_file_encoder_worker.c | 244 ++++ .../main/subghz/subghz_file_encoder_worker.h | 65 + applications/main/subghz/subghz_history.c | 763 +---------- applications/main/subghz/subghz_history.h | 7 - applications/main/subghz/subghz_i.c | 44 +- applications/main/subghz/subghz_i.h | 18 +- applications/main/subghz/subghz_keystore.c | 613 +++++++++ applications/main/subghz/subghz_keystore.h | 80 ++ .../main/subghz/subghz_last_settings.c | 43 +- .../main/subghz/subghz_last_settings.h | 14 - applications/main/subghz/subghz_setting.c | 480 +++++++ applications/main/subghz/subghz_setting.h | 58 + .../main/subghz/subghz_tx_rx_worker.c | 260 ++++ .../main/subghz/subghz_tx_rx_worker.h | 89 ++ applications/main/subghz/subghz_worker.c | 149 +++ applications/main/subghz/subghz_worker.h | 80 ++ applications/main/subghz/transmitter.c | 64 + applications/main/subghz/transmitter.h | 55 + applications/main/subghz/types.h | 104 ++ applications/main/subghz/views/receiver.c | 59 +- applications/main/subghz/views/receiver.h | 2 + .../subghz/views/subghz_frequency_analyzer.c | 9 +- .../main/subghz/views/subghz_read_raw.c | 21 +- .../main/subghz/views/subghz_test_carrier.c | 12 +- .../main/subghz/views/subghz_test_static.c | 5 +- applications/main/subghz/views/transmitter.c | 9 +- firmware/targets/f7/api_symbols.csv | 681 +++++++++- .../targets/f7/furi_hal/furi_hal_resources.c | 28 + .../targets/f7/furi_hal/furi_hal_resources.h | 12 + .../targets/f7/furi_hal/furi_hal_spi_config.c | 21 +- .../targets/f7/furi_hal/furi_hal_spi_config.h | 6 +- .../targets/f7/furi_hal/furi_hal_subghz.c | 616 +++++---- .../targets/f7/furi_hal/furi_hal_subghz.h | 51 + .../f7/furi_hal/furi_hal_subghz_configs.h | 10 - lib/subghz/SConscript | 1 + lib/subghz/blocks/encoder.c | 2 +- lib/subghz/blocks/generic.c | 2 +- lib/subghz/blocks/generic.h | 2 +- lib/subghz/protocols/bin_raw.c | 4 +- lib/subghz/protocols/kinggates_stylo_4k.c | 287 ++++- lib/subghz/protocols/kinggates_stylo_4k.h | 36 + lib/subghz/protocols/protocol_items.c | 2 +- lib/subghz/protocols/protocol_items.h | 2 +- lib/subghz/protocols/raw.c | 182 +-- lib/subghz/protocols/raw.h | 58 +- lib/subghz/protocols/secplus_v2.c | 8 +- lib/subghz/receiver.c | 5 - lib/subghz/receiver.h | 7 - lib/subghz/subghz_tx_rx_worker.c | 12 +- lib/subghz/types.h | 1 + 174 files changed, 31269 insertions(+), 1885 deletions(-) create mode 100644 applications/main/subghz/SConscript create mode 100644 applications/main/subghz/blocks/const.c create mode 100644 applications/main/subghz/blocks/const.h create mode 100644 applications/main/subghz/blocks/decoder.c create mode 100644 applications/main/subghz/blocks/decoder.h create mode 100644 applications/main/subghz/blocks/encoder.c create mode 100644 applications/main/subghz/blocks/encoder.h create mode 100644 applications/main/subghz/blocks/generic.c create mode 100644 applications/main/subghz/blocks/generic.h create mode 100644 applications/main/subghz/blocks/math.c create mode 100644 applications/main/subghz/blocks/math.h create mode 100644 applications/main/subghz/environment.c create mode 100644 applications/main/subghz/environment.h create mode 100644 applications/main/subghz/protocols/alutech_at_4n.c create mode 100644 applications/main/subghz/protocols/alutech_at_4n.h create mode 100644 applications/main/subghz/protocols/ansonic.c create mode 100644 applications/main/subghz/protocols/ansonic.h create mode 100644 applications/main/subghz/protocols/base.c create mode 100644 applications/main/subghz/protocols/base.h create mode 100644 applications/main/subghz/protocols/bett.c create mode 100644 applications/main/subghz/protocols/bett.h create mode 100644 applications/main/subghz/protocols/bin_raw.c create mode 100644 applications/main/subghz/protocols/bin_raw.h create mode 100644 applications/main/subghz/protocols/came.c create mode 100644 applications/main/subghz/protocols/came.h create mode 100644 applications/main/subghz/protocols/came_atomo.c create mode 100644 applications/main/subghz/protocols/came_atomo.h create mode 100644 applications/main/subghz/protocols/came_twee.c create mode 100644 applications/main/subghz/protocols/came_twee.h create mode 100644 applications/main/subghz/protocols/chamberlain_code.c create mode 100644 applications/main/subghz/protocols/chamberlain_code.h create mode 100644 applications/main/subghz/protocols/clemsa.c create mode 100644 applications/main/subghz/protocols/clemsa.h create mode 100644 applications/main/subghz/protocols/doitrand.c create mode 100644 applications/main/subghz/protocols/doitrand.h create mode 100644 applications/main/subghz/protocols/dooya.c create mode 100644 applications/main/subghz/protocols/dooya.h create mode 100644 applications/main/subghz/protocols/faac_slh.c create mode 100644 applications/main/subghz/protocols/faac_slh.h create mode 100644 applications/main/subghz/protocols/gate_tx.c create mode 100644 applications/main/subghz/protocols/gate_tx.h create mode 100644 applications/main/subghz/protocols/holtek.c create mode 100644 applications/main/subghz/protocols/holtek.h create mode 100644 applications/main/subghz/protocols/holtek_ht12x.c create mode 100644 applications/main/subghz/protocols/holtek_ht12x.h create mode 100644 applications/main/subghz/protocols/honeywell_wdb.c create mode 100644 applications/main/subghz/protocols/honeywell_wdb.h create mode 100644 applications/main/subghz/protocols/hormann.c create mode 100644 applications/main/subghz/protocols/hormann.h create mode 100644 applications/main/subghz/protocols/ido.c create mode 100644 applications/main/subghz/protocols/ido.h create mode 100644 applications/main/subghz/protocols/intertechno_v3.c create mode 100644 applications/main/subghz/protocols/intertechno_v3.h create mode 100644 applications/main/subghz/protocols/keeloq.c create mode 100644 applications/main/subghz/protocols/keeloq.h create mode 100644 applications/main/subghz/protocols/keeloq_common.c create mode 100644 applications/main/subghz/protocols/keeloq_common.h create mode 100644 applications/main/subghz/protocols/kia.c create mode 100644 applications/main/subghz/protocols/kia.h create mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.c create mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.h create mode 100644 applications/main/subghz/protocols/linear.c create mode 100644 applications/main/subghz/protocols/linear.h create mode 100644 applications/main/subghz/protocols/linear_delta3.c create mode 100644 applications/main/subghz/protocols/linear_delta3.h create mode 100644 applications/main/subghz/protocols/magellan.c create mode 100644 applications/main/subghz/protocols/magellan.h create mode 100644 applications/main/subghz/protocols/marantec.c create mode 100644 applications/main/subghz/protocols/marantec.h create mode 100644 applications/main/subghz/protocols/megacode.c create mode 100644 applications/main/subghz/protocols/megacode.h create mode 100644 applications/main/subghz/protocols/nero_radio.c create mode 100644 applications/main/subghz/protocols/nero_radio.h create mode 100644 applications/main/subghz/protocols/nero_sketch.c create mode 100644 applications/main/subghz/protocols/nero_sketch.h create mode 100644 applications/main/subghz/protocols/nice_flo.c create mode 100644 applications/main/subghz/protocols/nice_flo.h create mode 100644 applications/main/subghz/protocols/nice_flor_s.c create mode 100644 applications/main/subghz/protocols/nice_flor_s.h create mode 100644 applications/main/subghz/protocols/phoenix_v2.c create mode 100644 applications/main/subghz/protocols/phoenix_v2.h create mode 100644 applications/main/subghz/protocols/power_smart.c create mode 100644 applications/main/subghz/protocols/power_smart.h create mode 100644 applications/main/subghz/protocols/princeton.c create mode 100644 applications/main/subghz/protocols/princeton.h create mode 100644 applications/main/subghz/protocols/princeton_for_testing.c create mode 100644 applications/main/subghz/protocols/princeton_for_testing.h create mode 100644 applications/main/subghz/protocols/protocol_items.c create mode 100644 applications/main/subghz/protocols/protocol_items.h create mode 100644 applications/main/subghz/protocols/raw.c create mode 100644 applications/main/subghz/protocols/raw.h create mode 100644 applications/main/subghz/protocols/scher_khan.c create mode 100644 applications/main/subghz/protocols/scher_khan.h create mode 100644 applications/main/subghz/protocols/secplus_v1.c create mode 100644 applications/main/subghz/protocols/secplus_v1.h create mode 100644 applications/main/subghz/protocols/secplus_v2.c create mode 100644 applications/main/subghz/protocols/secplus_v2.h create mode 100644 applications/main/subghz/protocols/smc5326.c create mode 100644 applications/main/subghz/protocols/smc5326.h create mode 100644 applications/main/subghz/protocols/somfy_keytis.c create mode 100644 applications/main/subghz/protocols/somfy_keytis.h create mode 100644 applications/main/subghz/protocols/somfy_telis.c create mode 100644 applications/main/subghz/protocols/somfy_telis.h create mode 100644 applications/main/subghz/protocols/star_line.c create mode 100644 applications/main/subghz/protocols/star_line.h create mode 100644 applications/main/subghz/receiver.c create mode 100644 applications/main/subghz/receiver.h create mode 100644 applications/main/subghz/registry.c create mode 100644 applications/main/subghz/registry.h create mode 100644 applications/main/subghz/scenes/subghz_scene_ext_module_settings.c create mode 100644 applications/main/subghz/subghz_file_encoder_worker.c create mode 100644 applications/main/subghz/subghz_file_encoder_worker.h create mode 100644 applications/main/subghz/subghz_keystore.c create mode 100644 applications/main/subghz/subghz_keystore.h create mode 100644 applications/main/subghz/subghz_setting.c create mode 100644 applications/main/subghz/subghz_setting.h create mode 100644 applications/main/subghz/subghz_tx_rx_worker.c create mode 100644 applications/main/subghz/subghz_tx_rx_worker.h create mode 100644 applications/main/subghz/subghz_worker.c create mode 100644 applications/main/subghz/subghz_worker.h create mode 100644 applications/main/subghz/transmitter.c create mode 100644 applications/main/subghz/transmitter.h create mode 100644 applications/main/subghz/types.h diff --git a/applications/main/subghz/SConscript b/applications/main/subghz/SConscript new file mode 100644 index 000000000..8fbec94ad --- /dev/null +++ b/applications/main/subghz/SConscript @@ -0,0 +1,32 @@ +Import("env") + +env.Append( + CPPPATH=[ + "#/lib/subghz", + ], + SDK_HEADERS=[ + File("environment.h"), + File("receiver.h"), + File("subghz_worker.h"), + File("subghz_tx_rx_worker.h"), + File("transmitter.h"), + File("registry.h"), + File("protocols/protocol_items.h"), + File("protocols/raw.h"), + File("blocks/const.h"), + File("blocks/decoder.h"), + File("blocks/encoder.h"), + File("blocks/generic.h"), + File("blocks/math.h"), + File("subghz_setting.h"), + ], +) + +libenv = env.Clone(FW_LIB_NAME="subghz") +libenv.ApplyLibFlags() + +sources = libenv.GlobRecursive("*.c*") + +lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) +libenv.Install("${LIB_DIST_DIR}", lib) +Return("lib") diff --git a/applications/main/subghz/application.fam b/applications/main/subghz/application.fam index 6df4b1a8a..f0dc66e89 100644 --- a/applications/main/subghz/application.fam +++ b/applications/main/subghz/application.fam @@ -12,7 +12,7 @@ App( ], provides=["subghz_start"], icon="A_Sub1ghz_14", - stack_size=2 * 1024, + stack_size=3 * 1024, order=10, ) diff --git a/applications/main/subghz/blocks/const.c b/applications/main/subghz/blocks/const.c new file mode 100644 index 000000000..15719b2ac --- /dev/null +++ b/applications/main/subghz/blocks/const.c @@ -0,0 +1 @@ +#include "const.h" diff --git a/applications/main/subghz/blocks/const.h b/applications/main/subghz/blocks/const.h new file mode 100644 index 000000000..f32334e2f --- /dev/null +++ b/applications/main/subghz/blocks/const.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + const uint16_t te_long; + const uint16_t te_short; + const uint16_t te_delta; + const uint8_t min_count_bit_for_found; +} SubGhzBlockConst; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/decoder.c b/applications/main/subghz/blocks/decoder.c new file mode 100644 index 000000000..f491c87bf --- /dev/null +++ b/applications/main/subghz/blocks/decoder.c @@ -0,0 +1,27 @@ +#include "decoder.h" + +#define TAG "SubGhzBlockDecoder" + +void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { + decoder->decode_data = decoder->decode_data << 1 | bit; + decoder->decode_count_bit++; +} + +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit) { + if(++decoder->decode_count_bit > 64) { + (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); + } + decoder->decode_data = decoder->decode_data << 1 | bit; +} + +uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { + uint8_t hash = 0; + uint8_t* p = (uint8_t*)&decoder->decode_data; + for(size_t i = 0; i < len; i++) { + hash ^= p[i]; + } + return hash; +} diff --git a/applications/main/subghz/blocks/decoder.h b/applications/main/subghz/blocks/decoder.h new file mode 100644 index 000000000..a5e561e35 --- /dev/null +++ b/applications/main/subghz/blocks/decoder.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; + +struct SubGhzBlockDecoder { + uint32_t parser_step; + uint32_t te_last; + uint64_t decode_data; + uint8_t decode_count_bit; +}; + +/** + * Add data bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); + +/** + * Add data to_128 bit when decoding. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @param head_64_bit Pointer to a head_64_bit + * @param bit data, 1bit + */ +void subghz_protocol_blocks_add_to_128_bit( + SubGhzBlockDecoder* decoder, + uint8_t bit, + uint64_t* head_64_bit); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param decoder Pointer to a SubGhzBlockDecoder instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/encoder.c b/applications/main/subghz/blocks/encoder.c new file mode 100644 index 000000000..49ec4f177 --- /dev/null +++ b/applications/main/subghz/blocks/encoder.c @@ -0,0 +1,58 @@ +#include "encoder.h" +#include "math.h" +#include + +#include "furi.h" + +#define TAG "SubGhzBlockEncoder" + +void subghz_protocol_blocks_set_bit_array( + bool bit_value, + uint8_t data_array[], + size_t set_index_bit, + size_t max_size_array) { + furi_assert(set_index_bit < max_size_array * 8); + bit_write(data_array[set_index_bit >> 3], 7 - (set_index_bit & 0x7), bit_value); +} + +bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit) { + return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); +} + +size_t subghz_protocol_blocks_get_upload_from_bit_array( + uint8_t data_array[], + size_t count_bit_data_array, + LevelDuration* upload, + size_t max_size_upload, + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit) { + size_t bias_bit = 0; + size_t size_upload = 0; + uint32_t duration = duration_bit; + + if(align_bit == SubGhzProtocolBlockAlignBitRight) { + if(count_bit_data_array & 0x7) { + bias_bit = 8 - (count_bit_data_array & 0x7); + } + } + size_t index_bit = bias_bit; + + bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); + for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { + if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { + duration += duration_bit; + } else { + if(size_upload > max_size_upload) { + furi_crash("SubGhz: Encoder buffer overflow"); + } + upload[size_upload++] = level_duration_make( + subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); + last_bit = !last_bit; + duration = duration_bit; + } + index_bit++; + } + upload[size_upload++] = level_duration_make( + subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); + return size_upload; +} diff --git a/applications/main/subghz/blocks/encoder.h b/applications/main/subghz/blocks/encoder.h new file mode 100644 index 000000000..aeaa2add0 --- /dev/null +++ b/applications/main/subghz/blocks/encoder.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + bool is_running; + size_t repeat; + size_t front; + size_t size_upload; + LevelDuration* upload; + +} SubGhzProtocolBlockEncoder; + +typedef enum { + SubGhzProtocolBlockAlignBitLeft, + SubGhzProtocolBlockAlignBitRight, +} SubGhzProtocolBlockAlignBit; + +/** + * Set data bit when encoding HEX array. + * @param bit_value The value of the bit to be set + * @param data_array Pointer to a HEX array + * @param set_index_bit Number set a bit in the array starting from the left + * @param max_size_array array size, check not to overflow + */ +void subghz_protocol_blocks_set_bit_array( + bool bit_value, + uint8_t data_array[], + size_t set_index_bit, + size_t max_size_array); + +/** + * Get data bit when encoding HEX array. + * @param data_array Pointer to a HEX array + * @param read_index_bit Number get a bit in the array starting from the left + * @return bool value bit + */ +bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit); + +/** + * Generating an upload from data. + * @param data_array Pointer to a HEX array + * @param count_bit_data_array How many bits in the array are processed + * @param upload Pointer to a LevelDuration + * @param max_size_upload upload size, check not to overflow + * @param duration_bit duration 1 bit + * @param align_bit alignment of useful bits in an array + */ +size_t subghz_protocol_blocks_get_upload_from_bit_array( + uint8_t data_array[], + size_t count_bit_data_array, + LevelDuration* upload, + size_t max_size_upload, + uint32_t duration_bit, + SubGhzProtocolBlockAlignBit align_bit); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/generic.c b/applications/main/subghz/blocks/generic.c new file mode 100644 index 000000000..3d59adc82 --- /dev/null +++ b/applications/main/subghz/blocks/generic.c @@ -0,0 +1,120 @@ +#include "generic.h" +#include +#include + +#define TAG "SubGhzBlockGeneric" + +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { + const char* preset_name_temp; + if(!strcmp(preset_name, "AM270")) { + preset_name_temp = "FuriHalSubGhzPresetOok270Async"; + } else if(!strcmp(preset_name, "AM650")) { + preset_name_temp = "FuriHalSubGhzPresetOok650Async"; + } else if(!strcmp(preset_name, "FM238")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; + } else if(!strcmp(preset_name, "FM476")) { + preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; + } else { + preset_name_temp = "FuriHalSubGhzPresetCustom"; + } + furi_string_set(preset_str, preset_name_temp); +} + +bool subghz_block_generic_serialize( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + uint32_t temp = instance->data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + } + + if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + instance->data_count_bit = (uint16_t)temp_data; + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Key"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->data = instance->data << 8 | key_data[i]; + } + + res = true; + } while(0); + + furi_string_free(temp_str); + + return res; +} diff --git a/applications/main/subghz/blocks/generic.h b/applications/main/subghz/blocks/generic.h new file mode 100644 index 000000000..e69de8b4f --- /dev/null +++ b/applications/main/subghz/blocks/generic.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include "../types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; + +struct SubGhzBlockGeneric { + const char* protocol_name; + uint64_t data; + uint64_t data_2; + uint32_t serial; + uint16_t data_count_bit; + uint8_t btn; + uint32_t cnt; + uint8_t cnt_2; + uint32_t seed; +}; + +/** + * Get name preset. + * @param preset_name name preset + * @param preset_str Output name preset + */ +void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); + +/** + * Serialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_block_generic_serialize( + SubGhzBlockGeneric* instance, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzBlockGeneric. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.c b/applications/main/subghz/blocks/math.c new file mode 100644 index 000000000..24202ad1c --- /dev/null +++ b/applications/main/subghz/blocks/math.c @@ -0,0 +1,244 @@ +#include "math.h" + +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { + uint64_t reverse_key = 0; + for(uint8_t i = 0; i < bit_count; i++) { + reverse_key = reverse_key << 1 | bit_read(key, i); + } + return reverse_key; +} + +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { + uint8_t parity = 0; + for(uint8_t i = 0; i < bit_count; i++) { + parity += bit_read(key, i); + } + return parity & 0x01; +} + +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 4; // LSBs are unused + uint8_t poly = polynomial << 4; + uint8_t bit; + + while(size--) { + remainder ^= *message++; + for(bit = 0; bit < 8; bit++) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 4 & 0x0f; // discard the LSBs +} + +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init << 1; // LSB is unused + uint8_t poly = polynomial << 1; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ poly; + } else { + remainder = (remainder << 1); + } + } + } + return remainder >> 1 & 0x7f; // discard the LSB +} + +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x80) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init) { + uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); + polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte]; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 1) { + remainder = (remainder >> 1) ^ polynomial; + } else { + remainder = (remainder >> 1); + } + } + } + return remainder; +} + +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init) { + uint16_t remainder = init; + + for(size_t byte = 0; byte < size; ++byte) { + remainder ^= message[byte] << 8; + for(uint8_t bit = 0; bit < 8; ++bit) { + if(remainder & 0x8000) { + remainder = (remainder << 1) ^ polynomial; + } else { + remainder = (remainder << 1); + } + } + } + return remainder; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int i = 7; i >= 0; --i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key) { + uint8_t sum = 0; + // Process message from last byte to first byte (reflected) + for(int byte = size - 1; byte >= 0; --byte) { + uint8_t data = message[byte]; + // Process individual bits of each byte (reflected) + for(uint8_t i = 0; i < 8; ++i) { + // XOR key into sum if data bit is set + if((data >> i) & 1) { + sum ^= key; + } + + // roll the key left (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped lsb as MSB) + if(key & 0x80) + key = (key << 1) ^ gen; + else + key = (key << 1); + } + } + return sum; +} + +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key) { + uint16_t sum = 0; + for(size_t byte = 0; byte < size; ++byte) { + uint8_t data = message[byte]; + for(int8_t i = 7; i >= 0; --i) { + // if data bit is set then xor with key + if((data >> i) & 1) sum ^= key; + + // roll the key right (actually the LSB is dropped here) + // and apply the gen (needs to include the dropped LSB as MSB) + if(key & 1) + key = (key >> 1) ^ gen; + else + key = (key >> 1); + } + } + return sum; +} + +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { + uint32_t result = 0; + for(size_t i = 0; i < size; ++i) { + result += message[i]; + } + return (uint8_t)result; +} + +uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { + byte ^= byte >> 4; + byte &= 0xf; + return (0x6996 >> byte) & 1; +} + +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= subghz_protocol_blocks_parity8(message[i]); + } + return result; +} + +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { + uint8_t result = 0; + for(size_t i = 0; i < size; ++i) { + result ^= message[i]; + } + return result; +} \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.h b/applications/main/subghz/blocks/math.h new file mode 100644 index 000000000..dcea3da5f --- /dev/null +++ b/applications/main/subghz/blocks/math.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#include +#include + +#define bit_read(value, bit) (((value) >> (bit)) & 0x01) +#define bit_set(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) |= (_one << (bit)); \ + }) +#define bit_clear(value, bit) \ + ({ \ + __typeof__(value) _one = (1); \ + (value) &= ~(_one << (bit)); \ + }) +#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) +#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifdef __cplusplus +extern "C" { +#endif + +/** Flip the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return Reverse data + */ +uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); + +/** Get parity the data bitwise + * + * @param key In data + * @param bit_count number of data bits + * + * @return parity + */ +uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); + +/** CRC-4 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc4( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-7 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc7( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + + * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal + * bit-by-bit parity XOR) + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are + * reflected, i.e. least significant bit is shifted in first + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint8_t subghz_protocol_blocks_crc8le( + uint8_t const message[], + size_t size, + uint8_t polynomial, + uint8_t init); + +/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is + * shifted in first. Note that poly and init already need to be reflected + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16lsb( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** CRC-16 + * + * @param message array of bytes to check + * @param size number of bytes in message + * @param polynomial CRC polynomial + * @param init starting crc value + * + * @return CRC value + */ +uint16_t subghz_protocol_blocks_crc16( + uint8_t const message[], + size_t size, + uint16_t polynomial, + uint16_t init); + +/** Digest-8 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( + uint8_t const message[], + size_t size, + uint8_t gen, + uint8_t key); + +/** Digest-16 by "LFSR-based Toeplitz hash" + * + * @param message bytes of message data + * @param size number of bytes to digest + * @param gen key stream generator, needs to includes the MSB if the + * LFSR is rolling + * @param key initial key + * + * @return digest value + */ +uint16_t subghz_protocol_blocks_lfsr_digest16( + uint8_t const message[], + size_t size, + uint16_t gen, + uint16_t key); + +/** Compute Addition of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value + */ +uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); + +/** Compute bit parity of a single byte (8 bits) + * + * @param byte single byte to check + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity8(uint8_t byte); + +/** Compute bit parity of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); + +/** Compute XOR (byte-wide parity) of a number of bytes + * + * @param message bytes of message data + * @param size number of bytes to sum + * + * @return summation value, per bit-position 1 odd parity, 0 even parity + */ +uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/environment.c b/applications/main/subghz/environment.c new file mode 100644 index 000000000..b39b259d4 --- /dev/null +++ b/applications/main/subghz/environment.c @@ -0,0 +1,116 @@ +#include "environment.h" +#include "registry.h" + +struct SubGhzEnvironment { + SubGhzKeystore* keystore; + const SubGhzProtocolRegistry* protocol_registry; + const char* came_atomo_rainbow_table_file_name; + const char* nice_flor_s_rainbow_table_file_name; + const char* alutech_at_4n_rainbow_table_file_name; +}; + +SubGhzEnvironment* subghz_environment_alloc() { + SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); + + instance->keystore = subghz_keystore_alloc(); + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; + + return instance; +} + +void subghz_environment_free(SubGhzEnvironment* instance) { + furi_assert(instance); + + instance->protocol_registry = NULL; + instance->came_atomo_rainbow_table_file_name = NULL; + instance->nice_flor_s_rainbow_table_file_name = NULL; + subghz_keystore_free(instance->keystore); + + free(instance); +} + +bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename) { + furi_assert(instance); + + return subghz_keystore_load(instance->keystore, filename); +} + +SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->keystore; +} + +void subghz_environment_set_came_atomo_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->came_atomo_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->came_atomo_rainbow_table_file_name; +} + +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->alutech_at_4n_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->alutech_at_4n_rainbow_table_file_name; +} + +void subghz_environment_set_nice_flor_s_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename) { + furi_assert(instance); + + instance->nice_flor_s_rainbow_table_file_name = filename; +} + +const char* + subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance) { + furi_assert(instance); + + return instance->nice_flor_s_rainbow_table_file_name; +} + +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items) { + furi_assert(instance); + const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; + instance->protocol_registry = protocol_registry; +} + +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + return (void*)instance->protocol_registry; +} + +const char* + subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { + furi_assert(instance); + furi_assert(instance->protocol_registry); + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); + if(protocol != NULL) { + return protocol->name; + } else { + return NULL; + } +} \ No newline at end of file diff --git a/applications/main/subghz/environment.h b/applications/main/subghz/environment.h new file mode 100644 index 000000000..7bd38ba2f --- /dev/null +++ b/applications/main/subghz/environment.h @@ -0,0 +1,115 @@ +#pragma once + +#include + +#include "subghz_keystore.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +/** + * Allocate SubGhzEnvironment. + * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance + */ +SubGhzEnvironment* subghz_environment_alloc(); + +/** + * Free SubGhzEnvironment. + * @param instance Pointer to a SubGhzEnvironment instance + */ +void subghz_environment_free(SubGhzEnvironment* instance); + +/** + * Downloading the manufacture key file. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + * @return true On success + */ +bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename); + +/** + * Get pointer to a SubGhzKeystore* instance. + * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance + */ +SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance); + +/** + * Set filename to work with Came Atomo. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_came_atomo_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Came Atomo. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Alutech at-4n. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set filename to work with Nice Flor-S. + * @param instance Pointer to a SubGhzEnvironment instance + * @param filename Full path to the file + */ +void subghz_environment_set_nice_flor_s_rainbow_table_file_name( + SubGhzEnvironment* instance, + const char* filename); + +/** + * Get filename to work with Nice Flor-S. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Full path to the file + */ +const char* + subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); + +/** + * Set list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry + */ +void subghz_environment_set_protocol_registry( + SubGhzEnvironment* instance, + void* protocol_registry_items); + +/** + * Get list of protocols to work. + * @param instance Pointer to a SubGhzEnvironment instance + * @return Pointer to a SubGhzProtocolRegistry + */ +void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); + +/** + * Get list of protocols names. + * @param instance Pointer to a SubGhzEnvironment instance + * @param idx index protocols + * @return Pointer to a SubGhzProtocolRegistry + */ +const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index 0559ac67e..350e68ee6 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -50,7 +50,6 @@ typedef enum { SubGhzCustomEventSceneAnalyzerLock, SubGhzCustomEventSceneAnalyzerUnlock, SubGhzCustomEventSceneSettingLock, - SubGhzCustomEventSceneSettingError, SubGhzCustomEventSceneExit, SubGhzCustomEventSceneStay, diff --git a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c index 7dc67c9f2..6452792a6 100644 --- a/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c +++ b/applications/main/subghz/helpers/subghz_frequency_analyzer_worker.c @@ -36,13 +36,13 @@ struct SubGhzFrequencyAnalyzerWorker { }; static void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); size_t i = 0; while(data[i][0]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i][0], data[i][1]); i++; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } // running average with adaptive coefficient @@ -80,26 +80,26 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { //Start CC1101 furi_hal_subghz_reset(); - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3, + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_rx(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_MDMCFG3, 0b01111111); // symbol rate cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL2, 0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL1, 0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, CC1101_AGCCTRL0, 0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); @@ -118,20 +118,23 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { // First stage: coarse scan for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) { if(furi_hal_subghz_is_frequency_valid( - subghz_setting_get_frequency(instance->setting, i))) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + subghz_setting_get_frequency(instance->setting, i)) && + !((furi_hal_subghz.radio_type == SubGhzRadioExternal) && + (subghz_setting_get_frequency(instance->setting, i) >= 311900000 && + subghz_setting_get_frequency(instance->setting, i) <= 312200000))) { + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); frequency = cc1101_set_frequency( - &furi_hal_spi_bus_handle_subghz, + furi_hal_subghz.spi_bus_handle, subghz_setting_get_frequency(instance->setting, i)); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); } while(status.STATE != CC1101StateIDLE); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_delay_ms(2); @@ -166,17 +169,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) { i < frequency_rssi.frequency_coarse + 300000; i += 20000) { if(furi_hal_subghz_is_frequency_valid(i)) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, i); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); do { - status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); } while(status.STATE != CC1101StateIDLE); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); furi_delay_ms(2); diff --git a/applications/main/subghz/protocols/alutech_at_4n.c b/applications/main/subghz/protocols/alutech_at_4n.c new file mode 100644 index 000000000..6bcf9f25d --- /dev/null +++ b/applications/main/subghz/protocols/alutech_at_4n.c @@ -0,0 +1,455 @@ +#include "alutech_at_4n.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoAlutech_at_4n" + +#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF + +static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 72, +}; + +struct SubGhzProtocolDecoderAlutech_at_4n { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint64_t data; + uint32_t crc; + uint16_t header_count; + + const char* alutech_at_4n_rainbow_table_file_name; +}; + +struct SubGhzProtocolEncoderAlutech_at_4n { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Alutech_at_4nDecoderStepReset = 0, + Alutech_at_4nDecoderStepCheckPreambula, + Alutech_at_4nDecoderStepSaveDuration, + Alutech_at_4nDecoderStepCheckDuration, +} Alutech_at_4nDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { + .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, + .free = subghz_protocol_decoder_alutech_at_4n_free, + + .feed = subghz_protocol_decoder_alutech_at_4n_feed, + .reset = subghz_protocol_decoder_alutech_at_4n_reset, + + .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, + .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, + .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, + .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_alutech_at_4n = { + .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_alutech_at_4n_decoder, + .encoder = &subghz_protocol_alutech_at_4n_encoder, +}; + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param number_alutech_at_4n_magic_data number in the array + * @return alutech_at_4n_magic_data + */ +static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( + const char* file_name, + uint8_t number_alutech_at_4n_magic_data) { + if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + + uint8_t buffer[sizeof(uint32_t)] = {0}; + uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); + uint32_t alutech_at_4n_magic_data = 0; + + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { + for(size_t i = 0; i < sizeof(uint32_t); i++) { + alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; + } + } else { + alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; + } + return alutech_at_4n_magic_data; +} + +static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { + uint8_t* p = (uint8_t*)&data; + uint8_t crc = 0xff; + for(uint8_t y = 0; y < 8; y++) { + crc = crc ^ p[y]; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + } + return crc; +} + +static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { + uint8_t crc = data; + for(uint8_t i = 0; i < 8; i++) { + if((crc & 0x80) != 0) { + crc <<= 1; + crc ^= 0x31; + } else { + crc <<= 1; + } + } + return ~crc; +} + +static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t data3 = 0; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; + + uint32_t i = magic_data[0]; + do { + data2 = data2 - + ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); + data3 = data2 + i; + i += magic_data[3]; + data1 = + data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); + } while(i != 0); + + p[0] = (uint8_t)(data1 >> 24); + p[1] = (uint8_t)(data1 >> 16); + p[3] = (uint8_t)data1; + p[4] = (uint8_t)(data2 >> 24); + p[5] = (uint8_t)(data2 >> 16); + p[2] = (uint8_t)(data1 >> 8); + p[6] = (uint8_t)(data2 >> 8); + p[7] = (uint8_t)data2; + + return data; +} + +// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { +// uint8_t* p = (uint8_t*)&data; +// uint32_t data1 = 0; +// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; +// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; +// uint32_t magic_data[] = { +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), +// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; + +// do { +// data1 = data1 + magic_data[0]; +// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ +// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); +// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ +// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); +// } while(data1 != magic_data[5]); +// p[0] = (uint8_t)(data2 >> 24); +// p[1] = (uint8_t)(data2 >> 16); +// p[3] = (uint8_t)data2; +// p[4] = (uint8_t)(data3 >> 24); +// p[5] = (uint8_t)(data3 >> 16); +// p[2] = (uint8_t)(data2 >> 8); +// p[6] = (uint8_t)(data3 >> 8); +// p[7] = (uint8_t)data3; + +// return data; +// } + +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->alutech_at_4n_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; +} + +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + + switch(instance->decoder.parser_step) { + case Alutech_at_4nDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case Alutech_at_4nDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < + subghz_protocol_alutech_at_4n_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + case Alutech_at_4nDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; + } + break; + case Alutech_at_4nDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + + subghz_protocol_alutech_at_4n_const.te_delta)) { + //add last bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + + // Found end TX + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + if(instance->generic.data != instance->data) { + instance->generic.data = instance->data; + + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + instance->crc = instance->decoder.decode_data; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < + subghz_protocol_alutech_at_4n_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < + subghz_protocol_alutech_at_4n_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + if(instance->decoder.decode_count_bit == 64) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_alutech_at_4n_remote_controller( + SubGhzBlockGeneric* instance, + uint8_t crc, + const char* file_name) { + /** + * Message format 72bit LSB first + * data crc + * XXXXXXXXXXXXXXXX CC + * + * For analysis, you need to turn the package MSB + * in decoded messages format + * + * crc1 serial cnt key + * cc SSSSSSSS XXxx BB + * + * crc1 is calculated from the lower part of cnt + * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 + * + */ + + bool status = false; + uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); + crc = subghz_protocol_blocks_reverse_key(crc, 8); + + if(crc == subghz_protocol_alutech_at_4n_crc(data)) { + data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); + status = true; + } + + if(status && ((uint8_t)(data >> 56) == + subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { + instance->btn = (uint8_t)data & 0xFF; + instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(!status) { + instance->btn = 0; + instance->cnt = 0; + instance->serial = 0; + } +} + +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + return (uint8_t)instance->crc; +} + +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { + FURI_LOG_E(TAG, "Missing CRC"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAlutech_at_4n* instance = context; + subghz_protocol_alutech_at_4n_remote_controller( + &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %d\r\n" + "Key:0x%08lX%08lX%02X\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + (uint8_t)instance->crc, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/alutech_at_4n.h b/applications/main/subghz/protocols/alutech_at_4n.h new file mode 100644 index 000000000..38bac3ea6 --- /dev/null +++ b/applications/main/subghz/protocols/alutech_at_4n.h @@ -0,0 +1,74 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" + +typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; +typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; + +extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; +extern const SubGhzProtocol subghz_protocol_alutech_at_4n; + +/** + * Allocate SubGhzProtocolDecoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + */ +void subghz_protocol_decoder_alutech_at_4n_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance + * @param output Resulting text + */ +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ansonic.c b/applications/main/subghz/protocols/ansonic.c new file mode 100644 index 000000000..81b196e36 --- /dev/null +++ b/applications/main/subghz/protocols/ansonic.c @@ -0,0 +1,346 @@ +#include "ansonic.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolAnsonic" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_ansonic_const = { + .te_short = 555, + .te_long = 1111, + .te_delta = 120, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderAnsonic { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderAnsonic { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + AnsonicDecoderStepReset = 0, + AnsonicDecoderStepFoundStartBit, + AnsonicDecoderStepSaveDuration, + AnsonicDecoderStepCheckDuration, +} AnsonicDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { + .alloc = subghz_protocol_decoder_ansonic_alloc, + .free = subghz_protocol_decoder_ansonic_free, + + .feed = subghz_protocol_decoder_ansonic_feed, + .reset = subghz_protocol_decoder_ansonic_reset, + + .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, + .serialize = subghz_protocol_decoder_ansonic_serialize, + .deserialize = subghz_protocol_decoder_ansonic_deserialize, + .get_string = subghz_protocol_decoder_ansonic_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { + .alloc = subghz_protocol_encoder_ansonic_alloc, + .free = subghz_protocol_encoder_ansonic_free, + + .deserialize = subghz_protocol_encoder_ansonic_deserialize, + .stop = subghz_protocol_encoder_ansonic_stop, + .yield = subghz_protocol_encoder_ansonic_yield, +}; + +const SubGhzProtocol subghz_protocol_ansonic = { + .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_ansonic_decoder, + .encoder = &subghz_protocol_ansonic_encoder, +}; + +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); + + instance->base.protocol = &subghz_protocol_ansonic; + 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_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return true On success + */ +static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); + } + } + return true; +} + +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAnsonic* 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_ansonic_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); + + if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_ansonic_stop(void* context) { + SubGhzProtocolEncoderAnsonic* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { + SubGhzProtocolEncoderAnsonic* 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_ansonic_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); + instance->base.protocol = &subghz_protocol_ansonic; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_ansonic_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ansonic_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + instance->decoder.parser_step = AnsonicDecoderStepReset; +} + +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + + switch(instance->decoder.parser_step) { + case AnsonicDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < + subghz_protocol_ansonic_const.te_delta * 35)) { + //Found header Ansonic + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + } + break; + case AnsonicDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) { + //Found start bit Ansonic + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { + instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ansonic_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + case AnsonicDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < + subghz_protocol_ansonic_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < + subghz_protocol_ansonic_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; + } else + instance->decoder.parser_step = AnsonicDecoderStepReset; + } else { + instance->decoder.parser_step = AnsonicDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 12345678(10) k 9 + * AAA => 10101010 1 01 0 + * + * 1...10 - DIP + * k- KEY + */ + instance->cnt = instance->data & 0xFFF; + instance->btn = ((instance->data >> 1) & 0x3); +} + +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_ansonic_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_ansonic_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderAnsonic* instance = context; + subghz_protocol_ansonic_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%03lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/ansonic.h b/applications/main/subghz/protocols/ansonic.h new file mode 100644 index 000000000..0170a6048 --- /dev/null +++ b/applications/main/subghz/protocols/ansonic.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" + +typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; +typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; + +extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; +extern const SubGhzProtocol subghz_protocol_ansonic; + +/** + * Allocate SubGhzProtocolEncoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAnsonic. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + */ +void subghz_protocol_encoder_ansonic_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderAnsonic. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + */ +void subghz_protocol_decoder_ansonic_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderAnsonic. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/base.c b/applications/main/subghz/protocols/base.c new file mode 100644 index 000000000..36f33b9a5 --- /dev/null +++ b/applications/main/subghz/protocols/base.c @@ -0,0 +1,62 @@ +#include "base.h" +#include "registry.h" + +void subghz_protocol_decoder_base_set_decoder_callback( + SubGhzProtocolDecoderBase* decoder_base, + SubGhzProtocolDecoderBaseRxCallback callback, + void* context) { + decoder_base->callback = callback; + decoder_base->context = context; +} + +bool subghz_protocol_decoder_base_get_string( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->get_string) { + decoder_base->protocol->decoder->get_string(decoder_base, output); + status = true; + } + + return status; +} + +bool subghz_protocol_decoder_base_serialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->serialize) { + status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset); + } + + return status; +} + +bool subghz_protocol_decoder_base_deserialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format) { + bool status = false; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->deserialize) { + status = decoder_base->protocol->decoder->deserialize(decoder_base, flipper_format); + } + + return status; +} + +uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) { + uint8_t hash = 0; + + if(decoder_base->protocol && decoder_base->protocol->decoder && + decoder_base->protocol->decoder->get_hash_data) { + hash = decoder_base->protocol->decoder->get_hash_data(decoder_base); + } + + return hash; +} diff --git a/applications/main/subghz/protocols/base.h b/applications/main/subghz/protocols/base.h new file mode 100644 index 000000000..1f3d3e1be --- /dev/null +++ b/applications/main/subghz/protocols/base.h @@ -0,0 +1,88 @@ +#pragma once + +#include "../types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase; + +typedef void ( + *SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context); + +typedef void (*SubGhzProtocolDecoderBaseSerialize)( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output); + +struct SubGhzProtocolDecoderBase { + // Decoder general section + const SubGhzProtocol* protocol; + + // Callback section + SubGhzProtocolDecoderBaseRxCallback callback; + void* context; +}; + +/** + * Set a callback upon completion of successful decoding of one of the protocols. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param callback Callback, SubGhzProtocolDecoderBaseRxCallback + * @param context Context + */ +void subghz_protocol_decoder_base_set_decoder_callback( + SubGhzProtocolDecoderBase* decoder_base, + SubGhzProtocolDecoderBaseRxCallback callback, + void* context); + +/** + * Getting a textual representation of the received data. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param output Resulting text + */ +bool subghz_protocol_decoder_base_get_string( + SubGhzProtocolDecoderBase* decoder_base, + FuriString* output); + +/** + * Serialize data SubGhzProtocolDecoderBase. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_base_serialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBase. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_base_deserialize( + SubGhzProtocolDecoderBase* decoder_base, + FlipperFormat* flipper_format); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base); + +// Encoder Base +typedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase; + +struct SubGhzProtocolEncoderBase { + // Decoder general section + const SubGhzProtocol* protocol; + + // Callback section +}; + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/bett.c b/applications/main/subghz/protocols/bett.c new file mode 100644 index 000000000..644d80fd8 --- /dev/null +++ b/applications/main/subghz/protocols/bett.c @@ -0,0 +1,342 @@ +#include "bett.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 "SubGhzProtocolBETT" + +#define DIP_P 0b11 //(+) +#define DIP_O 0b10 //(0) +#define DIP_N 0b00 //(-) + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c" +#define SHOW_DIP_P(dip, check_dip) \ + ((((dip >> 0x8) >> 0x8) == 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_bett_const = { + .te_short = 340, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 18, +}; + +struct SubGhzProtocolDecoderBETT { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderBETT { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + BETTDecoderStepReset = 0, + BETTDecoderStepSaveDuration, + BETTDecoderStepCheckDuration, +} BETTDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_bett_decoder = { + .alloc = subghz_protocol_decoder_bett_alloc, + .free = subghz_protocol_decoder_bett_free, + + .feed = subghz_protocol_decoder_bett_feed, + .reset = subghz_protocol_decoder_bett_reset, + + .get_hash_data = subghz_protocol_decoder_bett_get_hash_data, + .serialize = subghz_protocol_decoder_bett_serialize, + .deserialize = subghz_protocol_decoder_bett_deserialize, + .get_string = subghz_protocol_decoder_bett_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bett_encoder = { + .alloc = subghz_protocol_encoder_bett_alloc, + .free = subghz_protocol_encoder_bett_free, + + .deserialize = subghz_protocol_encoder_bett_deserialize, + .stop = subghz_protocol_encoder_bett_stop, + .yield = subghz_protocol_encoder_bett_yield, +}; + +const SubGhzProtocol subghz_protocol_bett = { + .name = SUBGHZ_PROTOCOL_BETT_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_bett_decoder, + .encoder = &subghz_protocol_bett_encoder, +}; + +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT)); + + instance->base.protocol = &subghz_protocol_bett; + 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_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBETT* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBETT instance + * @return true On success + */ +static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* 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_bett_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_bett_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_bett_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_short + + subghz_protocol_bett_const.te_long * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7); + } + return true; +} + +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBETT* 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_bett_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); + + if(!subghz_protocol_encoder_bett_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_bett_stop(void* context) { + SubGhzProtocolEncoderBETT* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bett_yield(void* context) { + SubGhzProtocolEncoderBETT* 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_bett_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT)); + instance->base.protocol = &subghz_protocol_bett; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_bett_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + free(instance); +} + +void subghz_protocol_decoder_bett_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + instance->decoder.parser_step = BETTDecoderStepReset; +} + +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + + switch(instance->decoder.parser_step) { + case BETTDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < + (subghz_protocol_bett_const.te_delta * 15))) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } + break; + case BETTDecoderStepSaveDuration: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < + (subghz_protocol_bett_const.te_delta * 15)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_bett_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); + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3)) { + instance->decoder.parser_step = BETTDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } + } + break; + case BETTDecoderStepCheckDuration: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < + subghz_protocol_bett_const.te_delta * 3) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < + subghz_protocol_bett_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = BETTDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + } else { + instance->decoder.parser_step = BETTDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_bett_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_bett_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBETT* instance = context; + uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%05lX\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN "\r\n" + " -: " DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O), + SHOW_DIP_P(data, DIP_N)); +} diff --git a/applications/main/subghz/protocols/bett.h b/applications/main/subghz/protocols/bett.h new file mode 100644 index 000000000..c0ce0b7f4 --- /dev/null +++ b/applications/main/subghz/protocols/bett.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BETT_NAME "BETT" + +typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT; +typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT; + +extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder; +extern const SubGhzProtocol subghz_protocol_bett; + +/** + * Allocate SubGhzProtocolEncoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance + */ +void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBETT. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + */ +void subghz_protocol_encoder_bett_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBETT instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bett_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBETT. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance + */ +void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + */ +void subghz_protocol_decoder_bett_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bett_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBETT. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBETT instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/bin_raw.c b/applications/main/subghz/protocols/bin_raw.c new file mode 100644 index 000000000..67e0467ee --- /dev/null +++ b/applications/main/subghz/protocols/bin_raw.c @@ -0,0 +1,1120 @@ +#include "bin_raw.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" +#include +#include +#include + +#define TAG "SubGhzProtocolBinRAW" + +//change very carefully, RAM ends at the most inopportune moment +#define BIN_RAW_BUF_RAW_SIZE 2048 +#define BIN_RAW_BUF_DATA_SIZE 512 + +#define BIN_RAW_THRESHOLD_RSSI -85.0f +#define BIN_RAW_DELTA_RSSI 7.0f +#define BIN_RAW_SEARCH_CLASSES 20 +#define BIN_RAW_TE_MIN_COUNT 40 +#define BIN_RAW_BUF_MIN_DATA_COUNT 128 +#define BIN_RAW_MAX_MARKUP_COUNT 20 + +//#define BIN_RAW_DEBUG + +#ifdef BIN_RAW_DEBUG +#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) +#define bin_raw_debug_tag(tag, ...) \ + FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ + FURI_LOG_RAW_D(__VA_ARGS__) +#else +#define bin_raw_debug(...) +#define bin_raw_debug_tag(...) +#endif + +static const SubGhzBlockConst subghz_protocol_bin_raw_const = { + .te_short = 30, + .te_long = 65000, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +typedef enum { + BinRAWDecoderStepReset = 0, + BinRAWDecoderStepWrite, + BinRAWDecoderStepBufFull, + BinRAWDecoderStepNoParse, +} BinRAWDecoderStep; + +typedef enum { + BinRAWTypeUnknown = 0, + BinRAWTypeNoGap, + BinRAWTypeGap, + BinRAWTypeGapRecurring, + BinRAWTypeGapRolling, + BinRAWTypeGapUnknown, +} BinRAWType; + +struct BinRAW_Markup { + uint16_t byte_bias; + uint16_t bit_count; +}; +typedef struct BinRAW_Markup BinRAW_Markup; + +struct SubGhzProtocolDecoderBinRAW { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + int32_t* data_raw; + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + size_t data_raw_ind; + uint32_t te; + float adaptive_threshold_rssi; +}; + +struct SubGhzProtocolEncoderBinRAW { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t* data; + BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; + uint32_t te; +}; + +const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { + .alloc = subghz_protocol_decoder_bin_raw_alloc, + .free = subghz_protocol_decoder_bin_raw_free, + + .feed = subghz_protocol_decoder_bin_raw_feed, + .reset = subghz_protocol_decoder_bin_raw_reset, + + .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, + .serialize = subghz_protocol_decoder_bin_raw_serialize, + .deserialize = subghz_protocol_decoder_bin_raw_deserialize, + .get_string = subghz_protocol_decoder_bin_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { + .alloc = subghz_protocol_encoder_bin_raw_alloc, + .free = subghz_protocol_encoder_bin_raw_free, + + .deserialize = subghz_protocol_encoder_bin_raw_deserialize, + .stop = subghz_protocol_encoder_bin_raw_stop, + .yield = subghz_protocol_encoder_bin_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_bin_raw = { + .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, + .type = SubGhzProtocolTypeBinRAW, +#ifdef BIN_RAW_DEBUG + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#else + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, +#endif + .decoder = &subghz_protocol_bin_raw_decoder, + .encoder = &subghz_protocol_bin_raw_encoder, +}; + +static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { + if(bit_count & 0x7) { + return (bit_count >> 3) + 1; + } else { + return (bit_count >> 3); + } +} + +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); + + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + free(instance->encoder.upload); + free(instance->data); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return true On success + */ +static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { + furi_assert(instance); + + //we glue all the pieces of the package into 1 long sequence with left alignment, + //in the uploaded data we have right alignment. + + bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); + uint16_t i = 0; + uint16_t ind = 0; + bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + uint8_t bit_bias = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + instance->data_markup[i].bit_count; + bin_raw_debug( + "\t%d\t%d\t%d :\t\t%d\r\n", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count, + bit_bias); + for(uint16_t y = instance->data_markup[i].byte_bias * 8; + y < instance->data_markup[i].byte_bias * 8 + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - + bit_bias; + y++) { + subghz_protocol_blocks_set_bit_array( + subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), + instance->data, + ind++, + BIN_RAW_BUF_DATA_SIZE); + } + i++; + } + bin_raw_debug("\r\n"); +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); + for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); + + bin_raw_debug_tag( + TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); +#endif + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + instance->data, + ind, + instance->encoder.upload, + instance->encoder.size_upload, + instance->te, + SubGhzProtocolBlockAlignBitLeft); + + bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); + bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); + return true; +} + +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + +#ifdef BIN_RAW_DEBUG + uint16_t i = 0; + bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } + i++; + } + bin_raw_debug("\r\n\r\n"); +#endif + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_encoder_bin_raw_stop(void* context) { + SubGhzProtocolEncoderBinRAW* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { + SubGhzProtocolEncoderBinRAW* 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_bin_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); + instance->base.protocol = &subghz_protocol_bin_raw; + instance->generic.protocol_name = instance->base.protocol->name; + instance->data_raw_ind = 0; + instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; + return instance; +} + +void subghz_protocol_decoder_bin_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + free(instance->data_raw); + free(instance->data); + free(instance); +} + +void subghz_protocol_decoder_bin_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; +#ifdef BIN_RAW_DEBUG + UNUSED(instance); +#else + instance->decoder.parser_step = BinRAWDecoderStepNoParse; + instance->data_raw_ind = 0; +#endif +} + +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { + if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { + instance->decoder.parser_step = BinRAWDecoderStepBufFull; + } else { + instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); + } + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance + */ +static bool + subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { + struct { + float data; + uint16_t count; + } classes[BIN_RAW_SEARCH_CLASSES]; + + size_t ind = 0; + + memset(classes, 0x00, sizeof(classes)); + + uint16_t data_markup_ind = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + if(instance->data_raw_ind < 512) { + ind = + instance->data_raw_ind - + 100; //there is usually garbage at the end of the record, we exclude it from the classification + } else { + ind = 512; + } + + //sort the durations to find the shortest correlated interval + for(size_t i = 0; i < ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = (float)(abs(instance->data_raw[i])); + classes[k].count++; + break; + } else if( + DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < + (classes[k].data / 4)) { //if the test value does not differ by more than 25% + classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * + 0.05f; //running average k=0.05 + classes[k].count++; + break; + } + } + } + + // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { + // //filling the classifier, it means that they received an unclean signal + // return false; + // } + + //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT + instance->te = subghz_protocol_bin_raw_const.te_long * 2; + + bool te_ok = false; + uint16_t gap_ind = 0; + uint16_t gap_delta = 0; + uint32_t gap = 0; + int data_temp = 0; + BinRAWType bin_raw_type = BinRAWTypeUnknown; + + //sort by number of occurrences + bool swap = true; + while(swap) { + swap = false; + for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { + if(classes[i].count > classes[i - 1].count) { + uint32_t data = classes[i - 1].data; + uint32_t count = classes[i - 1].count; + classes[i - 1].data = classes[i].data; + classes[i - 1].count = classes[i].count; + classes[i].data = data; + classes[i].count = count; + swap = true; + } + } + } +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Sorted durations\r\n"); + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { + //adopted only the preamble + instance->te = (uint32_t)classes[0].data; + te_ok = true; + gap = 0; //gap no + } else { + //take the 2 most common durations + //check that there are enough + if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || + (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) + return false; + //arrange the first 2 date values in ascending order + if(classes[0].data > classes[1].data) { + uint32_t data = classes[1].data; + classes[0].data = classes[1].data; + classes[1].data = data; + } + + //determine the value to be corrected + for(uint8_t k = 1; k < 5; k++) { + float delta = (classes[1].data / (classes[0].data / k)); + bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); + delta -= (uint32_t)delta; + + if((delta < 0.20f) || (delta > 0.80f)) { + instance->te = (uint32_t)classes[0].data / k; + bin_raw_debug_tag(TAG, "K= %d\r\n", k); + te_ok = true; //found a correlated duration + break; + } + } + if(!te_ok) { + //did not find the minimum TE satisfying the condition + return false; + } + bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); + + //looking for a gap + for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { + if((classes[k].count > 2) && (classes[k].data > gap)) { + gap = (uint32_t)classes[k].data; + gap_delta = gap / 5; //calculate 20% deviation from ideal value + } + } + + if((gap / instance->te) < + 10) { //make an assumption, the longest gap should be more than 10 TE + gap = 0; //check that our signal has a gap greater than 10*TE + bin_raw_type = BinRAWTypeNoGap; + } else { + bin_raw_type = BinRAWTypeGap; + //looking for the last occurrence of gap + ind = instance->data_raw_ind - 1; + while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { + ind--; + } + gap_ind = ind; + } + } + + //if we consider that there is a gap, then we divide the signal with respect to this gap + //processing input data from the end + if(bin_raw_type == BinRAWTypeGap) { + bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); + ind = (BIN_RAW_BUF_DATA_SIZE * 8); + uint16_t bit_count = 0; + do { + gap_ind--; + data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); + bin_raw_debug("%d ", data_temp); + if(data_temp == 0) bit_count++; //there is noise in the package + for(size_t i = 0; i < abs(data_temp); i++) { + bit_count++; + if(ind) { + ind--; + } else { + break; + } + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); + } + } + //split into full bytes if gap is caught + if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + bit_count = 0; + + if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; + ind &= 0xFFFFFFF8; //jump to the pre whole byte + } + } while(gap_ind != 0); + if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { + instance->data_markup[data_markup_ind].byte_bias = ind >> 3; + instance->data_markup[data_markup_ind++].bit_count = bit_count; + } + + bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); + + //reset the classifier and classify the received data + memset(classes, 0x00, sizeof(classes)); + + bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); + for(size_t i = 0; i < data_markup_ind; i++) { + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + if(classes[k].count == 0) { + classes[k].data = instance->data_markup[i].bit_count; + classes[k].count++; + break; + } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { + classes[k].count++; + break; + } + } + } + +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\t\tind\tcount\tus\r\n"); + for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { + bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); + } + bin_raw_debug("\r\n"); +#endif + + //choose the value with the maximum repetition + data_temp = 0; + for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { + if((classes[i].count > 1) && (data_temp < classes[i].count)) + data_temp = (int)classes[i].data; + } + + //if(data_markup_ind == 0) return false; + +#ifdef BIN_RAW_DEBUG + //output in reverse order + bin_raw_debug_tag(TAG, "Found sequences\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + uint16_t data_markup_ind_temp = data_markup_ind; + if(data_markup_ind) { + data_markup_ind_temp--; + for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { + if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + data_markup_ind_temp, + instance->data_markup[data_markup_ind_temp].byte_bias, + instance->data_markup[data_markup_ind_temp].bit_count); + if(data_markup_ind_temp != 0) data_markup_ind_temp--; + } + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); + } + //compare data in chunks with the same number of bits + bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); +#endif + + //if(data_temp == 0) data_temp = (int)classes[0].data; + + if(data_temp != 0) { + //check that data in transmission is repeated every packet + for(uint16_t i = 0; i < data_markup_ind - 1; i++) { + if((instance->data_markup[i].bit_count == data_temp) && + (instance->data_markup[i + 1].bit_count == data_temp)) { + //if the number of bits in adjacent parcels is the same, compare the data + bin_raw_debug_tag( + TAG, + "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + i + 1, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[i + 1].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[i + 1].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i + 1].bit_count) - + 1]); + + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + if(memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[i + 1].byte_bias, + byte_count - 1) == 0) { + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); + + //place in 1 element the offset to valid data + instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; + instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; + //markup end sign + instance->data_markup[1].bit_count = 0; + instance->data_markup[1].byte_bias = 0; + + bin_raw_type = BinRAWTypeGapRecurring; + i = data_markup_ind; + break; + } + } + } + } + + if(bin_raw_type == BinRAWTypeGap) { + // check that retry occurs every n packets + for(uint16_t i = 0; i < data_markup_ind - 2; i++) { + uint16_t byte_count = + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); + for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { + bin_raw_debug_tag( + TAG, + "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", + i, + y, + instance->data[instance->data_markup[i].byte_bias], + instance->data[instance->data_markup[y].byte_bias], + instance->data + [instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count) - + 1], + instance->data + [instance->data_markup[y].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count) - + 1]); + + if(byte_count == + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[y].bit_count)) { //if the length in bytes matches + + if((memcmp( + instance->data + instance->data_markup[i].byte_bias, + instance->data + instance->data_markup[y].byte_bias, + byte_count - 1) == 0) && + (memcmp( + instance->data + instance->data_markup[i + 1].byte_bias, + instance->data + instance->data_markup[y + 1].byte_bias, + byte_count - 1) == 0)) { + uint8_t index = 0; +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag( + TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); + //output in reverse order + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); + index = y - 1; + for(size_t z = instance->data_markup[y].byte_bias + byte_count; + z < instance->data_markup[i].byte_bias + byte_count; + z++) { + if(instance->data_markup[index].byte_bias == z) { + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + index, + instance->data_markup[index].byte_bias, + instance->data_markup[index].bit_count); + if(index != 0) index--; + } + bin_raw_debug("%02X ", instance->data[z]); + } + + bin_raw_debug("\r\n\r\n"); +#endif + //todo can be optimized + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, + 0x00, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + + for(index = i; index < y; index++) { + instance->data_markup[index - i].bit_count = + markup_temp[y - index - 1].bit_count; + instance->data_markup[index - i].byte_bias = + markup_temp[y - index - 1].byte_bias; + } + + bin_raw_type = BinRAWTypeGapRolling; + i = data_markup_ind; + break; + } + } + } + } + } + //todo can be optimized + if(bin_raw_type == BinRAWTypeGap) { + if(data_temp != 0) { //there are sequences with the same number of bits + + BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; + memcpy( + markup_temp, + instance->data_markup, + BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + memset( + instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); + uint16_t index = 0; + uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; + do { + it--; + if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == + byte_count) { + instance->data_markup[index].bit_count = markup_temp[it].bit_count; + instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; + index++; + bin_raw_type = BinRAWTypeGapUnknown; + } + } while(it != 0); + } + } + + if(bin_raw_type != BinRAWTypeGap) + return true; + else + return false; + + } else { + // if bin_raw_type == BinRAWTypeGap + bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); + ind = 0; + for(size_t i = 0; i < instance->data_raw_ind; i++) { + int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); + if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise + bin_raw_debug("%d ", data_temp); + + for(size_t k = 0; k < abs(data_temp); k++) { + if(data_temp > 0) { + subghz_protocol_blocks_set_bit_array( + true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); + } + if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { + i = instance->data_raw_ind; + break; + } + } + } + + if(ind != 0) { + bin_raw_type = BinRAWTypeNoGap; + //right alignment + uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", + ind, + subghz_protocol_bin_raw_get_full_byte(ind), + bit_bias); + + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + //checking that the received sequence contains useful data + bool data_check = false; + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + if(instance->data[i] != 0) { + data_check = true; + break; + } + } + + if(data_check) { + for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { + instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | + (instance->data[i] >> bit_bias); + } + instance->data[0] = (instance->data[0] >> bit_bias); + +#ifdef BIN_RAW_DEBUG + bin_raw_debug_tag(TAG, "Data right alignment\r\n"); + for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { + bin_raw_debug("%02X ", instance->data[i]); + } + bin_raw_debug("\r\n\r\n"); +#endif + instance->data_markup[0].bit_count = ind; + instance->data_markup[0].byte_bias = 0; + + return true; + } else { + return false; + } + } else { + return false; + } + } + return false; +} + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi) { + furi_assert(instance); + switch(instance->decoder.parser_step) { + case BinRAWDecoderStepReset: + + bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + instance->data_raw_ind = 0; + memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); + memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); + instance->decoder.parser_step = BinRAWDecoderStepWrite; + bin_raw_debug_tag(TAG, "RSSI\r\n"); + } else { + //adaptive noise level adjustment + instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; + } + break; + + case BinRAWDecoderStepBufFull: + case BinRAWDecoderStepWrite: +#ifdef BIN_RAW_DEBUG + if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { + bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); + } else { + bin_raw_debug("%ld ", (int32_t)rssi); + } +#endif + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { +#ifdef BIN_RAW_DEBUG + bin_raw_debug("\r\n\r\n"); + bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); + for(size_t i = 0; i < instance->data_raw_ind; i++) { + bin_raw_debug("%ld ", instance->data_raw[i]); + } + bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); +#endif + instance->decoder.parser_step = BinRAWDecoderStepReset; + instance->generic.data_count_bit = 0; + if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { + if(subghz_protocol_bin_raw_check_remote_controller(instance)) { + bin_raw_debug_tag(TAG, "Sequence found\r\n"); + bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && + (instance->data_markup[i].bit_count != 0)) { + instance->generic.data_count_bit += instance->data_markup[i].bit_count; +#ifdef BIN_RAW_DEBUG + bin_raw_debug( + "\r\n\t%d\t%d\t%d :\t", + i, + instance->data_markup[i].byte_bias, + instance->data_markup[i].bit_count); + for(uint16_t y = instance->data_markup[i].byte_bias; + y < instance->data_markup[i].byte_bias + + subghz_protocol_bin_raw_get_full_byte( + instance->data_markup[i].bit_count); + y++) { + bin_raw_debug("%02X ", instance->data[y]); + } +#endif + i++; + } + bin_raw_debug("\r\n"); + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + } + } + break; + + default: + //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state + if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { + instance->decoder.parser_step = BinRAWDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + return subghz_protocol_blocks_add_bytes( + instance->data + instance->data_markup[0].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); +} + +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + flipper_format, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + flipper_format, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + flipper_format, "Protocol", instance->generic.protocol_name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + uint32_t temp = instance->generic.data_count_bit; + if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Bit"); + break; + } + + if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + break; + } + + uint16_t i = 0; + while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { + temp = instance->data_markup[i].bit_count; + if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { + FURI_LOG_E(TAG, "Bit_RAW"); + break; + } + if(!flipper_format_write_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[i].byte_bias, + subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { + FURI_LOG_E(TAG, "Unable to add Data_RAW"); + break; + } + i++; + } + + res = true; + } while(false); + furi_string_free(temp_str); + return res; +} + +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + + bool res = false; + uint32_t temp_data = 0; + + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { + FURI_LOG_E(TAG, "Missing Bit"); + break; + } + + instance->generic.data_count_bit = (uint16_t)temp_data; + + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + + temp_data = 0; + uint16_t ind = 0; + uint16_t byte_bias = 0; + uint16_t byte_count = 0; + memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); + while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { + if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { + FURI_LOG_E(TAG, "Markup overflow"); + break; + } + byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); + if(byte_count > BIN_RAW_BUF_DATA_SIZE) { + FURI_LOG_E(TAG, "Receive buffer overflow"); + break; + } + + instance->data_markup[ind].bit_count = temp_data; + instance->data_markup[ind].byte_bias = byte_bias; + byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); + + if(!flipper_format_read_hex( + flipper_format, + "Data_RAW", + instance->data + instance->data_markup[ind].byte_bias, + subghz_protocol_bin_raw_get_full_byte(temp_data))) { + instance->data_markup[ind].bit_count = 0; + FURI_LOG_E(TAG, "Missing Data_RAW"); + break; + } + ind++; + } + + res = true; + } while(0); + + return res; +} + +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderBinRAW* instance = context; + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:", + instance->generic.protocol_name, + instance->generic.data_count_bit); + + uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); + for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { + furi_string_cat_printf(output, "%02X", instance->data[i]); + } + + furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); +} diff --git a/applications/main/subghz/protocols/bin_raw.h b/applications/main/subghz/protocols/bin_raw.h new file mode 100644 index 000000000..c63f86ce6 --- /dev/null +++ b/applications/main/subghz/protocols/bin_raw.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" + +typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; +typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; +extern const SubGhzProtocol subghz_protocol_bin_raw; + +/** + * Allocate SubGhzProtocolEncoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderBinRAW. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + */ +void subghz_protocol_encoder_bin_raw_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderBinRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + */ +void subghz_protocol_decoder_bin_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); + +void subghz_protocol_decoder_bin_raw_data_input_rssi( + SubGhzProtocolDecoderBinRAW* instance, + float rssi); + +/** + * Serialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderBinRAW. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came.c b/applications/main/subghz/protocols/came.c new file mode 100644 index 000000000..bed26d7d2 --- /dev/null +++ b/applications/main/subghz/protocols/came.c @@ -0,0 +1,347 @@ +#include "came.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define TAG "SubGhzProtocolCAME" +#define CAME_24_COUNT_BIT 24 +#define PRASTEL_COUNT_BIT 25 +#define PRASTEL_NAME "Prastel" +#define AIRFORCE_COUNT_BIT 18 +#define AIRFORCE_NAME "Airforce" + +static const SubGhzBlockConst subghz_protocol_came_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 150, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderCame { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderCame { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameDecoderStepReset = 0, + CameDecoderStepFoundStartBit, + CameDecoderStepSaveDuration, + CameDecoderStepCheckDuration, +} CameDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_decoder = { + .alloc = subghz_protocol_decoder_came_alloc, + .free = subghz_protocol_decoder_came_free, + + .feed = subghz_protocol_decoder_came_feed, + .reset = subghz_protocol_decoder_came_reset, + + .get_hash_data = subghz_protocol_decoder_came_get_hash_data, + .serialize = subghz_protocol_decoder_came_serialize, + .deserialize = subghz_protocol_decoder_came_deserialize, + .get_string = subghz_protocol_decoder_came_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_encoder = { + .alloc = subghz_protocol_encoder_came_alloc, + .free = subghz_protocol_encoder_came_free, + + .deserialize = subghz_protocol_encoder_came_deserialize, + .stop = subghz_protocol_encoder_came_stop, + .yield = subghz_protocol_encoder_came_yield, +}; + +const SubGhzProtocol subghz_protocol_came = { + .name = SUBGHZ_PROTOCOL_CAME_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_decoder, + .encoder = &subghz_protocol_came_encoder, +}; + +void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCame* instance = malloc(sizeof(SubGhzProtocolEncoderCame)); + + instance->base.protocol = &subghz_protocol_came; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCame* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCame instance + * @return true On success + */ +static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = level_duration_make( + false, + (((instance->generic.data_count_bit == CAME_24_COUNT_BIT) || + (instance->generic.data_count_bit == + subghz_protocol_came_const.min_count_bit_for_found)) ? + (uint32_t)subghz_protocol_came_const.te_short * 76 : + (uint32_t)subghz_protocol_came_const.te_short * 39)); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCame* 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 > PRASTEL_COUNT_BIT)) { + 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); + + if(!subghz_protocol_encoder_came_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_stop(void* context) { + SubGhzProtocolEncoderCame* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_yield(void* context) { + SubGhzProtocolEncoderCame* 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_came_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCame* instance = malloc(sizeof(SubGhzProtocolDecoderCame)); + instance->base.protocol = &subghz_protocol_came; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + instance->decoder.parser_step = CameDecoderStepReset; +} + +void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + switch(instance->decoder.parser_step) { + case CameDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) < + subghz_protocol_came_const.te_delta * 47)) { + //Found header CAME + instance->decoder.parser_step = CameDecoderStepFoundStartBit; + } + break; + case CameDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta) { + //Found start bit CAME + instance->decoder.parser_step = CameDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + case CameDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_came_const.te_short * 4)) { + instance->decoder.parser_step = CameDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_came_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = CameDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + case CameDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_came_const.te_long) < + subghz_protocol_came_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = CameDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_long) < + subghz_protocol_came_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < + subghz_protocol_came_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = CameDecoderStepSaveDuration; + } else + instance->decoder.parser_step = CameDecoderStepReset; + } else { + instance->decoder.parser_step = CameDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCame* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n", + (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? + PRASTEL_NAME : + (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? + AIRFORCE_NAME : + instance->generic.protocol_name)), + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/came.h b/applications/main/subghz/protocols/came.h new file mode 100644 index 000000000..253c93aae --- /dev/null +++ b/applications/main/subghz/protocols/came.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_NAME "CAME" + +typedef struct SubGhzProtocolDecoderCame SubGhzProtocolDecoderCame; +typedef struct SubGhzProtocolEncoderCame SubGhzProtocolEncoderCame; + +extern const SubGhzProtocolDecoder subghz_protocol_came_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_encoder; +extern const SubGhzProtocol subghz_protocol_came; + +/** + * Allocate SubGhzProtocolEncoderCame. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCame* pointer to a SubGhzProtocolEncoderCame instance + */ +void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCame. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + */ +void subghz_protocol_encoder_came_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + */ +void subghz_protocol_encoder_came_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCame instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCame. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCame* pointer to a SubGhzProtocolDecoderCame instance + */ +void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + */ +void subghz_protocol_decoder_came_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + */ +void subghz_protocol_decoder_came_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCame. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCame instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_atomo.c b/applications/main/subghz/protocols/came_atomo.c new file mode 100644 index 000000000..d12e5976c --- /dev/null +++ b/applications/main/subghz/protocols/came_atomo.c @@ -0,0 +1,598 @@ +#include "came_atomo.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoCameAtomo" + +static const SubGhzBlockConst subghz_protocol_came_atomo_const = { + .te_short = 600, + .te_long = 1200, + .te_delta = 250, + .min_count_bit_for_found = 62, +}; + +struct SubGhzProtocolDecoderCameAtomo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderCameAtomo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameAtomoDecoderStepReset = 0, + CameAtomoDecoderStepDecoderData, +} CameAtomoDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder = { + .alloc = subghz_protocol_decoder_came_atomo_alloc, + .free = subghz_protocol_decoder_came_atomo_free, + + .feed = subghz_protocol_decoder_came_atomo_feed, + .reset = subghz_protocol_decoder_came_atomo_reset, + + .get_hash_data = subghz_protocol_decoder_came_atomo_get_hash_data, + .serialize = subghz_protocol_decoder_came_atomo_serialize, + .deserialize = subghz_protocol_decoder_came_atomo_deserialize, + .get_string = subghz_protocol_decoder_came_atomo_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder = { + .alloc = subghz_protocol_encoder_came_atomo_alloc, + .free = subghz_protocol_encoder_came_atomo_free, + + .deserialize = subghz_protocol_encoder_came_atomo_deserialize, + .stop = subghz_protocol_encoder_came_atomo_stop, + .yield = subghz_protocol_encoder_came_atomo_yield, +}; + +const SubGhzProtocol subghz_protocol_came_atomo = { + .name = SUBGHZ_PROTOCOL_CAME_ATOMO_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_atomo_decoder, + .encoder = &subghz_protocol_came_atomo_encoder, +}; + +static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance); + +void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolEncoderCameAtomo)); + + instance->base.protocol = &subghz_protocol_came_atomo; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 900; //actual size 766+ + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_atomo_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCameAtomo* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_came_atomo_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_came_atomo_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_came_atomo_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_came_atomo_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_came_atomo_const.te_short; + data.level = true; + break; + + default: + FURI_LOG_E(TAG, "SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +static void + subghz_protocol_encoder_came_atomo_get_upload(SubGhzProtocolEncoderCameAtomo* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + uint8_t pack[8] = {}; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long * 15); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_long * 60); + + for(uint8_t i = 0; i < 8; i++) { + pack[0] = (instance->generic.data_2 >> 56); + pack[1] = (instance->generic.cnt >> 8); + pack[2] = (instance->generic.cnt & 0xFF); + pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); + pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); + pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); + pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); + pack[7] = (instance->generic.data_2 & 0xFF); + + if(pack[0] == 0x7F) { + pack[0] = 0; + } else { + pack[0] += (i + 1); + } + + atomo_encrypt(pack); + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->generic.data = (uint64_t)hi << 32 | lo; + + instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; + instance->generic.data >>= 4; + instance->generic.data &= 0xFFFFFFFFFFFFFFF; + + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_short); + + for(uint8_t i = (instance->generic.data_count_bit - 2); i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); + } + instance->encoder.upload[index] = + subghz_protocol_encoder_came_atomo_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + //Send pause + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_delta * 272); + } + instance->encoder.size_upload = index; + instance->generic.cnt_2++; + pack[0] = (instance->generic.cnt_2); + pack[1] = (instance->generic.cnt >> 8); + pack[2] = (instance->generic.cnt & 0xFF); + pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); + pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); + pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); + pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); + pack[7] = (instance->generic.data_2 & 0xFF); + + atomo_encrypt(pack); + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->generic.data = (uint64_t)hi << 32 | lo; + + instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; + instance->generic.data >>= 4; + instance->generic.data &= 0xFFFFFFFFFFFFFFF; +} + +bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCameAtomo* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_came_atomo_remote_controller(&instance->generic); + subghz_protocol_encoder_came_atomo_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_atomo_stop(void* context) { + SubGhzProtocolEncoderCameAtomo* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context) { + SubGhzProtocolEncoderCameAtomo* 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_came_atomo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolDecoderCameAtomo)); + instance->base.protocol = &subghz_protocol_came_atomo; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_atomo_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_atomo_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + instance->decoder.parser_step = CameAtomoDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case CameAtomoDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) < + subghz_protocol_came_atomo_const.te_delta * 40)) { + //Found header CAME + instance->decoder.parser_step = CameAtomoDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case CameAtomoDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_came_atomo_const.te_long * 2 + + subghz_protocol_came_atomo_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_came_atomo_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.decode_data = 0; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = CameAtomoDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < + subghz_protocol_came_atomo_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = CameAtomoDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance) { + /* + * ***SkorP ver.*** + * 0x1fafef3ed0f7d9ef + * 0x185fcc1531ee86e7 + * 0x184fa96912c567ff + * 0x187f8a42f3dc38f7 + * 0x186f63915492a5cd + * 0x181f40bab58bfac5 + * 0x180f25c696a01bdd + * 0x183f06ed77b944d5 + * 0x182ef661d83d21a9 + * 0x18ded54a39247ea1 + * 0x18ceb0361a0f9fb9 + * 0x18fe931dfb16c0b1 + * 0x18ee7ace5c585d8b + * ........ + * transmission consists of 99 parcels with increasing counter while holding down the button + * with each new press, the counter in the encrypted part increases + * + * 0x1FAFF13ED0F7D9EF + * 0x1FAFF11ED0F7D9EF + * 0x1FAFF10ED0F7D9EF + * 0x1FAFF0FED0F7D9EF + * 0x1FAFF0EED0F7D9EF + * 0x1FAFF0DED0F7D9EF + * 0x1FAFF0CED0F7D9EF + * 0x1FAFF0BED0F7D9EF + * 0x1FAFF0AED0F7D9EF + * + * where 0x1FAF - parcel counter, 0хF0A - button press counter, + * 0xED0F7D9E - serial number, 0хF - key + * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F = 0x07F0 + * 0x185f ^ 0x185F = 0x0000 + * 0x184f ^ 0x185F = 0x0010 + * 0x187f ^ 0x185F = 0x0020 + * ..... + * 0x182e ^ 0x185F = 0x0071 + * 0x18de ^ 0x185F = 0x0081 + * ..... + * 0x1e43 ^ 0x185F = 0x061C + * where the last nibble is incremented every 8 samples + * + * Decode + * + * 0x1cf6931dfb16c0b1 => 0x1cf6 + * 0x1cf6 ^ 0x185F = 0x04A9 + * 0x04A9 => 0x04A = 74 (dec) + * 74+1 % 32(atomo_magic_xor) = 11 + * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX + * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX = 0xEF3ED0F7D9EF + * 0xEF3 ED0F7D9E F => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key + * + * ***Eng1n33r ver. (actual)*** + * 0x1FF08D9924984115 - received data + * 0x00F7266DB67BEEA0 - inverted data + * 0x0501FD0000A08300 - decrypted data, + * where: 0x05 - Button hold-cycle counter (8-bit, from 0 to 0x7F) + * 0x01FD - Parcel counter (normal 16-bit counter) + * 0x0000A083 - Serial number (32-bit) + * 0x0 - Button code (4-bit, 0x0 - #1 left-up; 0x2 - #2 right-up; 0x4 - #3 left-down; 0x6 - #4 right-down) + * 0x0 - Last zero nibble + * */ + + instance->data ^= 0xFFFFFFFFFFFFFFFF; + instance->data <<= 4; + + uint8_t pack[8] = {}; + pack[0] = (instance->data >> 56); + pack[1] = ((instance->data >> 48) & 0xFF); + pack[2] = ((instance->data >> 40) & 0xFF); + pack[3] = ((instance->data >> 32) & 0xFF); + pack[4] = ((instance->data >> 24) & 0xFF); + pack[5] = ((instance->data >> 16) & 0xFF); + pack[6] = ((instance->data >> 8) & 0xFF); + pack[7] = (instance->data & 0xFF); + + atomo_decrypt(pack); + + instance->cnt_2 = pack[0]; + instance->cnt = (uint16_t)pack[1] << 8 | pack[2]; + instance->serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6]; + + uint8_t btn_decode = (pack[7] >> 4); + if(btn_decode == 0x0) { + instance->btn = 0x1; + } + if(btn_decode == 0x2) { + instance->btn = 0x2; + } + if(btn_decode == 0x4) { + instance->btn = 0x3; + } + if(btn_decode == 0x6) { + instance->btn = 0x4; + } + + uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; + uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; + instance->data_2 = (uint64_t)hi << 32 | lo; +} + +void atomo_encrypt(uint8_t* buff) { + uint8_t tmpB = (~buff[0] + 1) & 0x7F; + + uint8_t bitCnt = 8; + while(bitCnt < 59) { + if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { + tmpB = ((tmpB << 1) & 0xFF) | 1; + } else { + tmpB = (tmpB << 1) & 0xFF; + } + + if(tmpB & 0x80) { + buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); + } + + bitCnt++; + } + + buff[0] = (buff[0] ^ 5) & 0x7F; +} + +void atomo_decrypt(uint8_t* buff) { + buff[0] = (buff[0] ^ 5) & 0x7F; + uint8_t tmpB = (-buff[0]) & 0x7F; + + uint8_t bitCnt = 8; + while(bitCnt < 59) { + if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { + tmpB = ((tmpB << 1) & 0xFF) | 1; + } else { + tmpB = (tmpB << 1) & 0xFF; + } + + if(tmpB & 0x80) { + buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); + } + + bitCnt++; + } +} + +uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_atomo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_atomo_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_came_atomo_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCameAtomo* instance = context; + subghz_protocol_came_atomo_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%08lX%08lX\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Pcl_Cnt:0x%04lX\r\n" + "Btn_Cnt:0x%02X", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt, + instance->generic.cnt_2); +} diff --git a/applications/main/subghz/protocols/came_atomo.h b/applications/main/subghz/protocols/came_atomo.h new file mode 100644 index 000000000..736aee850 --- /dev/null +++ b/applications/main/subghz/protocols/came_atomo.h @@ -0,0 +1,110 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_ATOMO_NAME "CAME Atomo" + +typedef struct SubGhzProtocolDecoderCameAtomo SubGhzProtocolDecoderCameAtomo; +typedef struct SubGhzProtocolEncoderCameAtomo SubGhzProtocolEncoderCameAtomo; + +extern const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder; +extern const SubGhzProtocol subghz_protocol_came_atomo; + +void atomo_decrypt(uint8_t* buff); + +void atomo_encrypt(uint8_t* buff); + +/** + * Allocate SubGhzProtocolEncoderCameAtomo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCameAtomo* pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCameAtomo. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void subghz_protocol_encoder_came_atomo_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + */ +void subghz_protocol_encoder_came_atomo_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCameAtomo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCameAtomo* pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void subghz_protocol_decoder_came_atomo_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + */ +void subghz_protocol_decoder_came_atomo_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_atomo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCameAtomo. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_twee.c b/applications/main/subghz/protocols/came_twee.c new file mode 100644 index 000000000..e7eb33c42 --- /dev/null +++ b/applications/main/subghz/protocols/came_twee.c @@ -0,0 +1,468 @@ +#include "came_twee.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin + * + */ + +#define TAG "SubGhzProtocolCAME_Twee" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +/** + * Rainbow table Came Twee. + */ +static const uint32_t came_twee_magic_numbers_xor[15] = { + 0x0E0E0E00, + 0x1D1D1D11, + 0x2C2C2C22, + 0x3B3B3B33, + 0x4A4A4A44, + 0x59595955, + 0x68686866, + 0x77777777, + 0x86868688, + 0x95959599, + 0xA4A4A4AA, + 0xB3B3B3BB, + 0xC2C2C2CC, + 0xD1D1D1DD, + 0xE0E0E0EE, +}; + +static const SubGhzBlockConst subghz_protocol_came_twee_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 250, + .min_count_bit_for_found = 54, +}; + +struct SubGhzProtocolDecoderCameTwee { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderCameTwee { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + CameTweeDecoderStepReset = 0, + CameTweeDecoderStepDecoderData, +} CameTweeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = { + .alloc = subghz_protocol_decoder_came_twee_alloc, + .free = subghz_protocol_decoder_came_twee_free, + + .feed = subghz_protocol_decoder_came_twee_feed, + .reset = subghz_protocol_decoder_came_twee_reset, + + .get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data, + .serialize = subghz_protocol_decoder_came_twee_serialize, + .deserialize = subghz_protocol_decoder_came_twee_deserialize, + .get_string = subghz_protocol_decoder_came_twee_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = { + .alloc = subghz_protocol_encoder_came_twee_alloc, + .free = subghz_protocol_encoder_came_twee_free, + + .deserialize = subghz_protocol_encoder_came_twee_deserialize, + .stop = subghz_protocol_encoder_came_twee_stop, + .yield = subghz_protocol_encoder_came_twee_yield, +}; + +const SubGhzProtocol subghz_protocol_came_twee = { + .name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_came_twee_decoder, + .encoder = &subghz_protocol_came_twee_encoder, +}; + +void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee)); + + instance->base.protocol = &subghz_protocol_came_twee; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!! + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_came_twee_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderCameTwee* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_came_twee_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_came_twee_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_came_twee_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_came_twee_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +static void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask + + for(int i = 14; i >= 0; i--) { + temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) | + (instance->generic.serial ^ came_twee_magic_numbers_xor[i]); + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_twee_add_duration_to_upload(result); + manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_came_twee_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51); + } + instance->encoder.size_upload = index; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) { + /* Came Twee 54 bit, rolling code 15 parcels with + * a decreasing counter from 0xE to 0x0 + * with originally coded dip switches on the console 10 bit code + * + * 0x003FFF72E04A6FEE + * 0x003FFF72D17B5EDD + * 0x003FFF72C2684DCC + * 0x003FFF72B3193CBB + * 0x003FFF72A40E2BAA + * 0x003FFF72953F1A99 + * 0x003FFF72862C0988 + * 0x003FFF7277DDF877 + * 0x003FFF7268C2E766 + * 0x003FFF7259F3D655 + * 0x003FFF724AE0C544 + * 0x003FFF723B91B433 + * 0x003FFF722C86A322 + * 0x003FFF721DB79211 + * 0x003FFF720EA48100 + * + * decryption + * the last 32 bits, do XOR by the desired number, divide the result by 4, + * convert the first 16 bits of the resulting 32-bit number to bin and do + * bit-by-bit mirroring, adding up to 10 bits + * + * Example + * Step 1. 0x003FFF721DB79211 => 0x1DB79211 + * Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00 + * Step 4. 0x00AA8F00 / 4 => 0x002AA3C0 + * Step 5. 0x002AA3C0 => 0x002A + * Step 6. 0x002A bin => b101010 + * Step 7. b101010 => b0101010000 + * Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off + */ + + uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF); + uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF); + + data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]); + instance->serial = data; + data /= 4; + instance->btn = (data >> 4) & 0x0F; + data >>= 16; + data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16); + instance->cnt = data >> 6; +} + +bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderCameTwee* 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_came_twee_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_came_twee_remote_controller(&instance->generic); + subghz_protocol_encoder_came_twee_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_came_twee_stop(void* context) { + SubGhzProtocolEncoderCameTwee* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_came_twee_yield(void* context) { + SubGhzProtocolEncoderCameTwee* 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_came_twee_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee)); + instance->base.protocol = &subghz_protocol_came_twee; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_came_twee_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + free(instance); +} + +void subghz_protocol_decoder_came_twee_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + instance->decoder.parser_step = CameTweeDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case CameTweeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) < + subghz_protocol_came_twee_const.te_delta * 20)) { + //Found header CAME + instance->decoder.parser_step = CameTweeDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongLow, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case CameTweeDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 + + subghz_protocol_came_twee_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_came_twee_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongLow, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = CameTweeDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < + subghz_protocol_came_twee_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = CameTweeDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_came_twee_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_came_twee_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_came_twee_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderCameTwee* instance = context; + subghz_protocol_came_twee_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/came_twee.h b/applications/main/subghz/protocols/came_twee.h new file mode 100644 index 000000000..359b964da --- /dev/null +++ b/applications/main/subghz/protocols/came_twee.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CAME_TWEE_NAME "CAME TWEE" + +typedef struct SubGhzProtocolDecoderCameTwee SubGhzProtocolDecoderCameTwee; +typedef struct SubGhzProtocolEncoderCameTwee SubGhzProtocolEncoderCameTwee; + +extern const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder; +extern const SubGhzProtocol subghz_protocol_came_twee; + +/** + * Allocate SubGhzProtocolEncoderCameTwee. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderCameTwee* pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderCameTwee. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void subghz_protocol_encoder_came_twee_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + */ +void subghz_protocol_encoder_came_twee_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_came_twee_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderCameTwee. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderCameTwee* pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void subghz_protocol_decoder_came_twee_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + */ +void subghz_protocol_decoder_came_twee_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_came_twee_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderCameTwee. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance + * @param output Resulting text + */ +void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/chamberlain_code.c b/applications/main/subghz/protocols/chamberlain_code.c new file mode 100644 index 000000000..9c8e5ee4a --- /dev/null +++ b/applications/main/subghz/protocols/chamberlain_code.c @@ -0,0 +1,499 @@ +#include "chamberlain_code.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolChamb_Code" + +#define CHAMBERLAIN_CODE_BIT_STOP 0b0001 +#define CHAMBERLAIN_CODE_BIT_1 0b0011 +#define CHAMBERLAIN_CODE_BIT_0 0b0111 + +#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F +#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F +#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F + +#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101 +#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001 +#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001 + +#define CHAMBERLAIN_7_CODE_DIP_PATTERN "%c%c%c%c%c%c%c" +#define CHAMBERLAIN_7_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +#define CHAMBERLAIN_8_CODE_DIP_PATTERN "%c%c%c%c%cx%c%c" +#define CHAMBERLAIN_8_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0') + +#define CHAMBERLAIN_9_CODE_DIP_PATTERN "%c%c%c%c%c%c%c%c%c" +#define CHAMBERLAIN_9_CODE_DATA_TO_DIP(dip) \ + (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ + (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), (dip & 0x0004 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_chamb_code_const = { + .te_short = 1000, + .te_long = 3000, + .te_delta = 200, + .min_count_bit_for_found = 10, +}; + +struct SubGhzProtocolDecoderChamb_Code { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderChamb_Code { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Chamb_CodeDecoderStepReset = 0, + Chamb_CodeDecoderStepFoundStartBit, + Chamb_CodeDecoderStepSaveDuration, + Chamb_CodeDecoderStepCheckDuration, +} Chamb_CodeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder = { + .alloc = subghz_protocol_decoder_chamb_code_alloc, + .free = subghz_protocol_decoder_chamb_code_free, + + .feed = subghz_protocol_decoder_chamb_code_feed, + .reset = subghz_protocol_decoder_chamb_code_reset, + + .get_hash_data = subghz_protocol_decoder_chamb_code_get_hash_data, + .serialize = subghz_protocol_decoder_chamb_code_serialize, + .deserialize = subghz_protocol_decoder_chamb_code_deserialize, + .get_string = subghz_protocol_decoder_chamb_code_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder = { + .alloc = subghz_protocol_encoder_chamb_code_alloc, + .free = subghz_protocol_encoder_chamb_code_free, + + .deserialize = subghz_protocol_encoder_chamb_code_deserialize, + .stop = subghz_protocol_encoder_chamb_code_stop, + .yield = subghz_protocol_encoder_chamb_code_yield, +}; + +const SubGhzProtocol subghz_protocol_chamb_code = { + .name = SUBGHZ_PROTOCOL_CHAMB_CODE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_chamb_code_decoder, + .encoder = &subghz_protocol_chamb_code_encoder, +}; + +void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolEncoderChamb_Code)); + + instance->base.protocol = &subghz_protocol_chamb_code; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 24; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_chamb_code_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderChamb_Code* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static uint64_t subghz_protocol_chamb_bit_to_code(uint64_t data, uint8_t size) { + uint64_t data_res = 0; + for(uint8_t i = 0; i < size; i++) { + if(!(bit_read(data, size - i - 1))) { + data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_0; + } else { + data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_1; + } + } + return data_res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @return true On success + */ +static bool + subghz_protocol_encoder_chamb_code_get_upload(SubGhzProtocolEncoderChamb_Code* instance) { + furi_assert(instance); + + uint64_t data = subghz_protocol_chamb_bit_to_code( + instance->generic.data, instance->generic.data_count_bit); + + switch(instance->generic.data_count_bit) { + case 7: + data = ((data >> 4) << 16) | (data & 0xF) << 4 | CHAMBERLAIN_7_CODE_MASK_CHECK; + break; + case 8: + data = ((data >> 12) << 16) | (data & 0xFF) << 4 | CHAMBERLAIN_8_CODE_MASK_CHECK; + break; + case 9: + data = (data << 4) | CHAMBERLAIN_9_CODE_MASK_CHECK; + break; + + default: + FURI_LOG_E(TAG, "Invalid bits count"); + return false; + break; + } +#define UPLOAD_HEX_DATA_SIZE 10 + uint8_t upload_hex_data[UPLOAD_HEX_DATA_SIZE] = {0}; + size_t upload_hex_count_bit = 0; + + //insert guard time + for(uint8_t i = 0; i < 36; i++) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + + //insert data + switch(instance->generic.data_count_bit) { + case 7: + case 9: + for(uint8_t i = 44; i > 0; i--) { + if(!bit_read(data, i - 1)) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + } + break; + case 8: + for(uint8_t i = 40; i > 0; i--) { + if(!bit_read(data, i - 1)) { + subghz_protocol_blocks_set_bit_array( + 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } else { + subghz_protocol_blocks_set_bit_array( + 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); + } + } + break; + } + + instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( + upload_hex_data, + upload_hex_count_bit, + instance->encoder.upload, + instance->encoder.size_upload, + subghz_protocol_chamb_code_const.te_short, + SubGhzProtocolBlockAlignBitLeft); + + return true; +} + +bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderChamb_Code* 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_chamb_code_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); + + if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_chamb_code_stop(void* context) { + SubGhzProtocolEncoderChamb_Code* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context) { + SubGhzProtocolEncoderChamb_Code* 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_chamb_code_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolDecoderChamb_Code)); + instance->base.protocol = &subghz_protocol_chamb_code; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_chamb_code_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + free(instance); +} + +void subghz_protocol_decoder_chamb_code_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; +} + +static bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) { + uint64_t data_tmp = data[0]; + uint64_t data_res = 0; + for(uint8_t i = 0; i < size; i++) { + if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) { + bit_write(data_res, i, 0); + } else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) { + bit_write(data_res, i, 1); + } else { + return false; + } + data_tmp >>= 4; + } + data[0] = data_res; + return true; +} + +static bool subghz_protocol_decoder_chamb_code_check_mask_and_parse( + SubGhzProtocolDecoderChamb_Code* instance) { + furi_assert(instance); + if(instance->decoder.decode_count_bit > + subghz_protocol_chamb_code_const.min_count_bit_for_found + 1) + return false; + + if((instance->decoder.decode_data & CHAMBERLAIN_7_CODE_MASK) == + CHAMBERLAIN_7_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 7; + instance->decoder.decode_data &= ~CHAMBERLAIN_7_CODE_MASK; + instance->decoder.decode_data = (instance->decoder.decode_data >> 12) | + ((instance->decoder.decode_data >> 4) & 0xF); + } else if( + (instance->decoder.decode_data & CHAMBERLAIN_8_CODE_MASK) == + CHAMBERLAIN_8_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 8; + instance->decoder.decode_data &= ~CHAMBERLAIN_8_CODE_MASK; + instance->decoder.decode_data = instance->decoder.decode_data >> 4 | + CHAMBERLAIN_CODE_BIT_0 << 8; //DIP 6 no use + } else if( + (instance->decoder.decode_data & CHAMBERLAIN_9_CODE_MASK) == + CHAMBERLAIN_9_CODE_MASK_CHECK) { + instance->decoder.decode_count_bit = 9; + instance->decoder.decode_data &= ~CHAMBERLAIN_9_CODE_MASK; + instance->decoder.decode_data >>= 4; + } else { + return false; + } + return subghz_protocol_chamb_code_to_bit( + &instance->decoder.decode_data, instance->decoder.decode_count_bit); +} + +void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + switch(instance->decoder.parser_step) { + case Chamb_CodeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 39) < + subghz_protocol_chamb_code_const.te_delta * 20)) { + //Found header Chamb_Code + instance->decoder.parser_step = Chamb_CodeDecoderStepFoundStartBit; + } + break; + case Chamb_CodeDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta)) { + //Found start bit Chamb_Code + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_STOP; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + case Chamb_CodeDecoderStepSaveDuration: + if(!level) { //save interval + if(duration > subghz_protocol_chamb_code_const.te_short * 5) { + if(instance->decoder.decode_count_bit >= + subghz_protocol_chamb_code_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + if(subghz_protocol_decoder_chamb_code_check_mask_and_parse(instance)) { + 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 = Chamb_CodeDecoderStepReset; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Chamb_CodeDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + case Chamb_CodeDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( //Found stop bit Chamb_Code + instance->decoder.te_last, + subghz_protocol_chamb_code_const.te_short * 3) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_STOP; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short * 2) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 2) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_1; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short) < + subghz_protocol_chamb_code_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 3) < + subghz_protocol_chamb_code_const.te_delta)) { + instance->decoder.decode_data = instance->decoder.decode_data << 4 | + CHAMBERLAIN_CODE_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + + } else { + instance->decoder.parser_step = Chamb_CodeDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_chamb_code_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit > + subghz_protocol_chamb_code_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_chamb_code_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderChamb_Code* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Yek:0x%03lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); + + switch(instance->generic.data_count_bit) { + case 7: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_7_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo)); + break; + case 8: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_8_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo)); + break; + case 9: + furi_string_cat_printf( + output, + "DIP:" CHAMBERLAIN_9_CODE_DIP_PATTERN "\r\n", + CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo)); + break; + + default: + break; + } +} diff --git a/applications/main/subghz/protocols/chamberlain_code.h b/applications/main/subghz/protocols/chamberlain_code.h new file mode 100644 index 000000000..f87b64d90 --- /dev/null +++ b/applications/main/subghz/protocols/chamberlain_code.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_CHAMB_CODE_NAME "Cham_Code" + +typedef struct SubGhzProtocolDecoderChamb_Code SubGhzProtocolDecoderChamb_Code; +typedef struct SubGhzProtocolEncoderChamb_Code SubGhzProtocolEncoderChamb_Code; + +extern const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder; +extern const SubGhzProtocol subghz_protocol_chamb_code; + +/** + * Allocate SubGhzProtocolEncoderChamb_Code. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderChamb_Code* pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderChamb_Code. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void subghz_protocol_encoder_chamb_code_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + */ +void subghz_protocol_encoder_chamb_code_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderChamb_Code. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderChamb_Code* pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void subghz_protocol_decoder_chamb_code_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + */ +void subghz_protocol_decoder_chamb_code_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_chamb_code_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderChamb_Code. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance + * @param output Resulting text + */ +void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/clemsa.c b/applications/main/subghz/protocols/clemsa.c new file mode 100644 index 000000000..a2cb7a18b --- /dev/null +++ b/applications/main/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); + + if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break; + 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, + SubGhzRadioPreset* 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, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderClemsa* instance = context; + subghz_protocol_clemsa_check_remote_controller(&instance->generic); + //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); + furi_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/applications/main/subghz/protocols/clemsa.h b/applications/main/subghz/protocols/clemsa.h new file mode 100644 index 000000000..8858c1a3b --- /dev/null +++ b/applications/main/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, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_clemsa_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* 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, FuriString* output); diff --git a/applications/main/subghz/protocols/doitrand.c b/applications/main/subghz/protocols/doitrand.c new file mode 100644 index 000000000..6b31d4f27 --- /dev/null +++ b/applications/main/subghz/protocols/doitrand.c @@ -0,0 +1,356 @@ +#include "doitrand.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDoitrand" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \ + (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_doitrand_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 150, + .min_count_bit_for_found = 37, +}; + +struct SubGhzProtocolDecoderDoitrand { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDoitrand { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DoitrandDecoderStepReset = 0, + DoitrandDecoderStepFoundStartBit, + DoitrandDecoderStepSaveDuration, + DoitrandDecoderStepCheckDuration, +} DoitrandDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = { + .alloc = subghz_protocol_decoder_doitrand_alloc, + .free = subghz_protocol_decoder_doitrand_free, + + .feed = subghz_protocol_decoder_doitrand_feed, + .reset = subghz_protocol_decoder_doitrand_reset, + + .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data, + .serialize = subghz_protocol_decoder_doitrand_serialize, + .deserialize = subghz_protocol_decoder_doitrand_deserialize, + .get_string = subghz_protocol_decoder_doitrand_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = { + .alloc = subghz_protocol_encoder_doitrand_alloc, + .free = subghz_protocol_encoder_doitrand_free, + + .deserialize = subghz_protocol_encoder_doitrand_deserialize, + .stop = subghz_protocol_encoder_doitrand_stop, + .yield = subghz_protocol_encoder_doitrand_yield, +}; + +const SubGhzProtocol subghz_protocol_doitrand = { + .name = SUBGHZ_PROTOCOL_DOITRAND_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_doitrand_decoder, + .encoder = &subghz_protocol_doitrand_encoder, +}; + +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand)); + + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return true On success + */ +static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDoitrand* 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_doitrand_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); + + if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_doitrand_stop(void* context) { + SubGhzProtocolEncoderDoitrand* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) { + SubGhzProtocolEncoderDoitrand* 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_doitrand_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand)); + instance->base.protocol = &subghz_protocol_doitrand; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_doitrand_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + free(instance); +} + +void subghz_protocol_decoder_doitrand_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + instance->decoder.parser_step = DoitrandDecoderStepReset; +} + +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + + switch(instance->decoder.parser_step) { + case DoitrandDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) < + subghz_protocol_doitrand_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + } + break; + case DoitrandDecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) < + subghz_protocol_doitrand_const.te_delta * 3))) { + //Found start bit + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + case DoitrandDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 + + subghz_protocol_doitrand_const.te_delta)) { + instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_doitrand_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DoitrandDecoderStepCheckDuration; + } + } + break; + case DoitrandDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) < + subghz_protocol_doitrand_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) < + subghz_protocol_doitrand_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + } else { + instance->decoder.parser_step = DoitrandDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* 67892345 0 k 1 +* 0000082F5F => 00000000000000000 10 000010111101011111 +* 0002082F5F => 00000000000100000 10 000010111101011111 +* 0200082F5F => 00010000000000000 10 000010111101011111 +* 0400082F5F => 00100000000000000 10 000010111101011111 +* 0800082F5F => 01000000000000000 10 000010111101011111 +* 1000082F5F => 10000000000000000 10 000010111101011111 +* 0020082F5F => 00000001000000000 10 000010111101011111 +* 0040082F5F => 00000010000000000 10 000010111101011111 +* 0080082F5F => 00000100000000000 10 000010111101011111 +* 0100082F5F => 00001000000000000 10 000010111101011111 +* 000008AF5F => 00000000000000000 10 001010111101011111 +* 1FE208AF5F => 11111111000100000 10 001010111101011111 +* +* 0...9 - DIP +* k- KEY +*/ + instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1); + instance->btn = ((instance->data >> 18) & 0x3); +} + +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_doitrand_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_doitrand_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDoitrand* instance = context; + subghz_protocol_doitrand_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Btn:%X\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.btn, + CNT_TO_DIP(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/doitrand.h b/applications/main/subghz/protocols/doitrand.h new file mode 100644 index 000000000..30f1fffd0 --- /dev/null +++ b/applications/main/subghz/protocols/doitrand.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand" + +typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand; +typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand; + +extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder; +extern const SubGhzProtocol subghz_protocol_doitrand; + +/** + * Allocate SubGhzProtocolEncoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDoitrand. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + */ +void subghz_protocol_encoder_doitrand_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_doitrand_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDoitrand. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + */ +void subghz_protocol_decoder_doitrand_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDoitrand. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance + * @param output Resulting text + */ +void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/dooya.c b/applications/main/subghz/protocols/dooya.c new file mode 100644 index 000000000..c70b6d54e --- /dev/null +++ b/applications/main/subghz/protocols/dooya.c @@ -0,0 +1,447 @@ +#include "dooya.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolDooya" + +#define DOYA_SINGLE_CHANNEL 0xFF + +static const SubGhzBlockConst subghz_protocol_dooya_const = { + .te_short = 366, + .te_long = 733, + .te_delta = 120, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderDooya { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderDooya { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + DooyaDecoderStepReset = 0, + DooyaDecoderStepFoundStartBit, + DooyaDecoderStepSaveDuration, + DooyaDecoderStepCheckDuration, +} DooyaDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { + .alloc = subghz_protocol_decoder_dooya_alloc, + .free = subghz_protocol_decoder_dooya_free, + + .feed = subghz_protocol_decoder_dooya_feed, + .reset = subghz_protocol_decoder_dooya_reset, + + .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, + .serialize = subghz_protocol_decoder_dooya_serialize, + .deserialize = subghz_protocol_decoder_dooya_deserialize, + .get_string = subghz_protocol_decoder_dooya_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { + .alloc = subghz_protocol_encoder_dooya_alloc, + .free = subghz_protocol_encoder_dooya_free, + + .deserialize = subghz_protocol_encoder_dooya_deserialize, + .stop = subghz_protocol_encoder_dooya_stop, + .yield = subghz_protocol_encoder_dooya_yield, +}; + +const SubGhzProtocol subghz_protocol_dooya = { + .name = SUBGHZ_PROTOCOL_DOOYA_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_dooya_decoder, + .encoder = &subghz_protocol_dooya_encoder, +}; + +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); + + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderDooya* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderDooya instance + * @return true On success + */ +static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + if(bit_read(instance->generic.data, 0)) { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_long); + } else { + instance->encoder.upload[index++] = level_duration_make( + false, + (uint32_t)subghz_protocol_dooya_const.te_long * 12 + + subghz_protocol_dooya_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderDooya* 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_dooya_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); + + if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_dooya_stop(void* context) { + SubGhzProtocolEncoderDooya* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { + SubGhzProtocolEncoderDooya* 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_dooya_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); + instance->base.protocol = &subghz_protocol_dooya; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_dooya_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + free(instance); +} + +void subghz_protocol_decoder_dooya_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + instance->decoder.parser_step = DooyaDecoderStepReset; +} + +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + switch(instance->decoder.parser_step) { + case DooyaDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < + subghz_protocol_dooya_const.te_delta * 20)) { + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + } + break; + + case DooyaDecoderStepFoundStartBit: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < + subghz_protocol_dooya_const.te_delta * 3) { + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < + subghz_protocol_dooya_const.te_delta * 5) { + break; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = DooyaDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + + case DooyaDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { + //add last bit + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + break; + } + instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_dooya_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); + } + break; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < + subghz_protocol_dooya_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < + subghz_protocol_dooya_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = DooyaDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + } else { + instance->decoder.parser_step = DooyaDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * serial s/m ch key + * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * + * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 + * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 + * + * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 + * + * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * + * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 + * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 + * + * serial: 3 byte serial number + * s/m: single (b0000) / multi (b0001) channel console + * ch: channel if single (always b0101) or multi + * key: 0b00010001 - long press up + * 0b00011110 - short press up + * 0b00110011 - long press down + * 0b00111100 - short press down + * 0b01010101 - press stop + * 0b01111001 - press up + down + * 0b10000000 - press up + stop + * 0b10000001 - press down + stop + * 0b11001100 - press P2 + * +*/ + + instance->serial = (instance->data >> 16); + if((instance->data >> 12) & 0x0F) { + instance->cnt = (instance->data >> 8) & 0x0F; + } else { + instance->cnt = DOYA_SINGLE_CHANNEL; + } + instance->btn = instance->data & 0xFF; +} + +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_dooya_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +/** + * Get button name. + * @param btn Button number, 8 bit + */ +static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { + const char* btn_name; + switch(btn) { + case 0b00010001: + btn_name = "Up_Long"; + break; + case 0b00011110: + btn_name = "Up_Short"; + break; + case 0b00110011: + btn_name = "Down_Long"; + break; + case 0b00111100: + btn_name = "Down_Short"; + break; + case 0b01010101: + btn_name = "Stop"; + break; + case 0b01111001: + btn_name = "Up+Down"; + break; + case 0b10000000: + btn_name = "Up+Stop"; + break; + case 0b10000001: + btn_name = "Down+Stop"; + break; + case 0b11001100: + btn_name = "P2"; + break; + default: + btn_name = "Unknown"; + break; + } + return btn_name; +} + +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderDooya* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%010llX\r\n" + "Sn:0x%08lX\r\n" + "Btn:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + subghz_protocol_dooya_get_name_button(instance->generic.btn)); + if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { + furi_string_cat_printf(output, "Ch:Single\r\n"); + } else { + furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); + } +} diff --git a/applications/main/subghz/protocols/dooya.h b/applications/main/subghz/protocols/dooya.h new file mode 100644 index 000000000..f0cf843c0 --- /dev/null +++ b/applications/main/subghz/protocols/dooya.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" + +typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; +typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; + +extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; +extern const SubGhzProtocol subghz_protocol_dooya; + +/** + * Allocate SubGhzProtocolEncoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance + */ +void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderDooya. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + */ +void subghz_protocol_encoder_dooya_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderDooya instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_dooya_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderDooya. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance + */ +void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + */ +void subghz_protocol_decoder_dooya_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_dooya_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderDooya. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderDooya instance + * @param output Resulting text + */ +void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/faac_slh.c b/applications/main/subghz/protocols/faac_slh.c new file mode 100644 index 000000000..7572bd8ab --- /dev/null +++ b/applications/main/subghz/protocols/faac_slh.c @@ -0,0 +1,512 @@ +#include "faac_slh.h" +#include "../subghz_keystore.h" +#include +#include "keeloq_common.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolFaacSLH" + +static const SubGhzBlockConst subghz_protocol_faac_slh_const = { + .te_short = 255, + .te_long = 595, + .te_delta = 100, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderFaacSLH { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; +}; + +struct SubGhzProtocolEncoderFaacSLH { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; +}; + +typedef enum { + FaacSLHDecoderStepReset = 0, + FaacSLHDecoderStepFoundPreambula, + FaacSLHDecoderStepSaveDuration, + FaacSLHDecoderStepCheckDuration, +} FaacSLHDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder = { + .alloc = subghz_protocol_decoder_faac_slh_alloc, + .free = subghz_protocol_decoder_faac_slh_free, + + .feed = subghz_protocol_decoder_faac_slh_feed, + .reset = subghz_protocol_decoder_faac_slh_reset, + + .get_hash_data = subghz_protocol_decoder_faac_slh_get_hash_data, + .serialize = subghz_protocol_decoder_faac_slh_serialize, + .deserialize = subghz_protocol_decoder_faac_slh_deserialize, + .get_string = subghz_protocol_decoder_faac_slh_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder = { + .alloc = subghz_protocol_encoder_faac_slh_alloc, + .free = subghz_protocol_encoder_faac_slh_free, + + .deserialize = subghz_protocol_encoder_faac_slh_deserialize, + .stop = subghz_protocol_encoder_faac_slh_stop, + .yield = subghz_protocol_encoder_faac_slh_yield, +}; + +const SubGhzProtocol subghz_protocol_faac_slh = { + .name = SUBGHZ_PROTOCOL_FAAC_SLH_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_faac_slh_decoder, + .encoder = &subghz_protocol_faac_slh_encoder, +}; + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_faac_slh_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolEncoderFaacSLH)); + + instance->base.protocol = &subghz_protocol_faac_slh; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_faac_slh_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderFaacSLH* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) { + instance->generic.cnt++; + uint32_t fix = instance->generic.serial << 4 | instance->generic.btn; + uint32_t hop = 0; + uint32_t decrypt = 0; + uint64_t man = 0; + int res = 0; + char fixx[8] = {}; + int shiftby = 32; + for(int i = 0; i < 8; i++) { + fixx[i] = (fix >> (shiftby -= 4)) & 0xF; + } + if((instance->generic.cnt % 2) == 0) { + decrypt = fixx[6] << 28 | fixx[7] << 24 | fixx[5] << 20 | + (instance->generic.cnt & 0xFFFFF); + } else { + decrypt = fixx[2] << 28 | fixx[3] << 24 | fixx[4] << 20 | + (instance->generic.cnt & 0xFFFFF); + } + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_FAAC: + //FAAC Learning + man = subghz_protocol_keeloq_common_faac_learning( + instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + } + break; + } + } + if(hop) { + instance->generic.data = (uint64_t)fix << 32 | hop; + } + return true; +} + +bool subghz_protocol_faac_slh_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + // roguemaster don't steal!!! + SubGhzProtocolEncoderFaacSLH* instance = context; + instance->generic.serial = serial; + instance->generic.btn = btn; + instance->generic.cnt = (cnt & 0xFFFFF); + instance->generic.seed = seed; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_faac_slh_gen_data(instance); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @return true On success + */ +static bool subghz_protocol_encoder_faac_slh_get_upload(SubGhzProtocolEncoderFaacSLH* instance) { + furi_assert(instance); + + subghz_protocol_faac_slh_gen_data(instance); + size_t index = 0; + size_t size_upload = 2 + (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; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderFaacSLH* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Missing Seed"); + break; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_faac_slh_get_upload(instance); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_faac_slh_stop(void* context) { + SubGhzProtocolEncoderFaacSLH* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context) { + SubGhzProtocolEncoderFaacSLH* 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_faac_slh_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolDecoderFaacSLH)); + instance->base.protocol = &subghz_protocol_faac_slh; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_faac_slh_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + free(instance); +} + +void subghz_protocol_decoder_faac_slh_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + instance->decoder.parser_step = FaacSLHDecoderStepReset; +} + +void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + + switch(instance->decoder.parser_step) { + case FaacSLHDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < + subghz_protocol_faac_slh_const.te_delta * 3)) { + instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; + } + break; + case FaacSLHDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < + subghz_protocol_faac_slh_const.te_delta * 3)) { + //Found Preambula + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + case FaacSLHDecoderStepSaveDuration: + if(level) { + if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 + + subghz_protocol_faac_slh_const.te_delta)) { + instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; + if(instance->decoder.decode_count_bit == + subghz_protocol_faac_slh_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = FaacSLHDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + case FaacSLHDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_short) < + subghz_protocol_faac_slh_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long) < + subghz_protocol_faac_slh_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_long) < + subghz_protocol_faac_slh_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_short) < + subghz_protocol_faac_slh_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + } else { + instance->decoder.parser_step = FaacSLHDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manifacture_name Manufacturer name + */ +static void subghz_protocol_faac_slh_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint32_t code_fix = instance->data >> 32; + uint32_t code_hop = instance->data & 0xFFFFFFFF; + instance->serial = code_fix >> 4; + instance->btn = code_fix & 0xF; + uint32_t decrypt = 0; + uint64_t man; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_FAAC: + // FAAC Learning + man = subghz_protocol_keeloq_common_faac_learning( + instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(code_hop, man); + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + break; + } + } + instance->cnt = decrypt & 0xFFFFF; +} + +uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_faac_slh_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Unable to add Seed"); + res = false; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + return res; +} + +bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* 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_faac_slh_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "Missing Seed"); + break; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderFaacSLH* instance = context; + subghz_protocol_faac_slh_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + uint32_t code_fix = instance->generic.data >> 32; + uint32_t code_hop = instance->generic.data & 0xFFFFFFFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%lX%08lX\r\n" + "Fix:%08lX Cnt:%05lX\r\n" + "Hop:%08lX Btn:%X\r\n" + "Sn:%07lX Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + code_fix, + instance->generic.cnt, + code_hop, + instance->generic.btn, + instance->generic.serial, + instance->generic.seed); +} diff --git a/applications/main/subghz/protocols/faac_slh.h b/applications/main/subghz/protocols/faac_slh.h new file mode 100644 index 000000000..9390da43a --- /dev/null +++ b/applications/main/subghz/protocols/faac_slh.h @@ -0,0 +1,129 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_FAAC_SLH_NAME "Faac SLH" + +typedef struct SubGhzProtocolDecoderFaacSLH SubGhzProtocolDecoderFaacSLH; +typedef struct SubGhzProtocolEncoderFaacSLH SubGhzProtocolEncoderFaacSLH; + +extern const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder; +extern const SubGhzProtocol subghz_protocol_faac_slh; + +/** + * Allocate SubGhzProtocolEncoderFaacSLH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderFaacSLH* pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderFaacSLH. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void subghz_protocol_encoder_faac_slh_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param seed Seed value, 32 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_faac_slh_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + */ +void subghz_protocol_encoder_faac_slh_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderFaacSLH. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderFaacSLH* pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void subghz_protocol_decoder_faac_slh_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + */ +void subghz_protocol_decoder_faac_slh_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_faac_slh_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderFaacSLH. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance + * @param output Resulting text + */ +void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/gate_tx.c b/applications/main/subghz/protocols/gate_tx.c new file mode 100644 index 000000000..4c7c2d484 --- /dev/null +++ b/applications/main/subghz/protocols/gate_tx.c @@ -0,0 +1,334 @@ +#include "gate_tx.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolGateTx" + +static const SubGhzBlockConst subghz_protocol_gate_tx_const = { + .te_short = 350, + .te_long = 700, + .te_delta = 100, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderGateTx { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderGateTx { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + GateTXDecoderStepReset = 0, + GateTXDecoderStepFoundStartBit, + GateTXDecoderStepSaveDuration, + GateTXDecoderStepCheckDuration, +} GateTXDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder = { + .alloc = subghz_protocol_decoder_gate_tx_alloc, + .free = subghz_protocol_decoder_gate_tx_free, + + .feed = subghz_protocol_decoder_gate_tx_feed, + .reset = subghz_protocol_decoder_gate_tx_reset, + + .get_hash_data = subghz_protocol_decoder_gate_tx_get_hash_data, + .serialize = subghz_protocol_decoder_gate_tx_serialize, + .deserialize = subghz_protocol_decoder_gate_tx_deserialize, + .get_string = subghz_protocol_decoder_gate_tx_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder = { + .alloc = subghz_protocol_encoder_gate_tx_alloc, + .free = subghz_protocol_encoder_gate_tx_free, + + .deserialize = subghz_protocol_encoder_gate_tx_deserialize, + .stop = subghz_protocol_encoder_gate_tx_stop, + .yield = subghz_protocol_encoder_gate_tx_yield, +}; + +const SubGhzProtocol subghz_protocol_gate_tx = { + .name = SUBGHZ_PROTOCOL_GATE_TX_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_gate_tx_decoder, + .encoder = &subghz_protocol_gate_tx_encoder, +}; + +void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderGateTx* instance = malloc(sizeof(SubGhzProtocolEncoderGateTx)); + + instance->base.protocol = &subghz_protocol_gate_tx; + 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_gate_tx_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderGateTx* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderGateTx instance + * @return true On success + */ +static bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGateTx* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short * 49); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderGateTx* 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_gate_tx_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); + + if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_gate_tx_stop(void* context) { + SubGhzProtocolEncoderGateTx* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context) { + SubGhzProtocolEncoderGateTx* 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_gate_tx_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderGateTx* instance = malloc(sizeof(SubGhzProtocolDecoderGateTx)); + instance->base.protocol = &subghz_protocol_gate_tx; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_gate_tx_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + free(instance); +} + +void subghz_protocol_decoder_gate_tx_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + instance->decoder.parser_step = GateTXDecoderStepReset; +} + +void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + + switch(instance->decoder.parser_step) { + case GateTXDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short * 47) < + subghz_protocol_gate_tx_const.te_delta * 47)) { + //Found Preambula + instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; + } + break; + case GateTXDecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3))) { + //Found start bit + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + break; + case GateTXDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + + subghz_protocol_gate_tx_const.te_delta)) { + instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_gate_tx_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = GateTXDecoderStepCheckDuration; + } + } + break; + case GateTXDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_short) < + subghz_protocol_gate_tx_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_long) < + subghz_protocol_gate_tx_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short) < + subghz_protocol_gate_tx_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = GateTXDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + } else { + instance->decoder.parser_step = GateTXDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_gate_tx_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_gate_tx_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_gate_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_gate_tx_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_gate_tx_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderGateTx* instance = context; + subghz_protocol_gate_tx_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%06lX\r\n" + "Sn:%05lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/gate_tx.h b/applications/main/subghz/protocols/gate_tx.h new file mode 100644 index 000000000..4bfba3597 --- /dev/null +++ b/applications/main/subghz/protocols/gate_tx.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_GATE_TX_NAME "GateTX" + +typedef struct SubGhzProtocolDecoderGateTx SubGhzProtocolDecoderGateTx; +typedef struct SubGhzProtocolEncoderGateTx SubGhzProtocolEncoderGateTx; + +extern const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder; +extern const SubGhzProtocol subghz_protocol_gate_tx; + +/** + * Allocate SubGhzProtocolEncoderGateTx. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderGateTx* pointer to a SubGhzProtocolEncoderGateTx instance + */ +void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderGateTx. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + */ +void subghz_protocol_encoder_gate_tx_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + */ +void subghz_protocol_encoder_gate_tx_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderGateTx instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderGateTx. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderGateTx* pointer to a SubGhzProtocolDecoderGateTx instance + */ +void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + */ +void subghz_protocol_decoder_gate_tx_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + */ +void subghz_protocol_decoder_gate_tx_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_gate_tx_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderGateTx. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderGateTx instance + * @param output Resulting text + */ +void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek.c b/applications/main/subghz/protocols/holtek.c new file mode 100644 index 000000000..8aaad3b71 --- /dev/null +++ b/applications/main/subghz/protocols/holtek.c @@ -0,0 +1,374 @@ +#include "holtek.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://pdf1.alldatasheet.com/datasheet-pdf/view/82103/HOLTEK/HT640.html + * https://fccid.io/OJM-CMD-HHLR-XXXA + * + */ + +#define TAG "SubGhzProtocolHoltek" + +#define HOLTEK_HEADER_MASK 0xF000000000 +#define HOLTEK_HEADER 0x5000000000 + +static const SubGhzBlockConst subghz_protocol_holtek_const = { + .te_short = 430, + .te_long = 870, + .te_delta = 100, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderHoltek { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHoltek { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + HoltekDecoderStepReset = 0, + HoltekDecoderStepFoundStartBit, + HoltekDecoderStepSaveDuration, + HoltekDecoderStepCheckDuration, +} HoltekDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_decoder = { + .alloc = subghz_protocol_decoder_holtek_alloc, + .free = subghz_protocol_decoder_holtek_free, + + .feed = subghz_protocol_decoder_holtek_feed, + .reset = subghz_protocol_decoder_holtek_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_serialize, + .deserialize = subghz_protocol_decoder_holtek_deserialize, + .get_string = subghz_protocol_decoder_holtek_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_encoder = { + .alloc = subghz_protocol_encoder_holtek_alloc, + .free = subghz_protocol_encoder_holtek_free, + + .deserialize = subghz_protocol_encoder_holtek_deserialize, + .stop = subghz_protocol_encoder_holtek_stop, + .yield = subghz_protocol_encoder_holtek_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek = { + .name = SUBGHZ_PROTOCOL_HOLTEK_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_decoder, + .encoder = &subghz_protocol_holtek_encoder, +}; + +void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek* instance = malloc(sizeof(SubGhzProtocolEncoderHoltek)); + + instance->base.protocol = &subghz_protocol_holtek; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek instance + * @return true On success + */ +static bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHoltek* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short * 36); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek* 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_holtek_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); + + if(!subghz_protocol_encoder_holtek_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_holtek_stop(void* context) { + SubGhzProtocolEncoderHoltek* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_yield(void* context) { + SubGhzProtocolEncoderHoltek* 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_holtek_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek* instance = malloc(sizeof(SubGhzProtocolDecoderHoltek)); + instance->base.protocol = &subghz_protocol_holtek; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + instance->decoder.parser_step = HoltekDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + + switch(instance->decoder.parser_step) { + case HoltekDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short * 36) < + subghz_protocol_holtek_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; + } + break; + case HoltekDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + case HoltekDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 + + subghz_protocol_holtek_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_const.min_count_bit_for_found) { + if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { + 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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + + instance->decoder.parser_step = HoltekDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + case HoltekDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_long) < + subghz_protocol_holtek_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_long) < + subghz_protocol_holtek_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < + subghz_protocol_holtek_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = HoltekDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + } else { + instance->decoder.parser_step = HoltekDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* instance) { + if((instance->data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { + instance->serial = + subghz_protocol_blocks_reverse_key((instance->data >> 16) & 0xFFFFF, 20); + uint16_t btn = instance->data & 0xFFFF; + if((btn & 0xf) != 0xA) { + instance->btn = 0x1 << 4 | (btn & 0xF); + } else if(((btn >> 4) & 0xF) != 0xA) { + instance->btn = 0x2 << 4 | ((btn >> 4) & 0xF); + } else if(((btn >> 8) & 0xF) != 0xA) { + instance->btn = 0x3 << 4 | ((btn >> 8) & 0xF); + } else if(((btn >> 12) & 0xF) != 0xA) { + instance->btn = 0x4 << 4 | ((btn >> 12) & 0xF); + } else { + instance->btn = 0; + } + } else { + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_holtek_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_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_holtek_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek* instance = context; + subghz_protocol_holtek_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%05lX Btn:%X ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn >> 4); + + if((instance->generic.btn & 0xF) == 0xE) { + furi_string_cat_printf(output, "ON\r\n"); + } else if(((instance->generic.btn & 0xF) == 0xB)) { + furi_string_cat_printf(output, "OFF\r\n"); + } +} diff --git a/applications/main/subghz/protocols/holtek.h b/applications/main/subghz/protocols/holtek.h new file mode 100644 index 000000000..252a26dc7 --- /dev/null +++ b/applications/main/subghz/protocols/holtek.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_NAME "Holtek" + +typedef struct SubGhzProtocolDecoderHoltek SubGhzProtocolDecoderHoltek; +typedef struct SubGhzProtocolEncoderHoltek SubGhzProtocolEncoderHoltek; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_encoder; +extern const SubGhzProtocol subghz_protocol_holtek; + +/** + * Allocate SubGhzProtocolEncoderHoltek. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek* pointer to a SubGhzProtocolEncoderHoltek instance + */ +void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + */ +void subghz_protocol_encoder_holtek_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + */ +void subghz_protocol_encoder_holtek_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek* pointer to a SubGhzProtocolDecoderHoltek instance + */ +void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + */ +void subghz_protocol_decoder_holtek_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + */ +void subghz_protocol_decoder_holtek_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_holtek_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek_ht12x.c b/applications/main/subghz/protocols/holtek_ht12x.c new file mode 100644 index 000000000..169387ded --- /dev/null +++ b/applications/main/subghz/protocols/holtek_ht12x.c @@ -0,0 +1,400 @@ +#include "holtek_ht12x.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf + * + */ + +#define TAG "SubGhzProtocolHoltek_HT12X" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define CNT_TO_DIP(dip) \ + (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \ + (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \ + (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1') + +static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = { + .te_short = 320, + .te_long = 640, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderHoltek_HT12X { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderHoltek_HT12X { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + Holtek_HT12XDecoderStepReset = 0, + Holtek_HT12XDecoderStepFoundStartBit, + Holtek_HT12XDecoderStepSaveDuration, + Holtek_HT12XDecoderStepCheckDuration, +} Holtek_HT12XDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = { + .alloc = subghz_protocol_decoder_holtek_th12x_alloc, + .free = subghz_protocol_decoder_holtek_th12x_free, + + .feed = subghz_protocol_decoder_holtek_th12x_feed, + .reset = subghz_protocol_decoder_holtek_th12x_reset, + + .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data, + .serialize = subghz_protocol_decoder_holtek_th12x_serialize, + .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize, + .get_string = subghz_protocol_decoder_holtek_th12x_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = { + .alloc = subghz_protocol_encoder_holtek_th12x_alloc, + .free = subghz_protocol_encoder_holtek_th12x_free, + + .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize, + .stop = subghz_protocol_encoder_holtek_th12x_stop, + .yield = subghz_protocol_encoder_holtek_th12x_yield, +}; + +const SubGhzProtocol subghz_protocol_holtek_th12x = { + .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_holtek_th12x_decoder, + .encoder = &subghz_protocol_holtek_th12x_encoder, +}; + +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X)); + + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return true On success + */ +static bool + subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36); + //Send start bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 2); + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 2); + } + } + return true; +} + +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_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); + + if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_holtek_th12x_stop(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) { + SubGhzProtocolEncoderHoltek_HT12X* 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_holtek_th12x_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoltek_HT12X* instance = + malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X)); + instance->base.protocol = &subghz_protocol_holtek_th12x; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_holtek_th12x_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + free(instance); +} + +void subghz_protocol_decoder_holtek_th12x_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; +} + +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + + switch(instance->decoder.parser_step) { + case Holtek_HT12XDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) < + subghz_protocol_holtek_th12x_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + } + break; + case Holtek_HT12XDecoderStepFoundStartBit: + if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + //Found StartBit + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = duration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepSaveDuration: + //save duration + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 + + subghz_protocol_holtek_th12x_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 3 + 1); + + 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->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; + break; + } else { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + case Holtek_HT12XDecoderStepCheckDuration: + if(level) { + instance->te += duration; + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) < + subghz_protocol_holtek_th12x_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) < + subghz_protocol_holtek_th12x_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + } else { + instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = instance->data & 0x0F; + instance->cnt = (instance->data >> 4) & 0xFF; +} + +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + ret = true; + } while(false); + return ret; +} + +static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 3) & 0x1) == 0x0 ? "B1 " : ""), + (((event >> 2) & 0x1) == 0x0 ? "B2 " : ""), + (((event >> 1) & 0x1) == 0x0 ? "B3 " : ""), + (((event >> 0) & 0x1) == 0x0 ? "B4 " : "")); +} + +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoltek_HT12X* instance = context; + subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%03lX\r\n" + "Btn: ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFF)); + subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output); + furi_string_cat_printf( + output, + "DIP:" DIP_PATTERN "\r\n" + "Te:%luus\r\n", + CNT_TO_DIP(instance->generic.cnt), + instance->te); +} diff --git a/applications/main/subghz/protocols/holtek_ht12x.h b/applications/main/subghz/protocols/holtek_ht12x.h new file mode 100644 index 000000000..7b5c31dd7 --- /dev/null +++ b/applications/main/subghz/protocols/holtek_ht12x.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X" + +typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X; +typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X; + +extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder; +extern const SubGhzProtocol subghz_protocol_holtek_th12x; + +/** + * Allocate SubGhzProtocolEncoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + */ +void subghz_protocol_encoder_holtek_th12x_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoltek_HT12X. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + */ +void subghz_protocol_decoder_holtek_th12x_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance + * @param output Resulting text + */ +void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/honeywell_wdb.c b/applications/main/subghz/protocols/honeywell_wdb.c new file mode 100644 index 000000000..3b940fc67 --- /dev/null +++ b/applications/main/subghz/protocols/honeywell_wdb.c @@ -0,0 +1,399 @@ +#include "honeywell_wdb.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolHoneywellWDB" + +/* + * + * https://github.com/klohner/honeywell-wireless-doorbell + * + */ + +static const SubGhzBlockConst subghz_protocol_honeywell_wdb_const = { + .te_short = 160, + .te_long = 320, + .te_delta = 60, + .min_count_bit_for_found = 48, +}; + +struct SubGhzProtocolDecoderHoneywell_WDB { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + const char* device_type; + const char* alert; + uint8_t secret_knock; + uint8_t relay; + uint8_t lowbat; +}; + +struct SubGhzProtocolEncoderHoneywell_WDB { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Honeywell_WDBDecoderStepReset = 0, + Honeywell_WDBDecoderStepFoundStartBit, + Honeywell_WDBDecoderStepSaveDuration, + Honeywell_WDBDecoderStepCheckDuration, +} Honeywell_WDBDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder = { + .alloc = subghz_protocol_decoder_honeywell_wdb_alloc, + .free = subghz_protocol_decoder_honeywell_wdb_free, + + .feed = subghz_protocol_decoder_honeywell_wdb_feed, + .reset = subghz_protocol_decoder_honeywell_wdb_reset, + + .get_hash_data = subghz_protocol_decoder_honeywell_wdb_get_hash_data, + .serialize = subghz_protocol_decoder_honeywell_wdb_serialize, + .deserialize = subghz_protocol_decoder_honeywell_wdb_deserialize, + .get_string = subghz_protocol_decoder_honeywell_wdb_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder = { + .alloc = subghz_protocol_encoder_honeywell_wdb_alloc, + .free = subghz_protocol_encoder_honeywell_wdb_free, + + .deserialize = subghz_protocol_encoder_honeywell_wdb_deserialize, + .stop = subghz_protocol_encoder_honeywell_wdb_stop, + .yield = subghz_protocol_encoder_honeywell_wdb_yield, +}; + +const SubGhzProtocol subghz_protocol_honeywell_wdb = { + .name = SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_honeywell_wdb_decoder, + .encoder = &subghz_protocol_honeywell_wdb_encoder, +}; + +void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHoneywell_WDB* instance = + malloc(sizeof(SubGhzProtocolEncoderHoneywell_WDB)); + + instance->base.protocol = &subghz_protocol_honeywell_wdb; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_honeywell_wdb_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @return true On success + */ +static bool subghz_protocol_encoder_honeywell_wdb_get_upload( + SubGhzProtocolEncoderHoneywell_WDB* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); + } + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); + return true; +} + +bool subghz_protocol_encoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHoneywell_WDB* 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_honeywell_wdb_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); + + if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_honeywell_wdb_stop(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) { + SubGhzProtocolEncoderHoneywell_WDB* 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_honeywell_wdb_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHoneywell_WDB* instance = + malloc(sizeof(SubGhzProtocolDecoderHoneywell_WDB)); + instance->base.protocol = &subghz_protocol_honeywell_wdb; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_honeywell_wdb_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + free(instance); +} + +void subghz_protocol_decoder_honeywell_wdb_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; +} + +void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + switch(instance->decoder.parser_step) { + case Honeywell_WDBDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + //Found header Honeywell_WDB + instance->decoder.decode_count_bit = 0; + instance->decoder.decode_data = 0; + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } + break; + case Honeywell_WDBDecoderStepSaveDuration: + if(level) { //save interval + if(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < + subghz_protocol_honeywell_wdb_const.te_delta) { + if((instance->decoder.decode_count_bit == + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) && + ((instance->decoder.decode_data & 0x01) == + subghz_protocol_blocks_get_parity( + instance->decoder.decode_data >> 1, + subghz_protocol_honeywell_wdb_const.min_count_bit_for_found - 1))) { + 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 = Honeywell_WDBDecoderStepReset; + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = Honeywell_WDBDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } + break; + case Honeywell_WDBDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_short) < + subghz_protocol_honeywell_wdb_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_long) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_long) < + subghz_protocol_honeywell_wdb_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short) < + subghz_protocol_honeywell_wdb_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; + } else + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } else { + instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzProtocolDecoderHoneywell_WDB* instance + */ +static void subghz_protocol_honeywell_wdb_check_remote_controller( + SubGhzProtocolDecoderHoneywell_WDB* instance) { + /* + * + * Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series Wireless Chimes + * 0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555 + * 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 + * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to recognize signal) + * XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter) + * .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested) + * .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor) + * .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on receiver) + * .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light pattern, 11 = full volume alarm) + * .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x rapidly) + * .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received transmission, only some models) + * .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't observe any effects) + * .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery alert) + * .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits) + * + */ + + instance->generic.serial = (instance->generic.data >> 28) & 0xFFFFF; + switch((instance->generic.data >> 20) & 0x3) { + case 0x02: + instance->device_type = "Doorbell"; + break; + case 0x01: + instance->device_type = "PIR-Motion"; + break; + default: + instance->device_type = "Unknown"; + break; + } + + switch((instance->generic.data >> 16) & 0x3) { + case 0x00: + instance->alert = "Normal"; + break; + case 0x01: + case 0x02: + instance->alert = "High"; + break; + case 0x03: + instance->alert = "Full"; + break; + default: + instance->alert = "Unknown"; + break; + } + + instance->secret_knock = (uint8_t)((instance->generic.data >> 4) & 0x1); + instance->relay = (uint8_t)((instance->generic.data >> 3) & 0x1); + instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1); +} + +uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_honeywell_wdb_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_honeywell_wdb_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_honeywell_wdb_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHoneywell_WDB* instance = context; + subghz_protocol_honeywell_wdb_check_remote_controller(instance); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%05lX\r\n" + "DT:%s Al:%s\r\n" + "SK:%01X R:%01X LBat:%01X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->device_type, + instance->alert, + instance->secret_knock, + instance->relay, + instance->lowbat); +} diff --git a/applications/main/subghz/protocols/honeywell_wdb.h b/applications/main/subghz/protocols/honeywell_wdb.h new file mode 100644 index 000000000..828631837 --- /dev/null +++ b/applications/main/subghz/protocols/honeywell_wdb.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME "Honeywell" + +typedef struct SubGhzProtocolDecoderHoneywell_WDB SubGhzProtocolDecoderHoneywell_WDB; +typedef struct SubGhzProtocolEncoderHoneywell_WDB SubGhzProtocolEncoderHoneywell_WDB; + +extern const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder; +extern const SubGhzProtocol subghz_protocol_honeywell_wdb; + +/** + * Allocate SubGhzProtocolEncoderHoneywell_WDB. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHoneywell_WDB* pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void subghz_protocol_encoder_honeywell_wdb_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + */ +void subghz_protocol_encoder_honeywell_wdb_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHoneywell_WDB. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHoneywell_WDB* pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void subghz_protocol_decoder_honeywell_wdb_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + */ +void subghz_protocol_decoder_honeywell_wdb_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_honeywell_wdb_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_honeywell_wdb_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance + * @param output Resulting text + */ +void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/hormann.c b/applications/main/subghz/protocols/hormann.c new file mode 100644 index 000000000..67b8cdfca --- /dev/null +++ b/applications/main/subghz/protocols/hormann.c @@ -0,0 +1,341 @@ +#include "hormann.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolHormannHSM" + +#define HORMANN_HSM_PATTERN 0xFF000000003 + +static const SubGhzBlockConst subghz_protocol_hormann_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 200, + .min_count_bit_for_found = 44, +}; + +struct SubGhzProtocolDecoderHormann { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderHormann { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + HormannDecoderStepReset = 0, + HormannDecoderStepFoundStartHeader, + HormannDecoderStepFoundHeader, + HormannDecoderStepFoundStartBit, + HormannDecoderStepSaveDuration, + HormannDecoderStepCheckDuration, +} HormannDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_hormann_decoder = { + .alloc = subghz_protocol_decoder_hormann_alloc, + .free = subghz_protocol_decoder_hormann_free, + + .feed = subghz_protocol_decoder_hormann_feed, + .reset = subghz_protocol_decoder_hormann_reset, + + .get_hash_data = subghz_protocol_decoder_hormann_get_hash_data, + .serialize = subghz_protocol_decoder_hormann_serialize, + .deserialize = subghz_protocol_decoder_hormann_deserialize, + .get_string = subghz_protocol_decoder_hormann_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_hormann_encoder = { + .alloc = subghz_protocol_encoder_hormann_alloc, + .free = subghz_protocol_encoder_hormann_free, + + .deserialize = subghz_protocol_encoder_hormann_deserialize, + .stop = subghz_protocol_encoder_hormann_stop, + .yield = subghz_protocol_encoder_hormann_yield, +}; + +const SubGhzProtocol subghz_protocol_hormann = { + .name = SUBGHZ_PROTOCOL_HORMANN_HSM_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_hormann_decoder, + .encoder = &subghz_protocol_hormann_encoder, +}; + +void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderHormann* instance = malloc(sizeof(SubGhzProtocolEncoderHormann)); + + instance->base.protocol = &subghz_protocol_hormann; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 2048; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_hormann_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderHormann* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderHormann instance + * @return true On success + */ +static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHormann* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1; + 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; + } + instance->encoder.repeat = 10; //original remote does 10 repeats + + for(size_t repeat = 0; repeat < 20; repeat++) { + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_long); + } + } + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); + return true; +} + +bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderHormann* 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_hormann_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); + + if(!subghz_protocol_encoder_hormann_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_hormann_stop(void* context) { + SubGhzProtocolEncoderHormann* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_hormann_yield(void* context) { + SubGhzProtocolEncoderHormann* 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_hormann_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderHormann* instance = malloc(sizeof(SubGhzProtocolDecoderHormann)); + instance->base.protocol = &subghz_protocol_hormann; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_hormann_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + free(instance); +} + +static bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) { + return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN; +} + +void subghz_protocol_decoder_hormann_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + instance->decoder.parser_step = HormannDecoderStepReset; +} + +void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + + switch(instance->decoder.parser_step) { + case HormannDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) < + subghz_protocol_hormann_const.te_delta * 24)) { + instance->decoder.parser_step = HormannDecoderStepFoundStartBit; + } + break; + case HormannDecoderStepFoundStartBit: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta)) { + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepSaveDuration: + if(level) { //save interval + if(duration >= (subghz_protocol_hormann_const.te_short * 5) && + subghz_protocol_decoder_hormann_check_pattern(instance)) { + instance->decoder.parser_step = HormannDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_hormann_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); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = HormannDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + case HormannDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_long) < + subghz_protocol_hormann_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_long) < + subghz_protocol_hormann_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < + subghz_protocol_hormann_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = HormannDecoderStepSaveDuration; + } else + instance->decoder.parser_step = HormannDecoderStepReset; + } else { + instance->decoder.parser_step = HormannDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 4) & 0xF; +} + +uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_hormann_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_hormann_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_hormann_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderHormann* instance = context; + subghz_protocol_hormann_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s\r\n" + "%dbit\r\n" + "Key:0x%03lX%08lX\r\n" + "Btn:0x%01X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/hormann.h b/applications/main/subghz/protocols/hormann.h new file mode 100644 index 000000000..857a50041 --- /dev/null +++ b/applications/main/subghz/protocols/hormann.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_HORMANN_HSM_NAME "Hormann HSM" + +typedef struct SubGhzProtocolDecoderHormann SubGhzProtocolDecoderHormann; +typedef struct SubGhzProtocolEncoderHormann SubGhzProtocolEncoderHormann; + +extern const SubGhzProtocolDecoder subghz_protocol_hormann_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_hormann_encoder; +extern const SubGhzProtocol subghz_protocol_hormann; + +/** + * Allocate SubGhzProtocolEncoderHormann. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderHormann* pointer to a SubGhzProtocolEncoderHormann instance + */ +void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderHormann. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + */ +void subghz_protocol_encoder_hormann_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + */ +void subghz_protocol_encoder_hormann_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderHormann instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_hormann_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderHormann. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderHormann* pointer to a SubGhzProtocolDecoderHormann instance + */ +void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + */ +void subghz_protocol_decoder_hormann_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + */ +void subghz_protocol_decoder_hormann_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_hormann_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderHormann. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderHormann instance + * @param output Resulting text + */ +void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ido.c b/applications/main/subghz/protocols/ido.c new file mode 100644 index 000000000..dff9defc0 --- /dev/null +++ b/applications/main/subghz/protocols/ido.c @@ -0,0 +1,234 @@ +#include "ido.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocol_iDo_117/111" + +static const SubGhzBlockConst subghz_protocol_ido_const = { + .te_short = 450, + .te_long = 1450, + .te_delta = 150, + .min_count_bit_for_found = 48, +}; + +struct SubGhzProtocolDecoderIDo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderIDo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + IDoDecoderStepReset = 0, + IDoDecoderStepFoundPreambula, + IDoDecoderStepSaveDuration, + IDoDecoderStepCheckDuration, +} IDoDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_ido_decoder = { + .alloc = subghz_protocol_decoder_ido_alloc, + .free = subghz_protocol_decoder_ido_free, + + .feed = subghz_protocol_decoder_ido_feed, + .reset = subghz_protocol_decoder_ido_reset, + + .get_hash_data = subghz_protocol_decoder_ido_get_hash_data, + .deserialize = subghz_protocol_decoder_ido_deserialize, + .serialize = subghz_protocol_decoder_ido_serialize, + .get_string = subghz_protocol_decoder_ido_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_ido_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_ido = { + .name = SUBGHZ_PROTOCOL_IDO_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_ido_decoder, + .encoder = &subghz_protocol_ido_encoder, +}; + +void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderIDo* instance = malloc(sizeof(SubGhzProtocolDecoderIDo)); + instance->base.protocol = &subghz_protocol_ido; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_ido_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_ido_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + instance->decoder.parser_step = IDoDecoderStepReset; +} + +void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + + switch(instance->decoder.parser_step) { + case IDoDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < + subghz_protocol_ido_const.te_delta * 5)) { + instance->decoder.parser_step = IDoDecoderStepFoundPreambula; + } + break; + case IDoDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < + subghz_protocol_ido_const.te_delta * 5)) { + //Found Preambula + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + case IDoDecoderStepSaveDuration: + if(level) { + if(duration >= ((uint32_t)subghz_protocol_ido_const.te_short * 5 + + subghz_protocol_ido_const.te_delta)) { + instance->decoder.parser_step = IDoDecoderStepFoundPreambula; + if(instance->decoder.decode_count_bit >= + subghz_protocol_ido_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = IDoDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + case IDoDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_ido_const.te_long) < + subghz_protocol_ido_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short) < + subghz_protocol_ido_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = IDoDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + } else { + instance->decoder.parser_step = IDoDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* instance) { + uint64_t code_found_reverse = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t code_fix = code_found_reverse & 0xFFFFFF; + + instance->serial = code_fix & 0xFFFFF; + instance->btn = (code_fix >> 20) & 0x0F; +} + +uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_ido_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_ido_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_ido_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderIDo* instance = context; + + subghz_protocol_ido_check_remote_controller(&instance->generic); + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_fix = code_found_reverse & 0xFFFFFF; + uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Fix:%06lX \r\n" + "Hop:%06lX \r\n" + "Sn:%05lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + code_fix, + code_hop, + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/ido.h b/applications/main/subghz/protocols/ido.h new file mode 100644 index 000000000..634f6ff89 --- /dev/null +++ b/applications/main/subghz/protocols/ido.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_IDO_NAME "iDo 117/111" + +typedef struct SubGhzProtocolDecoderIDo SubGhzProtocolDecoderIDo; +typedef struct SubGhzProtocolEncoderIDo SubGhzProtocolEncoderIDo; + +extern const SubGhzProtocolDecoder subghz_protocol_ido_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_ido_encoder; +extern const SubGhzProtocol subghz_protocol_ido; + +/** + * Allocate SubGhzProtocolDecoderIDo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderIDo* pointer to a SubGhzProtocolDecoderIDo instance + */ +void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + */ +void subghz_protocol_decoder_ido_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + */ +void subghz_protocol_decoder_ido_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_ido_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderIDo. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderIDo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/intertechno_v3.c b/applications/main/subghz/protocols/intertechno_v3.c new file mode 100644 index 000000000..2c4e514cc --- /dev/null +++ b/applications/main/subghz/protocols/intertechno_v3.c @@ -0,0 +1,472 @@ +#include "intertechno_v3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolIntertechnoV3" + +#define CH_PATTERN "%c%c%c%c" +#define CNT_TO_CH(ch) \ + (ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0') + +#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36 + +static const SubGhzBlockConst subghz_protocol_intertechno_v3_const = { + .te_short = 275, + .te_long = 1375, + .te_delta = 150, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderIntertechno_V3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderIntertechno_V3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + IntertechnoV3DecoderStepReset = 0, + IntertechnoV3DecoderStepStartSync, + IntertechnoV3DecoderStepFoundSync, + IntertechnoV3DecoderStepStartDuration, + IntertechnoV3DecoderStepSaveDuration, + IntertechnoV3DecoderStepCheckDuration, + IntertechnoV3DecoderStepEndDuration, +} IntertechnoV3DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = { + .alloc = subghz_protocol_decoder_intertechno_v3_alloc, + .free = subghz_protocol_decoder_intertechno_v3_free, + + .feed = subghz_protocol_decoder_intertechno_v3_feed, + .reset = subghz_protocol_decoder_intertechno_v3_reset, + + .get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data, + .serialize = subghz_protocol_decoder_intertechno_v3_serialize, + .deserialize = subghz_protocol_decoder_intertechno_v3_deserialize, + .get_string = subghz_protocol_decoder_intertechno_v3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = { + .alloc = subghz_protocol_encoder_intertechno_v3_alloc, + .free = subghz_protocol_encoder_intertechno_v3_free, + + .deserialize = subghz_protocol_encoder_intertechno_v3_deserialize, + .stop = subghz_protocol_encoder_intertechno_v3_stop, + .yield = subghz_protocol_encoder_intertechno_v3_yield, +}; + +const SubGhzProtocol subghz_protocol_intertechno_v3 = { + .name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_intertechno_v3_decoder, + .encoder = &subghz_protocol_intertechno_v3_encoder, +}; + +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3)); + + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return true On success + */ +static bool subghz_protocol_encoder_intertechno_v3_get_upload( + SubGhzProtocolEncoderIntertechno_V3* instance) { + furi_assert(instance); + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38); + //Send sync + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) { + //send bit dimm + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); + } + } + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderIntertechno_V3* 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_intertechno_v3_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + 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); + + if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_intertechno_v3_stop(void* context) { + SubGhzProtocolEncoderIntertechno_V3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) { + SubGhzProtocolEncoderIntertechno_V3* 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_intertechno_v3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderIntertechno_V3* instance = + malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3)); + instance->base.protocol = &subghz_protocol_intertechno_v3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_intertechno_v3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_intertechno_v3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; +} + +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + switch(instance->decoder.parser_step) { + case IntertechnoV3DecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) < + subghz_protocol_intertechno_v3_const.te_delta * 15)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + } + break; + case IntertechnoV3DecoderStepStartSync: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepFoundSync: + if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) < + subghz_protocol_intertechno_v3_const.te_delta * 3)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepStartDuration: + if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; + if((instance->decoder.decode_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + 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); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + case IntertechnoV3DecoderStepCheckDuration: + if(level) { + //Add 0 bit + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + } else if( + //Add 1 bit + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else if( + //Add dimm_state + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) && + (instance->decoder.decode_count_bit == 27)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; + + } else + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + + case IntertechnoV3DecoderStepEndDuration: + if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < + subghz_protocol_intertechno_v3_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) < + subghz_protocol_intertechno_v3_const.te_delta * 2))) { + instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; + } else { + instance->decoder.parser_step = IntertechnoV3DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * A frame is either 32 or 36 bits: + * + * _ + * start bit: | |__________ (T,10T) + * _ _ + * '0': | |_| |_____ (T,T,T,5T) + * _ _ + * '1': | |_____| |_ (T,5T,T,T) + * _ _ + * dimm: | |_| |_ (T,T,T,T) + * + * _ + * stop bit: | |____...____ (T,38T) + * + * if frame 32 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch on/off ~ch + * Key:0x3F86C59F => 00111111100001101100010110 0 1 1111 + * + * if frame 36 bits + * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch dimm ~ch dimm_level + * Key:0x42D2E8856 => 01000010110100101110100010 0 X 0101 0110 + * + */ + + if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + instance->serial = (instance->data >> 6) & 0x3FFFFFF; + if((instance->data >> 5) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~instance->data & 0xF); + } + instance->btn = (instance->data >> 4) & 0x1; + } else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + instance->serial = (instance->data >> 10) & 0x3FFFFFF; + if((instance->data >> 9) & 0x1) { + instance->cnt = 1 << 5; + } else { + instance->cnt = (~(instance->data >> 4) & 0xF); + } + instance->btn = (instance->data) & 0xF; + } else { + instance->serial = 0; + instance->cnt = 0; + instance->btn = 0; + } +} + +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderIntertechno_V3* instance = context; + + subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%.11s %db\r\n" + "Key:0x%08llX\r\n" + "Sn:%07lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial); + + if(instance->generic.data_count_bit == + subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { + if(instance->generic.cnt >> 5) { + furi_string_cat_printf( + output, "Ch: All Btn:%s\r\n", (instance->generic.btn ? "On" : "Off")); + } else { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Btn:%s\r\n", + CNT_TO_CH(instance->generic.cnt), + (instance->generic.btn ? "On" : "Off")); + } + } else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { + furi_string_cat_printf( + output, + "Ch:" CH_PATTERN " Dimm:%d%%\r\n", + CNT_TO_CH(instance->generic.cnt), + (int)(6.67 * (float)instance->generic.btn)); + } +} diff --git a/applications/main/subghz/protocols/intertechno_v3.h b/applications/main/subghz/protocols/intertechno_v3.h new file mode 100644 index 000000000..ffee14b04 --- /dev/null +++ b/applications/main/subghz/protocols/intertechno_v3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME "Intertechno_V3" + +typedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3; +typedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3; + +extern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder; +extern const SubGhzProtocol subghz_protocol_intertechno_v3; + +/** + * Allocate SubGhzProtocolEncoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void subghz_protocol_encoder_intertechno_v3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + */ +void subghz_protocol_encoder_intertechno_v3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderIntertechno_V3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + */ +void subghz_protocol_decoder_intertechno_v3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_intertechno_v3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderIntertechno_V3. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_intertechno_v3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq.c b/applications/main/subghz/protocols/keeloq.c new file mode 100644 index 000000000..a0970de4d --- /dev/null +++ b/applications/main/subghz/protocols/keeloq.c @@ -0,0 +1,1115 @@ +#include "keeloq.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolKeeloq" + +static const SubGhzBlockConst subghz_protocol_keeloq_const = { + .te_short = 400, + .te_long = 800, + .te_delta = 140, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderKeeloq { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +struct SubGhzProtocolEncoderKeeloq { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +typedef enum { + KeeloqDecoderStepReset = 0, + KeeloqDecoderStepCheckPreambula, + KeeloqDecoderStepSaveDuration, + KeeloqDecoderStepCheckDuration, +} KeeloqDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder = { + .alloc = subghz_protocol_decoder_keeloq_alloc, + .free = subghz_protocol_decoder_keeloq_free, + + .feed = subghz_protocol_decoder_keeloq_feed, + .reset = subghz_protocol_decoder_keeloq_reset, + + .get_hash_data = subghz_protocol_decoder_keeloq_get_hash_data, + .serialize = subghz_protocol_decoder_keeloq_serialize, + .deserialize = subghz_protocol_decoder_keeloq_deserialize, + .get_string = subghz_protocol_decoder_keeloq_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder = { + .alloc = subghz_protocol_encoder_keeloq_alloc, + .free = subghz_protocol_encoder_keeloq_free, + + .deserialize = subghz_protocol_encoder_keeloq_deserialize, + .stop = subghz_protocol_encoder_keeloq_stop, + .yield = subghz_protocol_encoder_keeloq_yield, +}; + +const SubGhzProtocol subghz_protocol_keeloq = { + .name = SUBGHZ_PROTOCOL_KEELOQ_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_keeloq_decoder, + .encoder = &subghz_protocol_keeloq_encoder, +}; + +static const char* mfname; +static int kl_type; + +void keeloq_reset_mfname() { + mfname = ""; +} + +void keeloq_reset_kl_type() { + kl_type = 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_keeloq_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKeeloq* instance = malloc(sizeof(SubGhzProtocolEncoderKeeloq)); + + instance->base.protocol = &subghz_protocol_keeloq; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 100; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + instance->manufacture_from_file = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_encoder_keeloq_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + furi_string_free(instance->manufacture_from_file); + free(instance->encoder.upload); + free(instance); +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial; + uint32_t decrypt = (uint32_t)btn << 28 | + (instance->generic.serial & 0x3FF) + << 16 | //ToDo in some protocols the discriminator is 0 + instance->generic.cnt; + uint32_t hop = 0; + uint64_t man = 0; + uint64_t code_found_reverse; + int res = 0; + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + + // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning + if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || + (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || + (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; + } + + // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning + if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || + (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || + (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || + (strcmp(instance->manufacture_name, "Normstahl") == 0)) { + decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + } + + if(strcmp(instance->manufacture_name, "Unknown") == 0) { + code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + hop = code_found_reverse & 0x00000000ffffffff; + } else if(strcmp(instance->manufacture_name, "AN-Motors") == 0) { + hop = (instance->generic.cnt & 0xFF) << 24 | (instance->generic.cnt & 0xFF) << 16 | + (instance->generic.btn & 0xF) << 12 | 0x404; + } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { + hop = instance->generic.cnt << 16 | (instance->generic.btn & 0xF) << 12 | 0x000; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + break; + case KEELOQ_LEARNING_NORMAL: + //Simple Learning + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_SECURE: + //Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + //Magic XOR type-1 Learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + //Magic Serial Type 1 learning + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = + subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 3) { + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->generic.seed, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + if(kl_type == 4) { + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + instance->generic.serial, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; + } + break; + } + } + } + if(hop) { + uint64_t yek = (uint64_t)fix << 32 | hop; + instance->generic.data = + subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); + } + return true; +} + +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_keeloq_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +bool subghz_protocol_keeloq_bft_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* instance = context; + instance->generic.serial = serial; + instance->generic.btn = btn; + instance->generic.cnt = cnt; + instance->generic.seed = seed; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + // roguuemaster don't steal.!!!! + bool res = subghz_protocol_keeloq_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool + subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_keeloq_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + size_t size_upload = 11 * 2 + 2 + (instance->generic.data_count_bit * 2) + 4; + 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; + } + + //Send header + for(uint8_t i = 11; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 10); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); + } + } + // +send 2 status bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); + // send end + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 40); + + return true; +} + +bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKeeloq* 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_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_D(TAG, "ENCODER: Missing Seed"); + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break; + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_keeloq_stop(void* context) { + SubGhzProtocolEncoderKeeloq* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_keeloq_yield(void* context) { + SubGhzProtocolEncoderKeeloq* 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_keeloq_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKeeloq* instance = malloc(sizeof(SubGhzProtocolDecoderKeeloq)); + instance->base.protocol = &subghz_protocol_keeloq; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + instance->manufacture_from_file = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_decoder_keeloq_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + furi_string_free(instance->manufacture_from_file); + + free(instance); +} + +void subghz_protocol_decoder_keeloq_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + instance->decoder.parser_step = KeeloqDecoderStepReset; + mfname = ""; + kl_type = 0; +} + +void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + switch(instance->decoder.parser_step) { + case KeeloqDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta) { + instance->decoder.parser_step = KeeloqDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KeeloqDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta)) { + instance->decoder.parser_step = KeeloqDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short * 10) < + subghz_protocol_keeloq_const.te_delta * 10)) { + // Found header + instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + break; + case KeeloqDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KeeloqDecoderStepCheckDuration; + } + break; + case KeeloqDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_keeloq_const.te_short * 2 + + subghz_protocol_keeloq_const.te_delta)) { + // Found end TX + instance->decoder.parser_step = KeeloqDecoderStepReset; + if((instance->decoder.decode_count_bit >= + subghz_protocol_keeloq_const.min_count_bit_for_found) && + (instance->decoder.decode_count_bit <= + subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { + if(instance->generic.data != instance->decoder.decode_data) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + subghz_protocol_keeloq_const.min_count_bit_for_found; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + } else if( + (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 * 2)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_keeloq_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.decode_count_bit++; + } + 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 * 2) && + (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < + subghz_protocol_keeloq_const.te_delta)) { + if(instance->decoder.decode_count_bit < + subghz_protocol_keeloq_const.min_count_bit_for_found) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else { + instance->decoder.decode_count_bit++; + } + instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + } else { + instance->decoder.parser_step = KeeloqDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Validation of decrypt data. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param decrypt Decrypd data + * @param btn Button number, 4 bit + * @param end_serial decrement the last 10 bits of the serial number + * @return true On success + */ +static inline bool subghz_protocol_keeloq_check_decrypt( + SubGhzBlockGeneric* instance, + uint32_t decrypt, + uint8_t btn, + uint32_t end_serial) { + furi_assert(instance); + if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) || + ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) { + instance->cnt = decrypt & 0x0000FFFF; + return true; + } + return false; +} + +/** + * Checking the accepted code against the database manafacture key + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param fix Fix part of the parcel + * @param hop Hop encrypted part of the parcel + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + * @return true on successful search + */ +static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( + SubGhzBlockGeneric* instance, + uint32_t fix, + uint32_t hop, + SubGhzKeystore* keystore, + const char** manufacture_name) { + // protocol HCS300 uses 10 bits in discriminator, HCS200 uses 8 bits, for backward compatibility, we are looking for the 8-bit pattern + // HCS300 -> uint16_t end_serial = (uint16_t)(fix & 0x3FF); + // HCS200 -> uint16_t end_serial = (uint16_t)(fix & 0xFF); + + uint16_t end_serial = (uint16_t)(fix & 0xFF); + uint8_t btn = (uint8_t)(fix >> 28); + uint32_t decrypt = 0; + uint64_t man; + int res = 0; + if(mfname == 0x0) { + mfname = ""; + } + + if(strcmp(mfname, "") == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_SECURE: + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: + man = subghz_protocol_keeloq_common_magic_serial_type2_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: + man = subghz_protocol_keeloq_common_magic_serial_type3_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_secure_learning(fix, instance->seed, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Magic xor type1 learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + break; + } + } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_SECURE: + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: + man = subghz_protocol_keeloq_common_magic_serial_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Secure Learning + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_secure_learning( + fix, instance->seed, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 3; + return 1; + } + + // Magic xor type1 learning + man = subghz_protocol_keeloq_common_magic_xor_type1_learning( + fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + // Check for mirrored man + man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); + if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 4; + return 1; + } + + break; + } + } + } + } + + *manufacture_name = "Unknown"; + mfname = "Unknown"; + instance->cnt = 0; + + return 0; +} + +static void subghz_protocol_keeloq_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t key_fix = key >> 32; + uint32_t key_hop = key & 0x00000000ffffffff; + // Check key AN-Motors + if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && + (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { + *manufacture_name = "AN-Motors"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) { + *manufacture_name = "HCS101"; + mfname = *manufacture_name; + instance->cnt = key_hop >> 16; + } else { + subghz_protocol_keeloq_check_remote_controller_selector( + instance, key_fix, key_hop, keystore, manufacture_name); + } + + instance->serial = key_fix & 0x0FFFFFFF; + instance->btn = key_fix >> 28; +} + +uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_keeloq_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + if(strcmp(instance->manufacture_name, "BFT") == 0) { + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add Seed"); + res = false; + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + } + + if(res && !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { + FURI_LOG_E(TAG, "DECODER Serialize: Unable to add manufacture name"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* 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_keeloq_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + + uint8_t seed_data[sizeof(uint32_t)] = {0}; + for(size_t i = 0; i < sizeof(uint32_t); i++) { + seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; + } + if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { + FURI_LOG_D(TAG, "DECODER: Missing Seed"); + } + instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | + seed_data[3]; + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKeeloq* instance = context; + + subghz_protocol_keeloq_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + if(strcmp(instance->manufacture_name, "BFT") == 0) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s Sd:%08lX", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name, + instance->generic.seed); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%01X\r\n" + "MF:%s", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name); + } +} diff --git a/applications/main/subghz/protocols/keeloq.h b/applications/main/subghz/protocols/keeloq.h new file mode 100644 index 000000000..7b0cfc3bd --- /dev/null +++ b/applications/main/subghz/protocols/keeloq.h @@ -0,0 +1,153 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_KEELOQ_NAME "KeeLoq" + +typedef struct SubGhzProtocolDecoderKeeloq SubGhzProtocolDecoderKeeloq; +typedef struct SubGhzProtocolEncoderKeeloq SubGhzProtocolEncoderKeeloq; + +extern const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder; +extern const SubGhzProtocol subghz_protocol_keeloq; + +void keeloq_reset_mfname(); + +void keeloq_reset_kl_type(); + +/** + * Allocate SubGhzProtocolEncoderKeeloq. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKeeloq. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_keeloq_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_keeloq_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Key generation for BFT. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 28 bit + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param seed Seed value, 32 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_keeloq_bft_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + uint32_t seed, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_keeloq_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_keeloq_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderKeeloq. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKeeloq* pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void subghz_protocol_decoder_keeloq_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + */ +void subghz_protocol_decoder_keeloq_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_keeloq_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKeeloq. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance + * @param output Resulting text + */ +void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq_common.c b/applications/main/subghz/protocols/keeloq_common.c new file mode 100644 index 000000000..1d2d04457 --- /dev/null +++ b/applications/main/subghz/protocols/keeloq_common.c @@ -0,0 +1,142 @@ +#include "keeloq_common.h" + +#include + +#include + +#define bit(x, n) (((x) >> (n)) & 1) +#define g5(x, a, b, c, d, e) \ + (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) + +/** Simple Learning Encrypt + * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + * @param key - manufacture (64bit) + * @return keeloq encrypt data + */ +inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for(r = 0; r < 528; r++) + x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^ + bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31))) + << 31); + return x; +} + +/** Simple Learning Decrypt + * @param data - keelog encrypt data + * @param key - manufacture (64bit) + * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + */ +inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) { + uint32_t x = data, r; + for(r = 0; r < 528; r++) + x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^ + bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30)); + return x; +} + +/** Normal Learning + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) { + uint32_t k1, k2; + + data &= 0x0FFFFFFF; + data |= 0x20000000; + k1 = subghz_protocol_keeloq_common_decrypt(data, key); + + data &= 0x0FFFFFFF; + data |= 0x60000000; + k2 = subghz_protocol_keeloq_common_decrypt(data, key); + + return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya +} + +/** Secure Learning + * @param data - serial number (28bit) + * @param seed - seed number (32bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t subghz_protocol_keeloq_common_secure_learning( + uint32_t data, + uint32_t seed, + const uint64_t key) { + uint32_t k1, k2; + + data &= 0x0FFFFFFF; + k1 = subghz_protocol_keeloq_common_decrypt(data, key); + k2 = subghz_protocol_keeloq_common_decrypt(seed, key); + + return ((uint64_t)k1 << 32) | k2; +} + +/** Magic_xor_type1 Learning + * @param data - serial number (28bit) + * @param xor - magic xor (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) { + data &= 0x0FFFFFFF; + return (((uint64_t)data << 32) | data) ^ xor; +} + +/** Faac SLH (Spa) Learning + * @param seed - seed number (32bit) + * @param key - mfkey (64bit) + * @return man_learning for this seed number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key) { + uint16_t hs = seed >> 16; + const uint16_t ending = 0x544D; + uint32_t lsb = (uint32_t)hs << 16 | ending; + uint64_t man = (uint64_t)subghz_protocol_keeloq_common_encrypt(seed, key) << 32 | + subghz_protocol_keeloq_common_encrypt(lsb, key); + return man; +} +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | + ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); +} + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { + uint8_t* p = (uint8_t*)&data; + uint8_t* m = (uint8_t*)&man; + m[7] = p[0]; + m[6] = p[1]; + m[5] = p[2]; + m[4] = p[3]; + return man; +} + +/** Magic_serial_type3 Learning + * @param data - serial number (24bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +inline uint64_t + subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { + return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); +} diff --git a/applications/main/subghz/protocols/keeloq_common.h b/applications/main/subghz/protocols/keeloq_common.h new file mode 100644 index 000000000..a6c0d346e --- /dev/null +++ b/applications/main/subghz/protocols/keeloq_common.h @@ -0,0 +1,100 @@ +#pragma once + +#include "base.h" + +#include + +/* + * Keeloq + * https://ru.wikipedia.org/wiki/KeeLoq + * https://phreakerclub.com/forum/showthread.php?t=1094 + * + */ +#define KEELOQ_NLF 0x3A5C742E + +/* + * KeeLoq learning types + * https://phreakerclub.com/forum/showthread.php?t=67 + */ +#define KEELOQ_LEARNING_UNKNOWN 0u +#define KEELOQ_LEARNING_SIMPLE 1u +#define KEELOQ_LEARNING_NORMAL 2u +#define KEELOQ_LEARNING_SECURE 3u +#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u +#define KEELOQ_LEARNING_FAAC 5u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u +#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u + +/** + * Simple Learning Encrypt + * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + * @param key - manufacture (64bit) + * @return keeloq encrypt data + */ +uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key); + +/** + * Simple Learning Decrypt + * @param data - keeloq encrypt data + * @param key - manufacture (64bit) + * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter + */ +uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key); + +/** + * Normal Learning + * @param data - serial number (28bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key); + +/** + * Secure Learning + * @param data - serial number (28bit) + * @param seed - seed number (32bit) + * @param key - manufacture (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t + subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key); + +/** + * Magic_xor_type1 Learning + * @param data - serial number (28bit) + * @param xor - magic xor (64bit) + * @return manufacture for this serial number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor); + +/** Faac SLH (Spa) Learning + * @param seed - seed number (32bit) + * @param key - mfkey (64bit) + * @return man_learning for this fix number (64bit) + */ +uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key); + +/** Magic_serial_type1 Learning + * @param data - serial number (28bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type2 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); + +/** Magic_serial_type3 Learning + * @param data - btn+serial number (32bit) + * @param man - magic man (64bit) + * @return manufacture for this serial number (64bit) + */ + +uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); diff --git a/applications/main/subghz/protocols/kia.c b/applications/main/subghz/protocols/kia.c new file mode 100644 index 000000000..a5d9e37ef --- /dev/null +++ b/applications/main/subghz/protocols/kia.c @@ -0,0 +1,279 @@ +#include "kia.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKIA" + +static const SubGhzBlockConst subghz_protocol_kia_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 100, + .min_count_bit_for_found = 61, +}; + +struct SubGhzProtocolDecoderKIA { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderKIA { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + KIADecoderStepReset = 0, + KIADecoderStepCheckPreambula, + KIADecoderStepSaveDuration, + KIADecoderStepCheckDuration, +} KIADecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kia_decoder = { + .alloc = subghz_protocol_decoder_kia_alloc, + .free = subghz_protocol_decoder_kia_free, + + .feed = subghz_protocol_decoder_kia_feed, + .reset = subghz_protocol_decoder_kia_reset, + + .get_hash_data = subghz_protocol_decoder_kia_get_hash_data, + .serialize = subghz_protocol_decoder_kia_serialize, + .deserialize = subghz_protocol_decoder_kia_deserialize, + .get_string = subghz_protocol_decoder_kia_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_kia = { + .name = SUBGHZ_PROTOCOL_KIA_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, + + .decoder = &subghz_protocol_kia_decoder, + .encoder = &subghz_protocol_kia_encoder, +}; + +void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA)); + instance->base.protocol = &subghz_protocol_kia; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_kia_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kia_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + instance->decoder.parser_step = KIADecoderStepReset; +} + +void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + + switch(instance->decoder.parser_step) { + case KIADecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + instance->decoder.parser_step = KIADecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case KIADecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + // Found header + instance->header_count++; + break; + } else if( + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + // Found start bit + if(instance->header_count > 15) { + instance->decoder.parser_step = KIADecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + case KIADecoderStepSaveDuration: + if(level) { + if(duration >= + (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) { + //Found stop bit + instance->decoder.parser_step = KIADecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_kia_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KIADecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + case KIADecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < + subghz_protocol_kia_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KIADecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < + subghz_protocol_kia_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KIADecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + } else { + instance->decoder.parser_step = KIADecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_kia_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x08; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x7F); + else + crc <<= 1; + } + } + return crc; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * 0x0F 0112 43B04EC 1 7D + * 0x0F 0113 43B04EC 1 DF + * 0x0F 0114 43B04EC 1 30 + * 0x0F 0115 43B04EC 2 13 + * 0x0F 0116 43B04EC 3 F5 + * CNT Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x08) + */ + + instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF); + instance->btn = (instance->data >> 8) & 0x0F; + instance->cnt = (instance->data >> 40) & 0xFFFF; +} + +uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kia_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != subghz_protocol_kia_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_kia_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKIA* instance = context; + + subghz_protocol_kia_check_remote_controller(&instance->generic); + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Sn:%07lX Btn:%X Cnt:%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/kia.h b/applications/main/subghz/protocols/kia.h new file mode 100644 index 000000000..a9afcf149 --- /dev/null +++ b/applications/main/subghz/protocols/kia.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_KIA_NAME "KIA Seed" + +typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA; +typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA; + +extern const SubGhzProtocolDecoder subghz_protocol_kia_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kia_encoder; +extern const SubGhzProtocol subghz_protocol_kia; + +/** + * Allocate SubGhzProtocolDecoderKIA. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKIA* pointer to a SubGhzProtocolDecoderKIA instance + */ +void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + */ +void subghz_protocol_decoder_kia_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + */ +void subghz_protocol_decoder_kia_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kia_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKIA. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKIA instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.c b/applications/main/subghz/protocols/kinggates_stylo_4k.c new file mode 100644 index 000000000..5f2a83d77 --- /dev/null +++ b/applications/main/subghz/protocols/kinggates_stylo_4k.c @@ -0,0 +1,581 @@ +#include "kinggates_stylo_4k.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocoKingGates_stylo_4k" + +static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { + .te_short = 400, + .te_long = 1100, + .te_delta = 140, + .min_count_bit_for_found = 89, +}; + +struct SubGhzProtocolDecoderKingGates_stylo_4k { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; +}; + +struct SubGhzProtocolEncoderKingGates_stylo_4k { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + SubGhzKeystore* keystore; +}; + +typedef enum { + KingGates_stylo_4kDecoderStepReset = 0, + KingGates_stylo_4kDecoderStepCheckPreambula, + KingGates_stylo_4kDecoderStepCheckStartBit, + KingGates_stylo_4kDecoderStepSaveDuration, + KingGates_stylo_4kDecoderStepCheckDuration, +} KingGates_stylo_4kDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { + .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_decoder_kinggates_stylo_4k_free, + + .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, + .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, + + .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, + .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, + .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, + .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { + .alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_encoder_kinggates_stylo_4k_free, + + .deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize, + .stop = subghz_protocol_encoder_kinggates_stylo_4k_stop, + .yield = subghz_protocol_encoder_kinggates_stylo_4k_yield, +}; + +const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { + .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, + .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, +}; + +// +// Encoder +// + +// Pre define function +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore); + +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k)); + + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* 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; +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_kinggates_stylo_4k_gen_data( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + UNUSED(btn); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53); + int res = 0; + uint32_t decrypt = 0; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + break; + } + } + instance->generic.cnt = decrypt & 0xFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + instance->generic.btn = (fix >> 17) & 0x0F; + instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt; + + uint64_t encrypt = 0; + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key); + encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32); + instance->generic.data_2 = encrypt << 4; + return true; + } + } + + return false; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return true On success + */ +static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + furi_assert(instance); + + // Gen new key + if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + // Start + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500); + + // Send header + for(uint8_t i = 12; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + + // After header + instance->encoder.upload[index - 1].duration = + (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2; + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2); + + // Send key fix + for(uint8_t i = 53; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Send key hop + for(uint8_t i = 36; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Set upload size after generating upload, fix it later + + instance->encoder.size_upload = index; + + return true; +} + +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->keystore); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + + subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +// +// Decoder +// +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + return instance; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + free(instance); +} + +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + + switch(instance->decoder.parser_step) { + case KingGates_stylo_4kDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; + instance->header_count++; + } + break; + case KingGates_stylo_4kDecoderStepCheckPreambula: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + break; + } + if((instance->header_count > 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + // Found header + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckStartBit: + if((level) && + DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->generic.data_2 = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + instance->generic.data = instance->generic.data_2; + instance->generic.data_2 = 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 = KingGates_stylo_4kDecoderStepReset; + instance->decoder.decode_data = 0; + instance->generic.data_2 = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + case KingGates_stylo_4kDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < + subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < + subghz_protocol_kinggates_stylo_4k_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + if(instance->decoder.decode_count_bit == 53) { + instance->generic.data_2 = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + } else { + instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; + instance->header_count = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param data Input encrypted data + * @param keystore Pointer to a SubGhzKeystore* instance + */ +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore) { + /** + * 9500us 12*(400/400) 2200/800|1-bit|0-bit| + * _ _ _ __ ___ _ + * ________| |_| |_..._| |_____| |_| |___| |..... + * + * 1-bit 400/1100 us + * 0-bit 1100/400 us + * + * The package consists of 89 bits of data, LSB first + * Data - 1C9037F0C80000 CE280BA00 + * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 + * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 + * + * Encryption - keeloq Simple Learning + * key C S[3] CNT + * Decrypt - 0xEC270B9C => 0x E C 27 0B9C + * + * + * +*/ + + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); + bool ret = false; + uint32_t decrypt = 0; + instance->btn = (fix >> 17) & 0x0F; + instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && + (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { + ret = true; + break; + } + } + } + if(ret) { + instance->cnt = decrypt & 0xFFFF; + } else { + instance->btn = 0; + instance->serial = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF; + } + + if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + return res; + + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; + subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore); + + furi_string_cat_printf( + output, + "%s\r\n" + "Key:0x%llX%07llX %dbit\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%04lX\r\n", + instance->generic.protocol_name, + instance->generic.data, + instance->generic.data_2, + instance->generic.data_count_bit, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} \ No newline at end of file diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.h b/applications/main/subghz/protocols/kinggates_stylo_4k.h new file mode 100644 index 000000000..9717f6715 --- /dev/null +++ b/applications/main/subghz/protocols/kinggates_stylo_4k.h @@ -0,0 +1,110 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" + +typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; +typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; + +extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; +extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; + +/** + * Allocate SubGhzProtocolEncoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + */ +void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance + * @param output Resulting text + */ +void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear.c b/applications/main/subghz/protocols/linear.c new file mode 100644 index 000000000..2fc8b20c8 --- /dev/null +++ b/applications/main/subghz/protocols/linear.c @@ -0,0 +1,352 @@ +#include "linear.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinear" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ + (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ + (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ + (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_const = { + .te_short = 500, + .te_long = 1500, + .te_delta = 150, + .min_count_bit_for_found = 10, +}; + +struct SubGhzProtocolDecoderLinear { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderLinear { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_decoder = { + .alloc = subghz_protocol_decoder_linear_alloc, + .free = subghz_protocol_decoder_linear_free, + + .feed = subghz_protocol_decoder_linear_feed, + .reset = subghz_protocol_decoder_linear_reset, + + .get_hash_data = subghz_protocol_decoder_linear_get_hash_data, + .serialize = subghz_protocol_decoder_linear_serialize, + .deserialize = subghz_protocol_decoder_linear_deserialize, + .get_string = subghz_protocol_decoder_linear_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_encoder = { + .alloc = subghz_protocol_encoder_linear_alloc, + .free = subghz_protocol_encoder_linear_free, + + .deserialize = subghz_protocol_encoder_linear_deserialize, + .stop = subghz_protocol_encoder_linear_stop, + .yield = subghz_protocol_encoder_linear_yield, +}; + +const SubGhzProtocol subghz_protocol_linear = { + .name = SUBGHZ_PROTOCOL_LINEAR_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_decoder, + .encoder = &subghz_protocol_linear_encoder, +}; + +void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinear* instance = malloc(sizeof(SubGhzProtocolEncoderLinear)); + + instance->base.protocol = &subghz_protocol_linear; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop) + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinear* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinear instance + * @return true On success + */ +static bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinear* 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; + } + + //Send key data + 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_linear_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 3); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 42); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 44); + } + + return true; +} + +bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinear* 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_linear_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); + + if(!subghz_protocol_encoder_linear_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_stop(void* context) { + SubGhzProtocolEncoderLinear* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_yield(void* context) { + SubGhzProtocolEncoderLinear* 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_linear_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinear* instance = malloc(sizeof(SubGhzProtocolDecoderLinear)); + instance->base.protocol = &subghz_protocol_linear; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; +} + +void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) < + subghz_protocol_linear_const.te_delta * 20)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_linear_const.te_short * 5)) { + instance->decoder.parser_step = LinearDecoderStepReset; + //checking that the duration matches the guardtime + if((DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) > + subghz_protocol_linear_const.te_delta * 20)) { + break; + } + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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); + } + break; + } + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < + subghz_protocol_linear_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short) < + subghz_protocol_linear_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_linear_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_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_linear_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinear* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo, + DATA_TO_DIP(code_found_lo)); +} diff --git a/applications/main/subghz/protocols/linear.h b/applications/main/subghz/protocols/linear.h new file mode 100644 index 000000000..923337ac2 --- /dev/null +++ b/applications/main/subghz/protocols/linear.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_NAME "Linear" + +typedef struct SubGhzProtocolDecoderLinear SubGhzProtocolDecoderLinear; +typedef struct SubGhzProtocolEncoderLinear SubGhzProtocolEncoderLinear; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_encoder; +extern const SubGhzProtocol subghz_protocol_linear; + +/** + * Allocate SubGhzProtocolEncoderLinear. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinear* pointer to a SubGhzProtocolEncoderLinear instance + */ +void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinear. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + */ +void subghz_protocol_encoder_linear_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + */ +void subghz_protocol_encoder_linear_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinear instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinear. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinear* pointer to a SubGhzProtocolDecoderLinear instance + */ +void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + */ +void subghz_protocol_decoder_linear_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + */ +void subghz_protocol_decoder_linear_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinear. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinear instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear_delta3.c b/applications/main/subghz/protocols/linear_delta3.c new file mode 100644 index 000000000..869edac84 --- /dev/null +++ b/applications/main/subghz/protocols/linear_delta3.c @@ -0,0 +1,359 @@ +#include "linear_delta3.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolLinearDelta3" + +#define DIP_PATTERN "%c%c%c%c%c%c%c%c" +#define DATA_TO_DIP(dip) \ + (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ + (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ + (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') + +static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { + .te_short = 500, + .te_long = 2000, + .te_delta = 150, + .min_count_bit_for_found = 8, +}; + +struct SubGhzProtocolDecoderLinearDelta3 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderLinearDelta3 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + LinearDecoderStepReset = 0, + LinearDecoderStepSaveDuration, + LinearDecoderStepCheckDuration, +} LinearDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { + .alloc = subghz_protocol_decoder_linear_delta3_alloc, + .free = subghz_protocol_decoder_linear_delta3_free, + + .feed = subghz_protocol_decoder_linear_delta3_feed, + .reset = subghz_protocol_decoder_linear_delta3_reset, + + .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, + .serialize = subghz_protocol_decoder_linear_delta3_serialize, + .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, + .get_string = subghz_protocol_decoder_linear_delta3_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { + .alloc = subghz_protocol_encoder_linear_delta3_alloc, + .free = subghz_protocol_encoder_linear_delta3_free, + + .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, + .stop = subghz_protocol_encoder_linear_delta3_stop, + .yield = subghz_protocol_encoder_linear_delta3_yield, +}; + +const SubGhzProtocol subghz_protocol_linear_delta3 = { + .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_linear_delta3_decoder, + .encoder = &subghz_protocol_linear_delta3_encoder, +}; + +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); + + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 16; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* 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; + } + + //Send key data + 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_linear_delta3_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + } + } + //Send end bit + if(bit_read(instance->generic.data, 0)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); + //Send PT_GUARD + instance->encoder.upload[index] = level_duration_make( + false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); + } + + return true; +} + +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_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); + + if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_linear_delta3_stop(void* context) { + SubGhzProtocolEncoderLinearDelta3* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { + SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderLinearDelta3* instance = + malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); + instance->base.protocol = &subghz_protocol_linear_delta3; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_linear_delta3_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + free(instance); +} + +void subghz_protocol_decoder_linear_delta3_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + instance->decoder.parser_step = LinearDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + switch(instance->decoder.parser_step) { + case LinearDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < + subghz_protocol_linear_delta3_const.te_delta * 24)) { + //Found header Linear + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } + break; + case LinearDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = LinearDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + case LinearDecoderStepCheckDuration: + if(!level) { + if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { + instance->decoder.parser_step = LinearDecoderStepReset; + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + if(instance->decoder.decode_count_bit == + subghz_protocol_linear_delta3_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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 = LinearDecoderStepSaveDuration; + instance->last_data = instance->decoder.decode_data; + } + break; + } + + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < + subghz_protocol_linear_delta3_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = LinearDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + + } else { + instance->decoder.parser_step = LinearDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8)); +} + +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_linear_delta3_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_linear_delta3_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderLinearDelta3* instance = context; + + uint32_t data = instance->generic.data & 0xFF; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX\r\n" + "DIP:" DIP_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + data, + DATA_TO_DIP(data)); +} diff --git a/applications/main/subghz/protocols/linear_delta3.h b/applications/main/subghz/protocols/linear_delta3.h new file mode 100644 index 000000000..2f0a32e68 --- /dev/null +++ b/applications/main/subghz/protocols/linear_delta3.h @@ -0,0 +1,111 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" + +typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; +typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; + +extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; +extern const SubGhzProtocol subghz_protocol_linear_delta3; + +/** + * Allocate SubGhzProtocolEncoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + */ +void subghz_protocol_encoder_linear_delta3_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderLinearDelta3. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + */ +void subghz_protocol_decoder_linear_delta3_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderLinearDelta3. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_linear_delta3_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/magellan.c b/applications/main/subghz/protocols/magellan.c new file mode 100644 index 000000000..67d3fe3d3 --- /dev/null +++ b/applications/main/subghz/protocols/magellan.c @@ -0,0 +1,445 @@ +#include "magellan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMagellan" + +static const SubGhzBlockConst subghz_protocol_magellan_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 100, + .min_count_bit_for_found = 32, +}; + +struct SubGhzProtocolDecoderMagellan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMagellan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MagellanDecoderStepReset = 0, + MagellanDecoderStepCheckPreambula, + MagellanDecoderStepFoundPreambula, + MagellanDecoderStepSaveDuration, + MagellanDecoderStepCheckDuration, +} MagellanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { + .alloc = subghz_protocol_decoder_magellan_alloc, + .free = subghz_protocol_decoder_magellan_free, + + .feed = subghz_protocol_decoder_magellan_feed, + .reset = subghz_protocol_decoder_magellan_reset, + + .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, + .serialize = subghz_protocol_decoder_magellan_serialize, + .deserialize = subghz_protocol_decoder_magellan_deserialize, + .get_string = subghz_protocol_decoder_magellan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { + .alloc = subghz_protocol_encoder_magellan_alloc, + .free = subghz_protocol_encoder_magellan_free, + + .deserialize = subghz_protocol_encoder_magellan_deserialize, + .stop = subghz_protocol_encoder_magellan_stop, + .yield = subghz_protocol_encoder_magellan_yield, +}; + +const SubGhzProtocol subghz_protocol_magellan = { + .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_magellan_decoder, + .encoder = &subghz_protocol_magellan_encoder, +}; + +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); + + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance + * @return true On success + */ +static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { + furi_assert(instance); + + size_t index = 0; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + for(uint8_t i = 0; i < 12; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); + + instance->encoder.size_upload = index; + return true; +} + +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMagellan* 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_magellan_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); + + if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_magellan_stop(void* context) { + SubGhzProtocolEncoderMagellan* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { + SubGhzProtocolEncoderMagellan* 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_magellan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); + instance->base.protocol = &subghz_protocol_magellan; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_magellan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_magellan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + instance->decoder.parser_step = MagellanDecoderStepReset; +} + +uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x00; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { + uint8_t data[3] = { + instance->decoder.decode_data >> 24, + instance->decoder.decode_data >> 16, + instance->decoder.decode_data >> 8}; + return (instance->decoder.decode_data & 0xFF) == + subghz_protocol_magellan_crc8(data, sizeof(data)); +} + +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + + switch(instance->decoder.parser_step) { + case MagellanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + + case MagellanDecoderStepCheckPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + // Found header + instance->header_count++; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2) && + (instance->header_count > 10)) { + instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepFoundPreambula: + if(level) { + instance->decoder.te_last = duration; + } else { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < + subghz_protocol_magellan_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta * 2)) { + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } + break; + + case MagellanDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = MagellanDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + + case MagellanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < + subghz_protocol_magellan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < + subghz_protocol_magellan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = MagellanDecoderStepSaveDuration; + } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { + //Found stop bit + if((instance->decoder.decode_count_bit == + subghz_protocol_magellan_const.min_count_bit_for_found) && + subghz_protocol_magellan_check_crc(instance)) { + 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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = MagellanDecoderStepReset; + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + } else { + instance->decoder.parser_step = MagellanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { + /* +* package 32b data 24b CRC8 +* 0x037AE4828 => 001101111010111001001000 00101000 +* +* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC +* +* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) +* +* event codes +* bit_0: 1-Open/Motion, 0-close/ok +* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) +* bit_2: ? +* bit_3: 1-power on +* bit_4: model type - wireless reed +* bit_5: model type - motion sensor +* bit_6: ? +* bit_7: ? +* +*/ + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); + instance->serial = data_rev & 0xFFFF; + instance->btn = (data_rev >> 16) & 0xFF; +} + +static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s%s%s%s%s", + ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : + (event & 0x1 ? " Motion" : " Ok")), + ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), + ((event >> 2) & 0x1 ? ", ?" : ""), + ((event >> 3) & 0x1 ? ", Power On" : ""), + ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), + ((event >> 5) & 0x1 ? ", MT:Motion_\nSensor" : ""), + ((event >> 6) & 0x1 ? ", ?" : ""), + ((event >> 7) & 0x1 ? ", ?" : "")); +} + +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_magellan_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_magellan_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMagellan* instance = context; + subghz_protocol_magellan_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Sn:%03ld%03ld, Event:0x%02X\r\n" + "Stat:", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + (instance->generic.serial >> 8) & 0xFF, + instance->generic.serial & 0xFF, + instance->generic.btn); + + subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); +} diff --git a/applications/main/subghz/protocols/magellan.h b/applications/main/subghz/protocols/magellan.h new file mode 100644 index 000000000..a179c9cb4 --- /dev/null +++ b/applications/main/subghz/protocols/magellan.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" + +typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; +typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; + +extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; +extern const SubGhzProtocol subghz_protocol_magellan; + +/** + * Allocate SubGhzProtocolEncoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance + */ +void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMagellan. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + */ +void subghz_protocol_encoder_magellan_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMagellan instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_magellan_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMagellan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance + */ +void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + */ +void subghz_protocol_decoder_magellan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_magellan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMagellan. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMagellan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/marantec.c b/applications/main/subghz/protocols/marantec.c new file mode 100644 index 000000000..d557c29b0 --- /dev/null +++ b/applications/main/subghz/protocols/marantec.c @@ -0,0 +1,393 @@ +#include "marantec.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolMarantec" + +static const SubGhzBlockConst subghz_protocol_marantec_const = { + .te_short = 1000, + .te_long = 2000, + .te_delta = 200, + .min_count_bit_for_found = 49, +}; + +struct SubGhzProtocolDecoderMarantec { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderMarantec { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MarantecDecoderStepReset = 0, + MarantecDecoderFoundHeader, + MarantecDecoderStepDecoderData, +} MarantecDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = { + .alloc = subghz_protocol_decoder_marantec_alloc, + .free = subghz_protocol_decoder_marantec_free, + + .feed = subghz_protocol_decoder_marantec_feed, + .reset = subghz_protocol_decoder_marantec_reset, + + .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data, + .serialize = subghz_protocol_decoder_marantec_serialize, + .deserialize = subghz_protocol_decoder_marantec_deserialize, + .get_string = subghz_protocol_decoder_marantec_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = { + .alloc = subghz_protocol_encoder_marantec_alloc, + .free = subghz_protocol_encoder_marantec_free, + + .deserialize = subghz_protocol_encoder_marantec_deserialize, + .stop = subghz_protocol_encoder_marantec_stop, + .yield = subghz_protocol_encoder_marantec_yield, +}; + +const SubGhzProtocol subghz_protocol_marantec = { + .name = SUBGHZ_PROTOCOL_MARANTEC_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_marantec_decoder, + .encoder = &subghz_protocol_marantec_encoder, +}; + +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec)); + + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_marantec_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_marantec_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance + */ +static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + if(!manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, + bit_read(instance->generic.data, instance->generic.data_count_bit - 1), + &result); + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5); + + for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_marantec_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.size_upload = index; +} + +uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { + uint8_t crc = 0x08; + size_t i, j; + for(i = 0; i < len; i++) { + crc ^= data[i]; + for(j = 0; j < 8; j++) { + if((crc & 0x80) != 0) + crc = (uint8_t)((crc << 1) ^ 0x1D); + else + crc <<= 1; + } + } + return crc; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) { + instance->btn = (instance->data >> 16) & 0xF; + instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); +} + +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMarantec* 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_marantec_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_marantec_remote_controller(&instance->generic); + subghz_protocol_encoder_marantec_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_marantec_stop(void* context) { + SubGhzProtocolEncoderMarantec* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_marantec_yield(void* context) { + SubGhzProtocolEncoderMarantec* 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_marantec_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec)); + instance->base.protocol = &subghz_protocol_marantec; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_marantec_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + free(instance); +} + +void subghz_protocol_decoder_marantec_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + ManchesterEvent event = ManchesterEventReset; + + switch(instance->decoder.parser_step) { + case MarantecDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) < + subghz_protocol_marantec_const.te_delta * 8)) { + //Found header marantec + instance->decoder.parser_step = MarantecDecoderStepDecoderData; + instance->decoder.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } + break; + case MarantecDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 + + subghz_protocol_marantec_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_marantec_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.decode_data = 1; + instance->decoder.decode_count_bit = 1; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < + subghz_protocol_marantec_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = MarantecDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_marantec_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_marantec_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMarantec* instance = context; + subghz_protocol_marantec_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/marantec.h b/applications/main/subghz/protocols/marantec.h new file mode 100644 index 000000000..e330ccf16 --- /dev/null +++ b/applications/main/subghz/protocols/marantec.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec" + +typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec; +typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec; + +extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder; +extern const SubGhzProtocol subghz_protocol_marantec; + +/** + * Allocate SubGhzProtocolEncoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance + */ +void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMarantec. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + */ +void subghz_protocol_encoder_marantec_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMarantec instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_marantec_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMarantec. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance + */ +void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + */ +void subghz_protocol_decoder_marantec_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_marantec_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMarantec. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMarantec instance + * @param output Resulting text + */ +void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/megacode.c b/applications/main/subghz/protocols/megacode.c new file mode 100644 index 000000000..05b5b6894 --- /dev/null +++ b/applications/main/subghz/protocols/megacode.c @@ -0,0 +1,429 @@ +#include "megacode.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://wiki.cuvoodoo.info/doku.php?id=megacode + * https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf + * https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf + * https://github.com/aaronsp777/megadecoder + * https://github.com/rjmendez/Linear_keyfob + * https://github.com/j07rdi/Linear_MegaCode_Garage_Remote + * + */ + +#define TAG "SubGhzProtocolMegaCode" + +static const SubGhzBlockConst subghz_protocol_megacode_const = { + .te_short = 1000, + .te_long = 1000, + .te_delta = 200, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderMegaCode { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint8_t last_bit; +}; + +struct SubGhzProtocolEncoderMegaCode { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + MegaCodeDecoderStepReset = 0, + MegaCodeDecoderStepFoundStartBit, + MegaCodeDecoderStepSaveDuration, + MegaCodeDecoderStepCheckDuration, +} MegaCodeDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_megacode_decoder = { + .alloc = subghz_protocol_decoder_megacode_alloc, + .free = subghz_protocol_decoder_megacode_free, + + .feed = subghz_protocol_decoder_megacode_feed, + .reset = subghz_protocol_decoder_megacode_reset, + + .get_hash_data = subghz_protocol_decoder_megacode_get_hash_data, + .serialize = subghz_protocol_decoder_megacode_serialize, + .deserialize = subghz_protocol_decoder_megacode_deserialize, + .get_string = subghz_protocol_decoder_megacode_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_megacode_encoder = { + .alloc = subghz_protocol_encoder_megacode_alloc, + .free = subghz_protocol_encoder_megacode_free, + + .deserialize = subghz_protocol_encoder_megacode_deserialize, + .stop = subghz_protocol_encoder_megacode_stop, + .yield = subghz_protocol_encoder_megacode_yield, +}; + +const SubGhzProtocol subghz_protocol_megacode = { + .name = SUBGHZ_PROTOCOL_MEGACODE_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_megacode_decoder, + .encoder = &subghz_protocol_megacode_encoder, +}; + +void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode)); + + instance->base.protocol = &subghz_protocol_megacode; + 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_megacode_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderMegaCode* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance + * @return true On success + */ +static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) { + furi_assert(instance); + uint8_t last_bit = 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; + } + + /* + * Due to the nature of the protocol + * + * 00000 1 + * _____|-| = 1 becomes + * + * 00 1 000 + * __|-|___ = 0 becomes + * + * it's easier for us to generate an upload backwards + */ + + size_t index = size_upload - 1; + + // Send end level + instance->encoder.upload[index--] = + level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); + if(bit_read(instance->generic.data, 0)) { + last_bit = 1; + } else { + last_bit = 0; + } + + //Send key data + for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) { + if(bit_read(instance->generic.data, i)) { + //if bit 1 + instance->encoder.upload[index--] = level_duration_make( + false, + last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 : + (uint32_t)subghz_protocol_megacode_const.te_short * 2); + last_bit = 1; + } else { + //if bit 0 + instance->encoder.upload[index--] = level_duration_make( + false, + last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 : + (uint32_t)subghz_protocol_megacode_const.te_short * 5); + last_bit = 0; + } + instance->encoder.upload[index--] = + level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); + } + + //Send PT_GUARD + if(bit_read(instance->generic.data, 0)) { + //if end bit 1 + instance->encoder.upload[index] = + level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11); + } else { + //if end bit 1 + instance->encoder.upload[index] = + level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14); + } + + return true; +} + +bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderMegaCode* 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_megacode_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); + + if(!subghz_protocol_encoder_megacode_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_megacode_stop(void* context) { + SubGhzProtocolEncoderMegaCode* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_megacode_yield(void* context) { + SubGhzProtocolEncoderMegaCode* 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_megacode_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode)); + instance->base.protocol = &subghz_protocol_megacode; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_megacode_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + free(instance); +} + +void subghz_protocol_decoder_megacode_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + instance->decoder.parser_step = MegaCodeDecoderStepReset; +} + +void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + switch(instance->decoder.parser_step) { + case MegaCodeDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) < + subghz_protocol_megacode_const.te_delta * 17)) { //10..16ms + //Found header MegaCode + instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit; + } + break; + case MegaCodeDecoderStepFoundStartBit: + if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + //Found start bit MegaCode + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->last_bit = 1; + + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + case MegaCodeDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_megacode_const.te_short * 10)) { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_megacode_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); + } + break; + } + + if(!instance->last_bit) { + instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3; + } else { + instance->decoder.te_last = duration; + } + instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + case MegaCodeDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) < + subghz_protocol_megacode_const.te_delta * 5) && + (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->last_bit = 1; + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) < + subghz_protocol_megacode_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < + subghz_protocol_megacode_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->last_bit = 0; + instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; + } else + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } else { + instance->decoder.parser_step = MegaCodeDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) { + /* + * Short: 1000 µs + * Long: 1000 µs + * Gap: 11000 .. 14000 µs + * A Linear Megacode transmission consists of 24 bit frames starting with + * the most significant bit and ending with the least. Each of the 24 bit + * frames is 6 milliseconds wide and always contains a single 1 millisecond + * pulse. A frame with more than 1 pulse or a frame with no pulse is invalid + * and a receiver should reset and begin watching for another start bit. + * Start bit is always 1. + * + * + * Example (I created with my own remote): + * Remote “A” has the code “17316”, a Facility Code of “3”, and a single button. + * Start bit (S) = 1 + * Facility Code 3 (F) = 0011 + * Remote Code (Key) 17316 = 43A4 = 0100001110100100 + * Button (Btn) 1 = 001 + * S F Key Btn + * Result = 1|0011|0100001110100100|001 + * + * 00000 1 + * _____|-| = 1 becomes + * + * 00 1 000 + * __|-|___ = 0 becomes + * + * The device needs to transmit with a 9000 µs gap between retransmissions: + * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 + * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 + * wait 9000 µs + * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 + * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 + * + */ + if((instance->data >> 23) == 1) { + instance->serial = (instance->data >> 3) & 0xFFFF; + instance->btn = instance->data & 0b111; + instance->cnt = (instance->data >> 19) & 0b1111; + } else { + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + } +} + +uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_megacode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_megacode_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_megacode_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderMegaCode* instance = context; + subghz_protocol_megacode_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%06lX\r\n" + "Sn:0x%04lX - %lu\r\n" + "Facility:%lX Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/megacode.h b/applications/main/subghz/protocols/megacode.h new file mode 100644 index 000000000..e31434fa3 --- /dev/null +++ b/applications/main/subghz/protocols/megacode.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_MEGACODE_NAME "MegaCode" + +typedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode; +typedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode; + +extern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder; +extern const SubGhzProtocol subghz_protocol_megacode; + +/** + * Allocate SubGhzProtocolEncoderMegaCode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderMegaCode. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void subghz_protocol_encoder_megacode_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + */ +void subghz_protocol_encoder_megacode_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_megacode_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderMegaCode. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void subghz_protocol_decoder_megacode_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + */ +void subghz_protocol_decoder_megacode_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_megacode_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderMegaCode. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance + * @param output Resulting text + */ +void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_radio.c b/applications/main/subghz/protocols/nero_radio.c new file mode 100644 index 000000000..c8126b1e1 --- /dev/null +++ b/applications/main/subghz/protocols/nero_radio.c @@ -0,0 +1,397 @@ +#include "nero_radio.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNeroRadio" + +static const SubGhzBlockConst subghz_protocol_nero_radio_const = { + .te_short = 200, + .te_long = 400, + .te_delta = 80, + .min_count_bit_for_found = 56, +}; + +struct SubGhzProtocolDecoderNeroRadio { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderNeroRadio { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NeroRadioDecoderStepReset = 0, + NeroRadioDecoderStepCheckPreambula, + NeroRadioDecoderStepSaveDuration, + NeroRadioDecoderStepCheckDuration, +} NeroRadioDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder = { + .alloc = subghz_protocol_decoder_nero_radio_alloc, + .free = subghz_protocol_decoder_nero_radio_free, + + .feed = subghz_protocol_decoder_nero_radio_feed, + .reset = subghz_protocol_decoder_nero_radio_reset, + + .get_hash_data = subghz_protocol_decoder_nero_radio_get_hash_data, + .serialize = subghz_protocol_decoder_nero_radio_serialize, + .deserialize = subghz_protocol_decoder_nero_radio_deserialize, + .get_string = subghz_protocol_decoder_nero_radio_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder = { + .alloc = subghz_protocol_encoder_nero_radio_alloc, + .free = subghz_protocol_encoder_nero_radio_free, + + .deserialize = subghz_protocol_encoder_nero_radio_deserialize, + .stop = subghz_protocol_encoder_nero_radio_stop, + .yield = subghz_protocol_encoder_nero_radio_yield, +}; + +const SubGhzProtocol subghz_protocol_nero_radio = { + .name = SUBGHZ_PROTOCOL_NERO_RADIO_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nero_radio_decoder, + .encoder = &subghz_protocol_nero_radio_encoder, +}; + +void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolEncoderNeroRadio)); + + instance->base.protocol = &subghz_protocol_nero_radio; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nero_radio_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNeroRadio* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @return true On success + */ +static bool + subghz_protocol_encoder_nero_radio_get_upload(SubGhzProtocolEncoderNeroRadio* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = 49 * 2 + 2 + (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; + } + + //Send header + for(uint8_t i = 0; i < 49; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + + //Send key data + 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_nero_radio_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_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_nero_radio_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); + } + return true; +} + +bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNeroRadio* 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_nero_radio_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); + + if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nero_radio_stop(void* context) { + SubGhzProtocolEncoderNeroRadio* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context) { + SubGhzProtocolEncoderNeroRadio* 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_nero_radio_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolDecoderNeroRadio)); + instance->base.protocol = &subghz_protocol_nero_radio; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nero_radio_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nero_radio_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + instance->decoder.parser_step = NeroRadioDecoderStepReset; +} + +void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + + switch(instance->decoder.parser_step) { + case NeroRadioDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta)) { + instance->decoder.parser_step = NeroRadioDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case NeroRadioDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short * 4) < + subghz_protocol_nero_radio_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short * 4) < + subghz_protocol_nero_radio_const.te_delta) { + // Found start bit + if(instance->header_count > 40) { + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + case NeroRadioDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = NeroRadioDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + case NeroRadioDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 + + subghz_protocol_nero_radio_const.te_delta * 2)) { + //Found stop bit + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + instance->decoder.parser_step = NeroRadioDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_nero_radio_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048 + break; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < + subghz_protocol_nero_radio_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < + subghz_protocol_nero_radio_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroRadioDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nero_radio_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_radio_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_nero_radio_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNeroRadio* instance = context; + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Yek:0x%lX%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nero_radio.h b/applications/main/subghz/protocols/nero_radio.h new file mode 100644 index 000000000..361da6173 --- /dev/null +++ b/applications/main/subghz/protocols/nero_radio.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NERO_RADIO_NAME "Nero Radio" + +typedef struct SubGhzProtocolDecoderNeroRadio SubGhzProtocolDecoderNeroRadio; +typedef struct SubGhzProtocolEncoderNeroRadio SubGhzProtocolEncoderNeroRadio; + +extern const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder; +extern const SubGhzProtocol subghz_protocol_nero_radio; + +/** + * Allocate SubGhzProtocolEncoderNeroRadio. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNeroRadio* pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNeroRadio. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void subghz_protocol_encoder_nero_radio_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + */ +void subghz_protocol_encoder_nero_radio_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNeroRadio. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNeroRadio* pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void subghz_protocol_decoder_nero_radio_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + */ +void subghz_protocol_decoder_nero_radio_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nero_radio_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNeroRadio. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_sketch.c b/applications/main/subghz/protocols/nero_sketch.c new file mode 100644 index 000000000..b124b717b --- /dev/null +++ b/applications/main/subghz/protocols/nero_sketch.c @@ -0,0 +1,382 @@ +#include "nero_sketch.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNeroSketch" + +static const SubGhzBlockConst subghz_protocol_nero_sketch_const = { + .te_short = 330, + .te_long = 660, + .te_delta = 150, + .min_count_bit_for_found = 40, +}; + +struct SubGhzProtocolDecoderNeroSketch { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderNeroSketch { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NeroSketchDecoderStepReset = 0, + NeroSketchDecoderStepCheckPreambula, + NeroSketchDecoderStepSaveDuration, + NeroSketchDecoderStepCheckDuration, +} NeroSketchDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder = { + .alloc = subghz_protocol_decoder_nero_sketch_alloc, + .free = subghz_protocol_decoder_nero_sketch_free, + + .feed = subghz_protocol_decoder_nero_sketch_feed, + .reset = subghz_protocol_decoder_nero_sketch_reset, + + .get_hash_data = subghz_protocol_decoder_nero_sketch_get_hash_data, + .serialize = subghz_protocol_decoder_nero_sketch_serialize, + .deserialize = subghz_protocol_decoder_nero_sketch_deserialize, + .get_string = subghz_protocol_decoder_nero_sketch_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder = { + .alloc = subghz_protocol_encoder_nero_sketch_alloc, + .free = subghz_protocol_encoder_nero_sketch_free, + + .deserialize = subghz_protocol_encoder_nero_sketch_deserialize, + .stop = subghz_protocol_encoder_nero_sketch_stop, + .yield = subghz_protocol_encoder_nero_sketch_yield, +}; + +const SubGhzProtocol subghz_protocol_nero_sketch = { + .name = SUBGHZ_PROTOCOL_NERO_SKETCH_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nero_sketch_decoder, + .encoder = &subghz_protocol_nero_sketch_encoder, +}; + +void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolEncoderNeroSketch)); + + instance->base.protocol = &subghz_protocol_nero_sketch; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nero_sketch_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNeroSketch* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @return true On success + */ +static bool + subghz_protocol_encoder_nero_sketch_get_upload(SubGhzProtocolEncoderNeroSketch* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = 47 * 2 + 2 + (instance->generic.data_count_bit * 2) + 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; + } + + //Send header + for(uint8_t i = 0; i < 47; i++) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + } + + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 4); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_long); + } + } + + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); + + return true; +} + +bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNeroSketch* 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_nero_sketch_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); + + if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nero_sketch_stop(void* context) { + SubGhzProtocolEncoderNeroSketch* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context) { + SubGhzProtocolEncoderNeroSketch* 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_nero_sketch_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolDecoderNeroSketch)); + instance->base.protocol = &subghz_protocol_nero_sketch; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nero_sketch_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nero_sketch_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + instance->decoder.parser_step = NeroSketchDecoderStepReset; +} + +void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + + switch(instance->decoder.parser_step) { + case NeroSketchDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta)) { + instance->decoder.parser_step = NeroSketchDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case NeroSketchDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short * 4) < + subghz_protocol_nero_sketch_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else if( + DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) { + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short * 4) < + subghz_protocol_nero_sketch_const.te_delta) { + // Found start bit + if(instance->header_count > 40) { + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + case NeroSketchDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_nero_sketch_const.te_short * 2 + + subghz_protocol_nero_sketch_const.te_delta * 2)) { + //Found stop bit + instance->decoder.parser_step = NeroSketchDecoderStepReset; + if(instance->decoder.decode_count_bit == + subghz_protocol_nero_sketch_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = NeroSketchDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + case NeroSketchDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_long) < + subghz_protocol_nero_sketch_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_long) < + subghz_protocol_nero_sketch_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < + subghz_protocol_nero_sketch_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + } else { + instance->decoder.parser_step = NeroSketchDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nero_sketch_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_nero_sketch_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_nero_sketch_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNeroSketch* instance = context; + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Yek:0x%lX%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nero_sketch.h b/applications/main/subghz/protocols/nero_sketch.h new file mode 100644 index 000000000..ac87fb00a --- /dev/null +++ b/applications/main/subghz/protocols/nero_sketch.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NERO_SKETCH_NAME "Nero Sketch" + +typedef struct SubGhzProtocolDecoderNeroSketch SubGhzProtocolDecoderNeroSketch; +typedef struct SubGhzProtocolEncoderNeroSketch SubGhzProtocolEncoderNeroSketch; + +extern const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder; +extern const SubGhzProtocol subghz_protocol_nero_sketch; + +/** + * Allocate SubGhzProtocolEncoderNeroSketch. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNeroSketch* pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNeroSketch. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void subghz_protocol_encoder_nero_sketch_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + */ +void subghz_protocol_encoder_nero_sketch_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNeroSketch. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNeroSketch* pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void subghz_protocol_decoder_nero_sketch_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + */ +void subghz_protocol_decoder_nero_sketch_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nero_sketch_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNeroSketch. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flo.c b/applications/main/subghz/protocols/nice_flo.c new file mode 100644 index 000000000..a57d5f4da --- /dev/null +++ b/applications/main/subghz/protocols/nice_flo.c @@ -0,0 +1,330 @@ +#include "nice_flo.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolNiceFLO" + +static const SubGhzBlockConst subghz_protocol_nice_flo_const = { + .te_short = 700, + .te_long = 1400, + .te_delta = 200, + .min_count_bit_for_found = 12, +}; + +struct SubGhzProtocolDecoderNiceFlo { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderNiceFlo { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + NiceFloDecoderStepReset = 0, + NiceFloDecoderStepFoundStartBit, + NiceFloDecoderStepSaveDuration, + NiceFloDecoderStepCheckDuration, +} NiceFloDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder = { + .alloc = subghz_protocol_decoder_nice_flo_alloc, + .free = subghz_protocol_decoder_nice_flo_free, + + .feed = subghz_protocol_decoder_nice_flo_feed, + .reset = subghz_protocol_decoder_nice_flo_reset, + + .get_hash_data = subghz_protocol_decoder_nice_flo_get_hash_data, + .serialize = subghz_protocol_decoder_nice_flo_serialize, + .deserialize = subghz_protocol_decoder_nice_flo_deserialize, + .get_string = subghz_protocol_decoder_nice_flo_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder = { + .alloc = subghz_protocol_encoder_nice_flo_alloc, + .free = subghz_protocol_encoder_nice_flo_free, + + .deserialize = subghz_protocol_encoder_nice_flo_deserialize, + .stop = subghz_protocol_encoder_nice_flo_stop, + .yield = subghz_protocol_encoder_nice_flo_yield, +}; + +const SubGhzProtocol subghz_protocol_nice_flo = { + .name = SUBGHZ_PROTOCOL_NICE_FLO_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nice_flo_decoder, + .encoder = &subghz_protocol_nice_flo_encoder, +}; + +void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlo)); + + instance->base.protocol = &subghz_protocol_nice_flo; + 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nice_flo_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlo* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @return true On success + */ +static bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNiceFlo* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short * 36); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlo* 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_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > + 2 * subghz_protocol_nice_flo_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); + + if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nice_flo_stop(void* context) { + SubGhzProtocolEncoderNiceFlo* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context) { + SubGhzProtocolEncoderNiceFlo* 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_nice_flo_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlo)); + instance->base.protocol = &subghz_protocol_nice_flo; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_nice_flo_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + free(instance); +} + +void subghz_protocol_decoder_nice_flo_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + instance->decoder.parser_step = NiceFloDecoderStepReset; +} + +void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + + switch(instance->decoder.parser_step) { + case NiceFloDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short * 36) < + subghz_protocol_nice_flo_const.te_delta * 36)) { + //Found header Nice Flo + instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; + } + break; + case NiceFloDecoderStepFoundStartBit: + if(!level) { + break; + } else if( + DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta) { + //Found start bit Nice Flo + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + case NiceFloDecoderStepSaveDuration: + if(!level) { //save interval + if(duration >= (subghz_protocol_nice_flo_const.te_short * 4)) { + instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit >= + subghz_protocol_nice_flo_const.min_count_bit_for_found) { + instance->generic.serial = 0x0; + instance->generic.btn = 0x0; + + 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); + } + break; + } + instance->decoder.te_last = duration; + instance->decoder.parser_step = NiceFloDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + case NiceFloDecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_long) < + subghz_protocol_nice_flo_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_long) < + subghz_protocol_nice_flo_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < + subghz_protocol_nice_flo_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; + } else + instance->decoder.parser_step = NiceFloDecoderStepReset; + } else { + instance->decoder.parser_step = NiceFloDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nice_flo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit < + subghz_protocol_nice_flo_const.min_count_bit_for_found) || + (instance->generic.data_count_bit > + 2 * subghz_protocol_nice_flo_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_nice_flo_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlo* instance = context; + + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_lo, + code_found_reverse_lo); +} diff --git a/applications/main/subghz/protocols/nice_flo.h b/applications/main/subghz/protocols/nice_flo.h new file mode 100644 index 000000000..e382e6146 --- /dev/null +++ b/applications/main/subghz/protocols/nice_flo.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NICE_FLO_NAME "Nice FLO" + +typedef struct SubGhzProtocolDecoderNiceFlo SubGhzProtocolDecoderNiceFlo; +typedef struct SubGhzProtocolEncoderNiceFlo SubGhzProtocolEncoderNiceFlo; + +extern const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder; +extern const SubGhzProtocol subghz_protocol_nice_flo; + +/** + * Allocate SubGhzProtocolEncoderNiceFlo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNiceFlo* pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNiceFlo. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void subghz_protocol_encoder_nice_flo_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + */ +void subghz_protocol_encoder_nice_flo_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderNiceFlo. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNiceFlo* pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void subghz_protocol_decoder_nice_flo_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + */ +void subghz_protocol_decoder_nice_flo_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nice_flo_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNiceFlo. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flor_s.c b/applications/main/subghz/protocols/nice_flor_s.c new file mode 100644 index 000000000..6447676cc --- /dev/null +++ b/applications/main/subghz/protocols/nice_flor_s.c @@ -0,0 +1,694 @@ +#include "nice_flor_s.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * https://phreakerclub.com/1615 + * https://phreakerclub.com/forum/showthread.php?t=2360 + * https://vrtp.ru/index.php?showtopic=27867 + */ + +#define TAG "SubGhzProtocolNiceFlorS" + +#define NICE_ONE_COUNT_BIT 72 +#define NICE_ONE_NAME "Nice One" + +static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 300, + .min_count_bit_for_found = 52, +}; + +struct SubGhzProtocolDecoderNiceFlorS { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + const char* nice_flor_s_rainbow_table_file_name; + uint64_t data; +}; + +struct SubGhzProtocolEncoderNiceFlorS { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + const char* nice_flor_s_rainbow_table_file_name; +}; + +typedef enum { + NiceFlorSDecoderStepReset = 0, + NiceFlorSDecoderStepCheckHeader, + NiceFlorSDecoderStepFoundHeader, + NiceFlorSDecoderStepSaveDuration, + NiceFlorSDecoderStepCheckDuration, +} NiceFlorSDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder = { + .alloc = subghz_protocol_decoder_nice_flor_s_alloc, + .free = subghz_protocol_decoder_nice_flor_s_free, + + .feed = subghz_protocol_decoder_nice_flor_s_feed, + .reset = subghz_protocol_decoder_nice_flor_s_reset, + + .get_hash_data = subghz_protocol_decoder_nice_flor_s_get_hash_data, + .serialize = subghz_protocol_decoder_nice_flor_s_serialize, + .deserialize = subghz_protocol_decoder_nice_flor_s_deserialize, + .get_string = subghz_protocol_decoder_nice_flor_s_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder = { + .alloc = subghz_protocol_encoder_nice_flor_s_alloc, + .free = subghz_protocol_encoder_nice_flor_s_free, + + .deserialize = subghz_protocol_encoder_nice_flor_s_deserialize, + .stop = subghz_protocol_encoder_nice_flor_s_stop, + .yield = subghz_protocol_encoder_nice_flor_s_yield, +}; + +const SubGhzProtocol subghz_protocol_nice_flor_s = { + .name = SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_nice_flor_s_decoder, + .encoder = &subghz_protocol_nice_flor_s_encoder, +}; + +static void subghz_protocol_nice_flor_s_remote_controller( + SubGhzBlockGeneric* instance, + const char* file_name); + +void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlorS)); + + instance->base.protocol = &subghz_protocol_nice_flor_s; + instance->generic.protocol_name = instance->base.protocol->name; + instance->nice_flor_s_rainbow_table_file_name = + subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); + if(instance->nice_flor_s_rainbow_table_file_name) { + FURI_LOG_D( + TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); + } + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1728; //wrong!! upload 186*16 = 2976 - actual size about 1728 + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_nice_flor_s_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @return true On success + */ +static void subghz_protocol_encoder_nice_flor_s_get_upload( + SubGhzProtocolEncoderNiceFlorS* instance, + uint8_t btn, + const char* file_name) { + furi_assert(instance); + size_t index = 0; + btn = instance->generic.btn; + + size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); + } else { + instance->encoder.size_upload = size_upload; + } + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; + uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name); + + for(int i = 0; i < 16; i++) { + static const uint64_t loops[16] = { + 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; + + uint8_t byte; + + byte = btn << 4 | (0xF ^ btn ^ loops[i]); + instance->generic.data = (uint64_t)byte << 44 | enc_part; + + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 37); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + } + } + //Send stop bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + //instance->encoder.upload[index++] = + //level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); + } + instance->encoder.size_upload = index; +} + +bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_nice_flor_s_remote_controller( + &instance->generic, instance->nice_flor_s_rainbow_table_file_name); + subghz_protocol_encoder_nice_flor_s_get_upload( + instance, instance->generic.btn, instance->nice_flor_s_rainbow_table_file_name); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_nice_flor_s_stop(void* context) { + SubGhzProtocolEncoderNiceFlorS* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { + SubGhzProtocolEncoderNiceFlorS* 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; +} + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 +// * @return crc +// */ +// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { +// uint8_t crc = 0; +// uint8_t crc_data = 0xff; +// for(uint8_t i = 4; i < 68; i++) { +// if(subghz_protocol_blocks_get_bit_array(p, i)) { +// crc = crc_data ^ 1; +// } else { +// crc = crc_data; +// } +// crc_data >>= 1; +// if((crc & 0x01)) { +// crc_data ^= 0x97; +// } +// } +// crc = 0; +// for(uint8_t i = 0; i < 8; i++) { +// crc <<= 1; +// if((crc_data >> i) & 0x01) crc = crc | 1; +// } +// return crc; +// } + +// /** +// * Read bytes from rainbow table +// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX +// * @param num_parcel parcel number 0..15 +// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down +// */ +// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { +// uint8_t k = 0; +// uint8_t crc = 0; +// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); +// if(num_parcel < 4) { +// k = 0x8f; +// } else { +// k = 0x80; +// } + +// if(!hold_bit) { +// hold_bit = 0; +// } else { +// hold_bit = 0x10; +// } +// k = num_parcel ^ k; +// p[7] = k; +// p[8] = hold_bit ^ (k << 4); + +// crc = subghz_protocol_nice_one_crc(p); + +// p[8] |= crc >> 4; +// p[9] = crc << 4; +// } + +/** + * Read bytes from rainbow table + * @param file_name Full path to rainbow table the file + * @param address Byte address in file + * @return data + */ +static uint8_t + subghz_protocol_nice_flor_s_get_byte_in_file(const char* file_name, uint32_t address) { + if(!file_name) return 0; + + uint8_t buffer[1] = {0}; + if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint8_t))) { + return buffer[0]; + } else { + return 0; + } +} + +static inline void subghz_protocol_decoder_nice_flor_s_magic_xor(uint8_t* p, uint8_t k) { + for(uint8_t i = 1; i < 6; i++) { + p[i] ^= k; + } +} + +uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + + uint8_t k = 0; + for(uint8_t y = 0; y < 2; y++) { + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0xe0; + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0x7; + if(y == 0) { + k = p[0]; + p[0] = p[1]; + p[1] = k; + } + } + + p[5] = ~p[5] & 0x0f; + k = ~p[4]; + p[4] = ~p[0]; + p[0] = ~p[2]; + p[2] = k; + k = ~p[3]; + p[3] = ~p[1]; + p[1] = k; + + return data; +} + +static uint64_t + subghz_protocol_nice_flor_s_decrypt(SubGhzBlockGeneric* instance, const char* file_name) { + furi_assert(instance); + uint64_t data = instance->data; + uint8_t* p = (uint8_t*)&data; + + uint8_t k = 0; + + k = ~p[4]; + p[5] = ~p[5]; + p[4] = ~p[2]; + p[2] = ~p[0]; + p[0] = k; + k = ~p[3]; + p[3] = ~p[1]; + p[1] = k; + + for(uint8_t y = 0; y < 2; y++) { + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0x7; + k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); + subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); + + p[5] &= 0x0f; + p[0] ^= k & 0xe0; + + if(y == 0) { + k = p[0]; + p[0] = p[1]; + p[1] = k; + } + } + + return data; +} + +bool subghz_protocol_nice_flor_s_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderNiceFlorS* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 52; + uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; + uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt( + decrypt, instance->nice_flor_s_rainbow_table_file_name); + uint8_t byte = btn << 4 | (0xF ^ btn ^ 0x3); + instance->generic.data = (uint64_t)byte << 44 | enc_part; + + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + return res; +} + +void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlorS)); + instance->base.protocol = &subghz_protocol_nice_flor_s; + instance->generic.protocol_name = instance->base.protocol->name; + instance->nice_flor_s_rainbow_table_file_name = + subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); + if(instance->nice_flor_s_rainbow_table_file_name) { + FURI_LOG_D( + TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); + } + return instance; +} + +void subghz_protocol_decoder_nice_flor_s_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + instance->nice_flor_s_rainbow_table_file_name = NULL; + free(instance); +} + +void subghz_protocol_decoder_nice_flor_s_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + instance->decoder.parser_step = NiceFlorSDecoderStepReset; +} + +void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + + switch(instance->decoder.parser_step) { + case NiceFlorSDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 38) < + subghz_protocol_nice_flor_s_const.te_delta * 38)) { + //Found start header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepCheckHeader; + } + break; + case NiceFlorSDecoderStepCheckHeader: + if((level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta * 3)) { + //Found next header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepFoundHeader; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + break; + case NiceFlorSDecoderStepFoundHeader: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta * 3)) { + //Found header Nice Flor-S + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + break; + case NiceFlorSDecoderStepSaveDuration: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < + subghz_protocol_nice_flor_s_const.te_delta) { + //Found STOP bit + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + if((instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || + (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { + instance->generic.data = instance->data; + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = instance->generic.data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + break; + } else { + //save interval + instance->decoder.te_last = duration; + instance->decoder.parser_step = NiceFlorSDecoderStepCheckDuration; + } + } + break; + case NiceFlorSDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_short) < + subghz_protocol_nice_flor_s_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_long) < + subghz_protocol_nice_flor_s_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_long) < + subghz_protocol_nice_flor_s_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short) < + subghz_protocol_nice_flor_s_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; + } else + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } else { + instance->decoder.parser_step = NiceFlorSDecoderStepReset; + } + if(instance->decoder.decode_count_bit == + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { + instance->data = instance->decoder.decode_data; + instance->decoder.decode_data = 0; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param file_name Full path to rainbow table the file + */ +static void subghz_protocol_nice_flor_s_remote_controller( + SubGhzBlockGeneric* instance, + const char* file_name) { + /* + * Protocol Nice Flor-S + * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP + * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; + * P1 (4-bit) - batch repetition number, calculated by the formula: + * P1 = 0xF ^ P0 ^ n; where n changes from 1 to 15, then 0, and then in a circle + * key 1: {0xE,0xF,0xC,0xD,0xA,0xB,0x8,0x9,0x6,0x7,0x4,0x5,0x2,0x3,0x0,0x1}; + * key 2: {0xD,0xC,0xF,0xE,0x9,0x8,0xB,0xA,0x5,0x4,0x7,0x6,0x1,0x0,0x3,0x2}; + * key 3: {0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC,0x3,0x2,0x1,0x0,0x7,0x6,0x5,0x4}; + * key 4: {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8}; + * P2 (4-bit) - part of the serial number, P2 = (K ^ S3) & 0xF; + * P3 (byte) - the major part of the encrypted index + * P4 (byte) - the low-order part of the encrypted index + * P5 (byte) - part of the serial number, P5 = K ^ S2; + * P6 (byte) - part of the serial number, P6 = K ^ S1; + * P7 (byte) - part of the serial number, P7 = K ^ S0; + * K (byte) - depends on P3 and P4, K = Fk(P3, P4); + * S3,S2,S1,S0 - serial number of the console 28 bit. + * + * data => 0x1c5783607f7b3 key serial cnt + * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 + * + * Protocol Nice One + * Generally repeats the Nice Flor-S protocol, but there are a few changes + * Packet format first 52 bytes repeat Nice Flor-S protocol + * The additional 20 bytes contain the code of the pressed button, + * the button hold bit and the CRC of the entire message. + * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP + * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; + * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc + * P10 (4-bit) - 4 lo bit crc + * key+b crc + * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button + * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold + * + * A small button hold counter (0..15) is stored between each press, + * i.e. if 1 press of the button stops counter 6, then the next press + * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... + * further up to 15 with overflow + * + */ + if(!file_name) { + instance->cnt = 0; + instance->serial = 0; + instance->btn = 0; + } else { + uint64_t decrypt = subghz_protocol_nice_flor_s_decrypt(instance, file_name); + instance->cnt = decrypt & 0xFFFF; + instance->serial = (decrypt >> 16) & 0xFFFFFFF; + instance->btn = (decrypt >> 48) & 0xF; + } +} + +uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_nice_flor_s_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(res && + !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + res = false; + } + } + return res; +} + +bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if((instance->generic.data_count_bit != + subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && + (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = 0; + if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + instance->data = (uint64_t)temp; + } + + ret = true; + } while(false); + return ret; +} + +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderNiceFlorS* instance = context; + + subghz_protocol_nice_flor_s_remote_controller( + &instance->generic, instance->nice_flor_s_rainbow_table_file_name); + + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX%llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + NICE_ONE_NAME, + instance->generic.data_count_bit, + instance->generic.data, + instance->data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%013llX\r\n" + "Sn:%05lX\r\n" + "Cnt:%04lX Btn:%02X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } +} diff --git a/applications/main/subghz/protocols/nice_flor_s.h b/applications/main/subghz/protocols/nice_flor_s.h new file mode 100644 index 000000000..e333fc979 --- /dev/null +++ b/applications/main/subghz/protocols/nice_flor_s.h @@ -0,0 +1,127 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME "Nice FloR-S" + +typedef struct SubGhzProtocolDecoderNiceFlorS SubGhzProtocolDecoderNiceFlorS; +typedef struct SubGhzProtocolEncoderNiceFlorS SubGhzProtocolEncoderNiceFlorS; + +extern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder; +extern const SubGhzProtocol subghz_protocol_nice_flor_s; + +/** + * Allocate SubGhzProtocolEncoderNiceFlorS. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderNiceFlorS* pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void subghz_protocol_encoder_nice_flor_s_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + */ +void subghz_protocol_encoder_nice_flor_s_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context); + +uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name); + +/** + * New remote generation. + * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number + * @param btn Button number, 4 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_nice_flor_s_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Allocate SubGhzProtocolDecoderNiceFlorS. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderNiceFlorS* pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void subghz_protocol_decoder_nice_flor_s_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + */ +void subghz_protocol_decoder_nice_flor_s_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_nice_flor_s_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderNiceFlorS. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance + * @param output Resulting text + */ +void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/phoenix_v2.c b/applications/main/subghz/protocols/phoenix_v2.c new file mode 100644 index 000000000..b3d6f1e98 --- /dev/null +++ b/applications/main/subghz/protocols/phoenix_v2.c @@ -0,0 +1,339 @@ +#include "phoenix_v2.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolPhoenix_V2" + +//transmission only static mode + +static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = { + .te_short = 427, + .te_long = 853, + .te_delta = 100, + .min_count_bit_for_found = 52, +}; + +struct SubGhzProtocolDecoderPhoenix_V2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderPhoenix_V2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + Phoenix_V2DecoderStepReset = 0, + Phoenix_V2DecoderStepFoundStartBit, + Phoenix_V2DecoderStepSaveDuration, + Phoenix_V2DecoderStepCheckDuration, +} Phoenix_V2DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = { + .alloc = subghz_protocol_decoder_phoenix_v2_alloc, + .free = subghz_protocol_decoder_phoenix_v2_free, + + .feed = subghz_protocol_decoder_phoenix_v2_feed, + .reset = subghz_protocol_decoder_phoenix_v2_reset, + + .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data, + .serialize = subghz_protocol_decoder_phoenix_v2_serialize, + .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize, + .get_string = subghz_protocol_decoder_phoenix_v2_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = { + .alloc = subghz_protocol_encoder_phoenix_v2_alloc, + .free = subghz_protocol_encoder_phoenix_v2_free, + + .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize, + .stop = subghz_protocol_encoder_phoenix_v2_stop, + .yield = subghz_protocol_encoder_phoenix_v2_yield, +}; + +const SubGhzProtocol subghz_protocol_phoenix_v2 = { + .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_phoenix_v2_decoder, + .encoder = &subghz_protocol_phoenix_v2_encoder, +}; + +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2)); + + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) { + furi_assert(instance); + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + //Send header + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60); + //Send start bit + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6); + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); + } + } + return true; +} + +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* 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_phoenix_v2_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); + + if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_phoenix_v2_stop(void* context) { + SubGhzProtocolEncoderPhoenix_V2* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) { + SubGhzProtocolEncoderPhoenix_V2* 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_phoenix_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2)); + instance->base.protocol = &subghz_protocol_phoenix_v2; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_phoenix_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + free(instance); +} + +void subghz_protocol_decoder_phoenix_v2_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; +} + +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + + switch(instance->decoder.parser_step) { + case Phoenix_V2DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) < + subghz_protocol_phoenix_v2_const.te_delta * 30)) { + //Found Preambula + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + } + break; + case Phoenix_V2DecoderStepFoundStartBit: + if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) < + subghz_protocol_phoenix_v2_const.te_delta * 4))) { + //Found start bit + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + case Phoenix_V2DecoderStepSaveDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 + + subghz_protocol_phoenix_v2_const.te_delta)) { + instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; + if(instance->decoder.decode_count_bit == + subghz_protocol_phoenix_v2_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration; + } + } + break; + case Phoenix_V2DecoderStepCheckDuration: + if(level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) < + subghz_protocol_phoenix_v2_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) < + subghz_protocol_phoenix_v2_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + } else { + instance->decoder.parser_step = Phoenix_V2DecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) { + uint64_t data_rev = + subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4); + instance->serial = data_rev & 0xFFFFFFFF; + instance->cnt = (data_rev >> 40) & 0xFFFF; + instance->btn = (data_rev >> 32) & 0xF; +} + +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_phoenix_v2_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_phoenix_v2_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPhoenix_V2* instance = context; + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%02lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%X\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + instance->generic.btn); +} diff --git a/applications/main/subghz/protocols/phoenix_v2.h b/applications/main/subghz/protocols/phoenix_v2.h new file mode 100644 index 000000000..48487535e --- /dev/null +++ b/applications/main/subghz/protocols/phoenix_v2.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2" + +typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2; +typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2; + +extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder; +extern const SubGhzProtocol subghz_protocol_phoenix_v2; + +/** + * Allocate SubGhzProtocolEncoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + */ +void subghz_protocol_encoder_phoenix_v2_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPhoenix_V2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + */ +void subghz_protocol_decoder_phoenix_v2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPhoenix_V2. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/power_smart.c b/applications/main/subghz/protocols/power_smart.c new file mode 100644 index 000000000..1e8d10e95 --- /dev/null +++ b/applications/main/subghz/protocols/power_smart.c @@ -0,0 +1,394 @@ +#include "power_smart.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolPowerSmart" +#define POWER_SMART_PACKET_HEADER 0xFD000000AA000000 +#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000 + +#define CHANNEL_PATTERN "%c%c%c%c%c%c" +#define CNT_TO_CHANNEL(dip) \ + (dip & 0x0001 ? '*' : '-'), (dip & 0x0002 ? '*' : '-'), (dip & 0x0004 ? '*' : '-'), \ + (dip & 0x0008 ? '*' : '-'), (dip & 0x0010 ? '*' : '-'), (dip & 0x0020 ? '*' : '-') + +static const SubGhzBlockConst subghz_protocol_power_smart_const = { + .te_short = 225, + .te_long = 450, + .te_delta = 100, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderPowerSmart { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + ManchesterState manchester_saved_state; + uint16_t header_count; +}; + +struct SubGhzProtocolEncoderPowerSmart { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + PowerSmartDecoderStepReset = 0, + PowerSmartDecoderFoundHeader, + PowerSmartDecoderStepDecoderData, +} PowerSmartDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder = { + .alloc = subghz_protocol_decoder_power_smart_alloc, + .free = subghz_protocol_decoder_power_smart_free, + + .feed = subghz_protocol_decoder_power_smart_feed, + .reset = subghz_protocol_decoder_power_smart_reset, + + .get_hash_data = subghz_protocol_decoder_power_smart_get_hash_data, + .serialize = subghz_protocol_decoder_power_smart_serialize, + .deserialize = subghz_protocol_decoder_power_smart_deserialize, + .get_string = subghz_protocol_decoder_power_smart_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder = { + .alloc = subghz_protocol_encoder_power_smart_alloc, + .free = subghz_protocol_encoder_power_smart_free, + + .deserialize = subghz_protocol_encoder_power_smart_deserialize, + .stop = subghz_protocol_encoder_power_smart_stop, + .yield = subghz_protocol_encoder_power_smart_yield, +}; + +const SubGhzProtocol subghz_protocol_power_smart = { + .name = SUBGHZ_PROTOCOL_POWER_SMART_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_power_smart_decoder, + .encoder = &subghz_protocol_power_smart_encoder, +}; + +void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolEncoderPowerSmart)); + + instance->base.protocol = &subghz_protocol_power_smart; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 1024; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_power_smart_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPowerSmart* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static LevelDuration + subghz_protocol_encoder_power_smart_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_power_smart_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_power_smart_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_power_smart_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_power_smart_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +static void + subghz_protocol_encoder_power_smart_get_upload(SubGhzProtocolEncoderPowerSmart* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + for(int i = 8; i > 0; i--) { + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_power_smart_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, !bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_power_smart_add_duration_to_upload(result); + } + } + instance->encoder.upload[index] = subghz_protocol_encoder_power_smart_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_power_smart_const.te_long * 1111); + instance->encoder.size_upload = index; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* instance) { + /* + * Protocol: Manchester encoding, symbol rate ~2222. + * Packet Format: + * 0xFDXXXXYYAAZZZZWW where 0xFD and 0xAA sync word + * XXXX = ~ZZZZ, YY=(~WW)-1 + * Example: + * SYNC1 K1 CHANNEL DATA1 K2 DATA2 SYNC2 ~K1 ~CHANNEL ~DATA2 ~K2 (~DATA2)-1 + * 0xFD2137ACAADEC852 => 11111101 0 010000 10011011 1 10101100 10101010 1 1011110 1100100 0 01010010 + * 0xFDA137ACAA5EC852 => 11111101 1 010000 10011011 1 10101100 10101010 0 1011110 1100100 0 01010010 + * 0xFDA136ACAA5EC952 => 11111101 1 010000 10011011 0 10101100 10101010 0 1011110 1100100 1 01010010 + * + * Key: + * K1K2 + * 0 0 - key_unknown + * 0 1 - key_down + * 1 0 - key_up + * 1 1 - key_stop + * + */ + + instance->btn = ((instance->data >> 54) & 0x02) | ((instance->data >> 40) & 0x1); + instance->serial = ((instance->data >> 33) & 0x3FFF00) | ((instance->data >> 32) & 0xFF); + instance->cnt = ((instance->data >> 49) & 0x3F); +} + +bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPowerSmart* 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_power_smart_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_power_smart_remote_controller(&instance->generic); + subghz_protocol_encoder_power_smart_get_upload(instance); + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_power_smart_stop(void* context) { + SubGhzProtocolEncoderPowerSmart* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_power_smart_yield(void* context) { + SubGhzProtocolEncoderPowerSmart* 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_power_smart_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolDecoderPowerSmart)); + instance->base.protocol = &subghz_protocol_power_smart; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_power_smart_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + free(instance); +} + +void subghz_protocol_decoder_power_smart_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +bool subghz_protocol_power_smart_chek_valid(uint64_t packet) { + uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF); + uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF); + uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF; + uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1); + return (data_1 == data_2) && (data_3 == data_4); +} + +void subghz_protocol_decoder_power_smart_feed( + void* context, + bool level, + volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + ManchesterEvent event = ManchesterEventReset; + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < + subghz_protocol_power_smart_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < + subghz_protocol_power_smart_const.te_delta * 2) { + event = ManchesterEventLongLow; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < + subghz_protocol_power_smart_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < + subghz_protocol_power_smart_const.te_delta * 2) { + event = ManchesterEventLongHigh; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; + } + if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == + POWER_SMART_PACKET_HEADER) { + if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = + subghz_protocol_power_smart_const.min_count_bit_for_found; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } + } + } else { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + } +} + +static const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) { + btn &= 0x3; + const char* name_btn[0x4] = {"Unknown", "Down", "Up", "Stop"}; + return name_btn[btn]; +} + +uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_power_smart_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_power_smart_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_power_smart_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPowerSmart* instance = context; + subghz_protocol_power_smart_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%07lX \r\n" + "Btn:%s\r\n" + "Channel:" CHANNEL_PATTERN "\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)(instance->generic.data & 0xFFFFFFFF), + instance->generic.serial, + subghz_protocol_power_smart_get_name_button(instance->generic.btn), + CNT_TO_CHANNEL(instance->generic.cnt)); +} diff --git a/applications/main/subghz/protocols/power_smart.h b/applications/main/subghz/protocols/power_smart.h new file mode 100644 index 000000000..806729f8e --- /dev/null +++ b/applications/main/subghz/protocols/power_smart.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_POWER_SMART_NAME "Power Smart" + +typedef struct SubGhzProtocolDecoderPowerSmart SubGhzProtocolDecoderPowerSmart; +typedef struct SubGhzProtocolEncoderPowerSmart SubGhzProtocolEncoderPowerSmart; + +extern const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder; +extern const SubGhzProtocol subghz_protocol_power_smart; + +/** + * Allocate SubGhzProtocolEncoderPowerSmart. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPowerSmart* pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPowerSmart. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void subghz_protocol_encoder_power_smart_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + */ +void subghz_protocol_encoder_power_smart_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_power_smart_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPowerSmart. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPowerSmart* pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void subghz_protocol_decoder_power_smart_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + */ +void subghz_protocol_decoder_power_smart_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_power_smart_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPowerSmart. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance + * @param output Resulting text + */ +void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/princeton.c b/applications/main/subghz/protocols/princeton.c new file mode 100644 index 000000000..7fc8f6524 --- /dev/null +++ b/applications/main/subghz/protocols/princeton.c @@ -0,0 +1,374 @@ +#include "princeton.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define TAG "SubGhzProtocolPrinceton" + +static const SubGhzBlockConst subghz_protocol_princeton_const = { + .te_short = 390, + .te_long = 1170, + .te_delta = 300, + .min_count_bit_for_found = 24, +}; + +struct SubGhzProtocolDecoderPrinceton { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderPrinceton { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + PrincetonDecoderStepReset = 0, + PrincetonDecoderStepSaveDuration, + PrincetonDecoderStepCheckDuration, +} PrincetonDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_princeton_decoder = { + .alloc = subghz_protocol_decoder_princeton_alloc, + .free = subghz_protocol_decoder_princeton_free, + + .feed = subghz_protocol_decoder_princeton_feed, + .reset = subghz_protocol_decoder_princeton_reset, + + .get_hash_data = subghz_protocol_decoder_princeton_get_hash_data, + .serialize = subghz_protocol_decoder_princeton_serialize, + .deserialize = subghz_protocol_decoder_princeton_deserialize, + .get_string = subghz_protocol_decoder_princeton_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_princeton_encoder = { + .alloc = subghz_protocol_encoder_princeton_alloc, + .free = subghz_protocol_encoder_princeton_free, + + .deserialize = subghz_protocol_encoder_princeton_deserialize, + .stop = subghz_protocol_encoder_princeton_stop, + .yield = subghz_protocol_encoder_princeton_yield, +}; + +const SubGhzProtocol subghz_protocol_princeton = { + .name = SUBGHZ_PROTOCOL_PRINCETON_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_princeton_decoder, + .encoder = &subghz_protocol_princeton_encoder, +}; + +void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderPrinceton* instance = malloc(sizeof(SubGhzProtocolEncoderPrinceton)); + + instance->base.protocol = &subghz_protocol_princeton; + 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_princeton_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderPrinceton* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance + * @return true On success + */ +static bool + subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 30); + + return true; +} + +bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderPrinceton* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_princeton_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); + + if(!subghz_protocol_encoder_princeton_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_princeton_stop(void* context) { + SubGhzProtocolEncoderPrinceton* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_princeton_yield(void* context) { + SubGhzProtocolEncoderPrinceton* 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_princeton_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderPrinceton* instance = malloc(sizeof(SubGhzProtocolDecoderPrinceton)); + instance->base.protocol = &subghz_protocol_princeton; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_princeton_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + free(instance); +} + +void subghz_protocol_decoder_princeton_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + instance->decoder.parser_step = PrincetonDecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + + switch(instance->decoder.parser_step) { + case PrincetonDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short * 36) < + subghz_protocol_princeton_const.te_delta * 36)) { + //Found Preambula + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case PrincetonDecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = PrincetonDecoderStepCheckDuration; + } + break; + case PrincetonDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_princeton_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + 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->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_short) < + subghz_protocol_princeton_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_long) < + subghz_protocol_princeton_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_long) < + subghz_protocol_princeton_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short) < + subghz_protocol_princeton_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = PrincetonDecoderStepReset; + } + } else { + instance->decoder.parser_step = PrincetonDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) { + instance->serial = instance->data >> 4; + instance->btn = instance->data & 0xF; +} + +uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_princeton_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* 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_princeton_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderPrinceton* instance = context; + subghz_protocol_princeton_check_remote_controller(&instance->generic); + uint32_t data_rev = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%08lX\r\n" + "Yek:0x%08lX\r\n" + "Sn:0x%05lX Btn:%01X\r\n" + "Te:%luus\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFF), + data_rev, + instance->generic.serial, + instance->generic.btn, + instance->te); +} diff --git a/applications/main/subghz/protocols/princeton.h b/applications/main/subghz/protocols/princeton.h new file mode 100644 index 000000000..a2a11292e --- /dev/null +++ b/applications/main/subghz/protocols/princeton.h @@ -0,0 +1,115 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton; +typedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton; + +extern const SubGhzProtocolDecoder subghz_protocol_princeton_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_princeton_encoder; +extern const SubGhzProtocol subghz_protocol_princeton; + +/** + * Allocate SubGhzProtocolEncoderPrinceton. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderPrinceton* pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderPrinceton. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void subghz_protocol_encoder_princeton_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + */ +void subghz_protocol_encoder_princeton_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_princeton_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderPrinceton. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderPrinceton* pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void subghz_protocol_decoder_princeton_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + */ +void subghz_protocol_decoder_princeton_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_princeton_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderPrinceton. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance + * @param output Resulting text + */ +void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/princeton_for_testing.c b/applications/main/subghz/protocols/princeton_for_testing.c new file mode 100644 index 000000000..478d14cdf --- /dev/null +++ b/applications/main/subghz/protocols/princeton_for_testing.c @@ -0,0 +1,288 @@ +#include "princeton_for_testing.h" + +#include +#include "../blocks/math.h" + +/* + * Help + * https://phreakerclub.com/447 + * + */ + +#define SUBGHZ_PT_SHORT 300 +#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3) +#define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30) +#define SUBGHZ_PT_COUNT_KEY_433 9 +#define SUBGHZ_PT_TIMEOUT_433 900 +#define SUBGHZ_PT_COUNT_KEY_868 9 +#define SUBGHZ_PT_TIMEOUT_868 14000 + +#define TAG "SubGhzProtocolPrinceton" + +struct SubGhzEncoderPrinceton { + uint32_t key; + uint16_t te; + size_t repeat; + size_t front; + size_t count_key; + size_t count_key_package; + uint32_t time_high; + uint32_t time_low; + uint32_t timeout; + uint32_t time_stop; +}; + +typedef enum { + PrincetonDecoderStepReset = 0, + PrincetonDecoderStepSaveDuration, + PrincetonDecoderStepCheckDuration, +} PrincetonDecoderStep; + +SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc() { + SubGhzEncoderPrinceton* instance = malloc(sizeof(SubGhzEncoderPrinceton)); + return instance; +} + +void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_encoder_princeton_for_testing_stop( + SubGhzEncoderPrinceton* instance, + uint32_t time_stop) { + instance->time_stop = time_stop; +} + +void subghz_encoder_princeton_for_testing_set( + SubGhzEncoderPrinceton* instance, + uint32_t key, + size_t repeat, + uint32_t frequency) { + furi_assert(instance); + instance->te = SUBGHZ_PT_SHORT; + instance->key = key; + instance->repeat = repeat + 1; + instance->front = 48; + instance->time_high = 0; + instance->time_low = 0; + if(frequency < 700000000) { + instance->count_key_package = SUBGHZ_PT_COUNT_KEY_433; + instance->timeout = SUBGHZ_PT_TIMEOUT_433; + } else { + instance->count_key_package = SUBGHZ_PT_COUNT_KEY_868; + instance->timeout = SUBGHZ_PT_TIMEOUT_868; + } + + instance->count_key = instance->count_key_package + 3; + + if((furi_get_tick() - instance->time_stop) < instance->timeout) { + instance->time_stop = (instance->timeout - (furi_get_tick() - instance->time_stop)) * 1000; + } else { + instance->time_stop = 0; + } +} + +size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance) { + furi_assert(instance); + return instance->repeat; +} + +void subghz_encoder_princeton_for_testing_print_log(void* context) { + SubGhzEncoderPrinceton* instance = context; + float duty_cycle = + ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; + FURI_LOG_I( + TAG "Encoder", + "Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%", + instance->time_high + instance->time_low, + instance->time_high, + instance->time_low, + (uint32_t)duty_cycle, + (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL)); +} + +LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) { + SubGhzEncoderPrinceton* instance = context; + if(instance->repeat == 0) { + subghz_encoder_princeton_for_testing_print_log(instance); + return level_duration_reset(); + } + + size_t bit = instance->front / 2; + bool level = !(instance->front % 2); + + LevelDuration ret; + if(bit < 24) { + uint8_t byte = bit / 8; + uint8_t bit_in_byte = bit % 8; + bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1; + if(value) { + ret = level_duration_make(level, level ? instance->te * 3 : instance->te); + if(level) + instance->time_high += instance->te * 3; + else + instance->time_low += instance->te; + } else { + ret = level_duration_make(level, level ? instance->te : instance->te * 3); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 3; + } + } else { + if(instance->time_stop) { + ret = level_duration_make(level, level ? instance->te : instance->time_stop); + if(level) + instance->time_high += instance->te; + else { + instance->time_low += instance->time_stop; + instance->time_stop = 0; + instance->front = 47; + } + } else { + if(--instance->count_key != 0) { + ret = level_duration_make(level, level ? instance->te : instance->te * 30); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->te * 30; + } else { + instance->count_key = instance->count_key_package + 2; + instance->front = 48; + ret = level_duration_make(level, level ? instance->te : instance->timeout * 1000); + if(level) + instance->time_high += instance->te; + else + instance->time_low += instance->timeout * 1000; + } + } + } + + instance->front++; + if(instance->front == 50) { + instance->repeat--; + instance->front = 0; + } + return ret; +} + +struct SubGhzDecoderPrinceton { + const char* name; + uint16_t te_long; + uint16_t te_short; + uint16_t te_delta; + uint8_t code_count_bit; + uint8_t code_last_count_bit; + uint64_t code_found; + uint64_t code_last_found; + uint8_t code_min_count_bit_for_found; + uint8_t btn; + uint32_t te_last; + uint32_t serial; + uint32_t parser_step; + uint16_t cnt; + uint32_t te; + + SubGhzDecoderPrincetonCallback callback; + void* context; +}; + +SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void) { + SubGhzDecoderPrinceton* instance = malloc(sizeof(SubGhzDecoderPrinceton)); + + instance->te = SUBGHZ_PT_SHORT; + instance->name = "Princeton"; + instance->code_min_count_bit_for_found = 24; + instance->te_short = 400; + instance->te_long = 1200; + instance->te_delta = 250; + return instance; +} + +void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance) { + furi_assert(instance); + free(instance); +} + +void subghz_decoder_princeton_for_testing_set_callback( + SubGhzDecoderPrinceton* instance, + SubGhzDecoderPrincetonCallback callback, + void* context) { + instance->callback = callback; + instance->context = context; +} + +void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance) { + instance->parser_step = PrincetonDecoderStepReset; +} + +static void + subghz_decoder_princeton_for_testing_add_bit(SubGhzDecoderPrinceton* instance, uint8_t bit) { + instance->code_found = instance->code_found << 1 | bit; + instance->code_count_bit++; +} + +void subghz_decoder_princeton_for_testing_parse( + SubGhzDecoderPrinceton* instance, + bool level, + uint32_t duration) { + switch(instance->parser_step) { + case PrincetonDecoderStepReset: + if((!level) && + (DURATION_DIFF(duration, instance->te_short * 36) < instance->te_delta * 36)) { + //Found Preambula + instance->parser_step = PrincetonDecoderStepSaveDuration; + instance->code_found = 0; + instance->code_count_bit = 0; + instance->te = 0; + } + break; + case PrincetonDecoderStepSaveDuration: + //save duration + if(level) { + instance->te_last = duration; + instance->te += duration; + instance->parser_step = PrincetonDecoderStepCheckDuration; + } + break; + case PrincetonDecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)instance->te_short * 10 + instance->te_delta)) { + instance->parser_step = PrincetonDecoderStepSaveDuration; + if(instance->code_count_bit == instance->code_min_count_bit_for_found) { + instance->te /= (instance->code_count_bit * 4 + 1); + + instance->code_last_found = instance->code_found; + instance->code_last_count_bit = instance->code_count_bit; + instance->serial = instance->code_found >> 4; + instance->btn = (uint8_t)instance->code_found & 0x00000F; + + if(instance->callback) instance->callback(instance, instance->context); + } + instance->code_found = 0; + instance->code_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->te_last, instance->te_short) < instance->te_delta) && + (DURATION_DIFF(duration, instance->te_long) < instance->te_delta * 3)) { + subghz_decoder_princeton_for_testing_add_bit(instance, 0); + instance->parser_step = PrincetonDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->te_last, instance->te_long) < instance->te_delta * 3) && + (DURATION_DIFF(duration, instance->te_short) < instance->te_delta)) { + subghz_decoder_princeton_for_testing_add_bit(instance, 1); + instance->parser_step = PrincetonDecoderStepSaveDuration; + } else { + instance->parser_step = PrincetonDecoderStepReset; + } + } else { + instance->parser_step = PrincetonDecoderStepReset; + } + break; + } +} diff --git a/applications/main/subghz/protocols/princeton_for_testing.h b/applications/main/subghz/protocols/princeton_for_testing.h new file mode 100644 index 000000000..07a37ec5f --- /dev/null +++ b/applications/main/subghz/protocols/princeton_for_testing.h @@ -0,0 +1,97 @@ +#pragma once + +#include "base.h" + +/** SubGhzDecoderPrinceton anonymous type */ +typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton; +/** SubGhzEncoderPrinceton anonymous type */ +typedef struct SubGhzEncoderPrinceton SubGhzEncoderPrinceton; + +typedef void (*SubGhzDecoderPrincetonCallback)(SubGhzDecoderPrinceton* parser, void* context); + +/** + * Allocate SubGhzEncoderPrinceton + * @return pointer to SubGhzEncoderPrinceton instance + */ +SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc(); + +/** + * Free SubGhzEncoderPrinceton instance + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance); + +/** + * Forced transmission stop. + * @param instance Pointer to a SubGhzEncoderPrinceton instance + * @param time_stop Transmission stop time, ms + */ +void subghz_encoder_princeton_for_testing_stop( + SubGhzEncoderPrinceton* instance, + uint32_t time_stop); + +/** + * Set new encoder params + * @param instance - SubGhzEncoderPrinceton instance + * @param key - 24bit key + * @param repeat - how many times to repeat + * @param frequency - frequency + */ +void subghz_encoder_princeton_for_testing_set( + SubGhzEncoderPrinceton* instance, + uint32_t key, + size_t repeat, + uint32_t frequency); + +/** + * Get repeat count left + * @param instance - SubGhzEncoderPrinceton instance + * @return repeat count left + */ +size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance); + +/** + * Print encoder log + * @param instance - SubGhzEncoderPrinceton instance + */ +void subghz_encoder_princeton_for_testing_print_log(void* context); + +/** + * Get level duration + * @param instance - SubGhzEncoderPrinceton instance + * @return level duration + */ +LevelDuration subghz_encoder_princeton_for_testing_yield(void* context); + +/** + * Allocate SubGhzDecoderPrinceton + * @return SubGhzDecoderPrinceton* + */ +SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(); + +/** + * Free SubGhzDecoderPrinceton + * @param instance + */ +void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance); + +void subghz_decoder_princeton_for_testing_set_callback( + SubGhzDecoderPrinceton* instance, + SubGhzDecoderPrincetonCallback callback, + void* context); + +/** + * Reset internal state + * @param instance - SubGhzDecoderPrinceton instance + */ +void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance); + +/** + * Parse accepted duration + * @param instance - SubGhzDecoderPrinceton instance + * @param data - LevelDuration level_duration + */ +void subghz_decoder_princeton_for_testing_parse( + SubGhzDecoderPrinceton* instance, + bool level, + uint32_t duration); diff --git a/applications/main/subghz/protocols/protocol_items.c b/applications/main/subghz/protocols/protocol_items.c new file mode 100644 index 000000000..74244c5ff --- /dev/null +++ b/applications/main/subghz/protocols/protocol_items.c @@ -0,0 +1,50 @@ +#include "protocol_items.h" + +const SubGhzProtocol* subghz_protocol_registry_items[] = { + &subghz_protocol_gate_tx, + &subghz_protocol_keeloq, + &subghz_protocol_star_line, + &subghz_protocol_nice_flo, + &subghz_protocol_came, + &subghz_protocol_faac_slh, + &subghz_protocol_nice_flor_s, + &subghz_protocol_came_twee, + &subghz_protocol_came_atomo, + &subghz_protocol_nero_sketch, + &subghz_protocol_ido, + &subghz_protocol_kia, + &subghz_protocol_hormann, + &subghz_protocol_nero_radio, + &subghz_protocol_somfy_telis, + &subghz_protocol_somfy_keytis, + &subghz_protocol_scher_khan, + &subghz_protocol_princeton, + &subghz_protocol_raw, + &subghz_protocol_linear, + &subghz_protocol_secplus_v2, + &subghz_protocol_secplus_v1, + &subghz_protocol_megacode, + &subghz_protocol_holtek, + &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_magellan, + &subghz_protocol_intertechno_v3, + &subghz_protocol_clemsa, + &subghz_protocol_ansonic, + &subghz_protocol_smc5326, + &subghz_protocol_holtek_th12x, + &subghz_protocol_linear_delta3, + &subghz_protocol_dooya, + &subghz_protocol_alutech_at_4n, + &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, +}; + +const SubGhzProtocolRegistry subghz_protocol_registry = { + .items = subghz_protocol_registry_items, + .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/applications/main/subghz/protocols/protocol_items.h b/applications/main/subghz/protocols/protocol_items.h new file mode 100644 index 000000000..b7e082bf5 --- /dev/null +++ b/applications/main/subghz/protocols/protocol_items.h @@ -0,0 +1,55 @@ +#pragma once +#include "../registry.h" + +#include "princeton.h" +#include "keeloq.h" +#include "star_line.h" +#include "nice_flo.h" +#include "came.h" +#include "faac_slh.h" +#include "nice_flor_s.h" +#include "came_twee.h" +#include "came_atomo.h" +#include "nero_sketch.h" +#include "ido.h" +#include "kia.h" +#include "hormann.h" +#include "nero_radio.h" +#include "somfy_telis.h" +#include "somfy_keytis.h" +#include "scher_khan.h" +#include "gate_tx.h" +#include "raw.h" +#include "linear.h" +#include "linear_delta3.h" +#include "secplus_v2.h" +#include "secplus_v1.h" +#include "megacode.h" +#include "holtek.h" +#include "chamberlain_code.h" +#include "power_smart.h" +#include "marantec.h" +#include "bett.h" +#include "doitrand.h" +#include "phoenix_v2.h" +#include "honeywell_wdb.h" +#include "magellan.h" +#include "intertechno_v3.h" +#include "clemsa.h" +#include "ansonic.h" +#include "smc5326.h" +#include "holtek_ht12x.h" +#include "dooya.h" +#include "alutech_at_4n.h" +#include "kinggates_stylo_4k.h" +#include "bin_raw.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const SubGhzProtocolRegistry subghz_protocol_registry; + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/main/subghz/protocols/raw.c b/applications/main/subghz/protocols/raw.c new file mode 100644 index 000000000..01a229047 --- /dev/null +++ b/applications/main/subghz/protocols/raw.c @@ -0,0 +1,374 @@ +#include "raw.h" +#include +#include "../subghz_file_encoder_worker.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include +#include + +#define TAG "SubGhzProtocolRAW" +#define SUBGHZ_DOWNLOAD_MAX_SIZE 512 + +static const SubGhzBlockConst subghz_protocol_raw_const = { + .te_short = 50, + .te_long = 32700, + .te_delta = 0, + .min_count_bit_for_found = 0, +}; + +struct SubGhzProtocolDecoderRAW { + SubGhzProtocolDecoderBase base; + + int32_t* upload_raw; + uint16_t ind_write; + Storage* storage; + FlipperFormat* flipper_file; + uint32_t file_is_open; + FuriString* file_name; + size_t sample_write; + bool last_level; + bool pause; +}; + +struct SubGhzProtocolEncoderRAW { + SubGhzProtocolEncoderBase base; + + bool is_running; + FuriString* file_name; + SubGhzFileEncoderWorker* file_worker_encoder; +}; + +typedef enum { + RAWFileIsOpenClose = 0, + RAWFileIsOpenWrite, + RAWFileIsOpenRead, +} RAWFilIsOpen; + +const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { + .alloc = subghz_protocol_decoder_raw_alloc, + .free = subghz_protocol_decoder_raw_free, + + .feed = subghz_protocol_decoder_raw_feed, + .reset = subghz_protocol_decoder_raw_reset, + + .get_hash_data = NULL, + .serialize = NULL, + .deserialize = subghz_protocol_decoder_raw_deserialize, + .get_string = subghz_protocol_decoder_raw_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_raw_encoder = { + .alloc = subghz_protocol_encoder_raw_alloc, + .free = subghz_protocol_encoder_raw_free, + + .deserialize = subghz_protocol_encoder_raw_deserialize, + .stop = subghz_protocol_encoder_raw_stop, + .yield = subghz_protocol_encoder_raw_yield, +}; + +const SubGhzProtocol subghz_protocol_raw = { + .name = SUBGHZ_PROTOCOL_RAW_NAME, + .type = SubGhzProtocolTypeRAW, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_RAW | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_raw_decoder, + .encoder = &subghz_protocol_raw_encoder, +}; + +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolDecoderRAW* instance, + const char* dev_name, + SubGhzRadioPreset* preset) { + furi_assert(instance); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->flipper_file = flipper_format_file_alloc(instance->storage); + + FuriString* temp_str; + temp_str = furi_string_alloc(); + bool init = false; + + do { + // Create subghz folder directory if necessary + if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { + break; + } + // Create saved directory if necessary + if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { + break; + } + + furi_string_set(instance->file_name, dev_name); + // First remove subghz device file if it was saved + furi_string_printf(temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_EXTENSION); + + if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) { + break; + } + + // Open file + if(!flipper_format_file_open_always( + instance->flipper_file, furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", furi_string_get_cstr(temp_str)); + break; + } + + if(!flipper_format_write_header_cstr( + instance->flipper_file, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + + if(!flipper_format_write_uint32( + instance->flipper_file, "Frequency", &preset->frequency, 1)) { + FURI_LOG_E(TAG, "Unable to add Frequency"); + break; + } + + subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Preset", furi_string_get_cstr(temp_str))) { + FURI_LOG_E(TAG, "Unable to add Preset"); + break; + } + if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Custom_preset_module", "CC1101")) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); + break; + } + if(!flipper_format_write_hex( + instance->flipper_file, "Custom_preset_data", preset->data, preset->data_size)) { + FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); + break; + } + } + if(!flipper_format_write_string_cstr( + instance->flipper_file, "Protocol", instance->base.protocol->name)) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); + instance->file_is_open = RAWFileIsOpenWrite; + instance->sample_write = 0; + instance->last_level = false; + instance->pause = false; + init = true; + } while(0); + + furi_string_free(temp_str); + + return init; +} + +static bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolDecoderRAW* instance) { + furi_assert(instance); + + bool is_write = false; + if(instance->file_is_open == RAWFileIsOpenWrite) { + if(!flipper_format_write_int32( + instance->flipper_file, "RAW_Data", instance->upload_raw, instance->ind_write)) { + FURI_LOG_E(TAG, "Unable to add RAW_Data"); + } else { + instance->sample_write += instance->ind_write; + instance->ind_write = 0; + is_write = true; + } + } + return is_write; +} + +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { + furi_assert(instance); + + if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write) + subghz_protocol_raw_save_to_file_write(instance); + if(instance->file_is_open != RAWFileIsOpenClose) { + free(instance->upload_raw); + instance->upload_raw = NULL; + flipper_format_file_close(instance->flipper_file); + flipper_format_free(instance->flipper_file); + furi_record_close(RECORD_STORAGE); + } + + instance->file_is_open = RAWFileIsOpenClose; +} + +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { + furi_assert(instance); + + if(instance->pause != pause) { + instance->pause = pause; + } +} + +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { + return instance->sample_write + instance->ind_write; +} + +void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderRAW* instance = malloc(sizeof(SubGhzProtocolDecoderRAW)); + instance->base.protocol = &subghz_protocol_raw; + instance->upload_raw = NULL; + instance->ind_write = 0; + instance->last_level = false; + instance->file_is_open = RAWFileIsOpenClose; + instance->file_name = furi_string_alloc(); + + return instance; +} + +void subghz_protocol_decoder_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + furi_string_free(instance->file_name); + free(instance); +} + +void subghz_protocol_decoder_raw_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + instance->ind_write = 0; + instance->last_level = false; +} + +void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderRAW* instance = context; + + if(!instance->pause && (instance->upload_raw != NULL)) { + if(duration > subghz_protocol_raw_const.te_short) { + if(instance->last_level != level) { + instance->last_level = (level ? true : false); + instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); + } + } + + if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { + subghz_protocol_raw_save_to_file_write(instance); + } + } +} + +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + UNUSED(context); + UNUSED(flipper_format); + //ToDo stub, for backwards compatibility + return true; +} + +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { + furi_assert(context); + //SubGhzProtocolDecoderRAW* instance = context; + UNUSED(context); + //ToDo no use + furi_string_cat_printf(output, "RAW Data"); +} + +void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW)); + + instance->base.protocol = &subghz_protocol_raw; + instance->file_name = furi_string_alloc(); + instance->is_running = false; + return instance; +} + +void subghz_protocol_encoder_raw_stop(void* context) { + SubGhzProtocolEncoderRAW* instance = context; + instance->is_running = false; + if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) { + subghz_file_encoder_worker_stop(instance->file_worker_encoder); + subghz_file_encoder_worker_free(instance->file_worker_encoder); + } +} + +void subghz_protocol_encoder_raw_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderRAW* instance = context; + subghz_protocol_encoder_raw_stop(instance); + furi_string_free(instance->file_name); + free(instance); +} + +void subghz_protocol_raw_file_encoder_worker_set_callback_end( + SubGhzProtocolEncoderRAW* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback_end, + void* context_end) { + furi_assert(instance); + furi_assert(callback_end); + subghz_file_encoder_worker_callback_end( + instance->file_worker_encoder, callback_end, context_end); +} + +static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* instance) { + furi_assert(instance); + + instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); + if(subghz_file_encoder_worker_start( + instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) { + //the worker needs a file in order to open and read part of the file + furi_delay_ms(100); + instance->is_running = true; + } else { + subghz_protocol_encoder_raw_stop(instance); + } + return instance->is_running; +} + +void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { + do { + stream_clean(flipper_format_get_raw_stream(flipper_format)); + if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { + FURI_LOG_E(TAG, "Unable to add Protocol"); + break; + } + + if(!flipper_format_write_string_cstr(flipper_format, "File_name", file_path)) { + FURI_LOG_E(TAG, "Unable to add File_name"); + break; + } + } while(false); +} + +bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderRAW* instance = context; + bool res = false; + FuriString* temp_str; + temp_str = furi_string_alloc(); + do { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + if(!flipper_format_read_string(flipper_format, "File_name", temp_str)) { + FURI_LOG_E(TAG, "Missing File_name"); + break; + } + furi_string_set(instance->file_name, temp_str); + + res = subghz_protocol_encoder_raw_worker_init(instance); + } while(false); + furi_string_free(temp_str); + return res; +} + +LevelDuration subghz_protocol_encoder_raw_yield(void* context) { + SubGhzProtocolEncoderRAW* instance = context; + + if(!instance->is_running) return level_duration_reset(); + return subghz_file_encoder_worker_get_level_duration(instance->file_worker_encoder); +} diff --git a/applications/main/subghz/protocols/raw.h b/applications/main/subghz/protocols/raw.h new file mode 100644 index 000000000..44c7faec5 --- /dev/null +++ b/applications/main/subghz/protocols/raw.h @@ -0,0 +1,148 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_RAW_NAME "RAW" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context); + +typedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW; +typedef struct SubGhzProtocolEncoderRAW SubGhzProtocolEncoderRAW; + +extern const SubGhzProtocolDecoder subghz_protocol_raw_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_raw_encoder; +extern const SubGhzProtocol subghz_protocol_raw; + +/** + * Open file for writing + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + * @param dev_name File name + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_raw_save_to_file_init( + SubGhzProtocolDecoderRAW* instance, + const char* dev_name, + SubGhzRadioPreset* preset); + +/** + * Stop writing file to flash + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance); + +/** + * Get the number of samples received SubGhzProtocolDecoderRAW. + * @param instance Pointer to a SubGhzProtocolDecoderRAW instance + * @return count of samples + */ +size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance); + +/** + * Allocate SubGhzProtocolDecoderRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderRAW* pointer to a SubGhzProtocolDecoderRAW instance + */ +void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_decoder_raw_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + */ +void subghz_protocol_decoder_raw_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration); + +/** + * Deserialize data SubGhzProtocolDecoderRAW. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param output Resulting text + */ +void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output); + +/** + * Allocate SubGhzProtocolEncoderRAW. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderRAW* pointer to a SubGhzProtocolEncoderRAW instance + */ +void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderRAW. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + */ +void subghz_protocol_encoder_raw_free(void* context); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + */ +void subghz_protocol_encoder_raw_stop(void* context); + +/** + * pause writing to flash. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param pause pause writing + */ +void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); + +/** + * Set callback on completion of file transfer. + * @param instance Pointer to a SubGhzProtocolEncoderRAW instance + * @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd + * @param context_end Context + */ +void subghz_protocol_raw_file_encoder_worker_set_callback_end( + SubGhzProtocolEncoderRAW* instance, + SubGhzProtocolEncoderRAWCallbackEnd callback_end, + void* context_end); + +/** + * File generation for RAW work. + * @param flipper_format Pointer to a FlipperFormat instance + * @param file_path File path + */ +void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderRAW instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_raw_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/protocols/scher_khan.c b/applications/main/subghz/protocols/scher_khan.c new file mode 100644 index 000000000..a5de7d04b --- /dev/null +++ b/applications/main/subghz/protocols/scher_khan.c @@ -0,0 +1,288 @@ +#include "scher_khan.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +//https://phreakerclub.com/72 +//https://phreakerclub.com/forum/showthread.php?t=7&page=2 +//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar +//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5 + +#define TAG "SubGhzProtocolScherKhan" + +static const SubGhzBlockConst subghz_protocol_scher_khan_const = { + .te_short = 750, + .te_long = 1100, + .te_delta = 150, + .min_count_bit_for_found = 35, +}; + +struct SubGhzProtocolDecoderScherKhan { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + const char* protocol_name; +}; + +struct SubGhzProtocolEncoderScherKhan { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + ScherKhanDecoderStepReset = 0, + ScherKhanDecoderStepCheckPreambula, + ScherKhanDecoderStepSaveDuration, + ScherKhanDecoderStepCheckDuration, +} ScherKhanDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = { + .alloc = subghz_protocol_decoder_scher_khan_alloc, + .free = subghz_protocol_decoder_scher_khan_free, + + .feed = subghz_protocol_decoder_scher_khan_feed, + .reset = subghz_protocol_decoder_scher_khan_reset, + + .get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data, + .serialize = subghz_protocol_decoder_scher_khan_serialize, + .deserialize = subghz_protocol_decoder_scher_khan_deserialize, + .get_string = subghz_protocol_decoder_scher_khan_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = { + .alloc = NULL, + .free = NULL, + + .deserialize = NULL, + .stop = NULL, + .yield = NULL, +}; + +const SubGhzProtocol subghz_protocol_scher_khan = { + .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_scher_khan_decoder, + .encoder = &subghz_protocol_scher_khan_encoder, +}; + +void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan)); + instance->base.protocol = &subghz_protocol_scher_khan; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_scher_khan_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + free(instance); +} + +void subghz_protocol_decoder_scher_khan_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + instance->decoder.parser_step = ScherKhanDecoderStepReset; +} + +void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + + switch(instance->decoder.parser_step) { + case ScherKhanDecoderStepReset: + if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta)) { + instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula; + instance->decoder.te_last = duration; + instance->header_count = 0; + } + break; + case ScherKhanDecoderStepCheckPreambula: + if(level) { + if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + instance->decoder.te_last = duration; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else if( + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) || + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + if(DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) < + subghz_protocol_scher_khan_const.te_delta) { + // Found header + instance->header_count++; + break; + } else if( + DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta) { + // Found start bit + if(instance->header_count >= 2) { + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 1; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL + + subghz_protocol_scher_khan_const.te_long)) { + //Found stop bit + instance->decoder.parser_step = ScherKhanDecoderStepReset; + if(instance->decoder.decode_count_bit >= + subghz_protocol_scher_khan_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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + case ScherKhanDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < + subghz_protocol_scher_khan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) < + subghz_protocol_scher_khan_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) < + subghz_protocol_scher_khan_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + } else { + instance->decoder.parser_step = ScherKhanDecoderStepReset; + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param protocol_name + */ +static void subghz_protocol_scher_khan_check_remote_controller( + SubGhzBlockGeneric* instance, + const char** protocol_name) { + /* + * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic + * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100 + * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101 + * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110 + * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111 + * Serial Key Ser ~Key CNT + */ + + switch(instance->data_count_bit) { + // case 35: //MAGIC CODE, Static + // instance->protocol_name = "MAGIC CODE, Static"; + // break; + case 51: //MAGIC CODE, Dynamic + *protocol_name = "MAGIC CODE, Dynamic"; + instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F); + instance->btn = (instance->data >> 24) & 0x0F; + instance->cnt = instance->data & 0xFFFF; + break; + // case 57: //MAGIC CODE PRO / PRO2 + // instance->protocol_name = "MAGIC CODE PRO / PRO2"; + // break; + + default: + *protocol_name = "Unknown"; + instance->serial = 0; + instance->btn = 0; + instance->cnt = 0; + break; + } +} + +uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_scher_khan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + return subghz_block_generic_deserialize(&instance->generic, flipper_format); +} + +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderScherKhan* instance = context; + + subghz_protocol_scher_khan_check_remote_controller( + &instance->generic, &instance->protocol_name); + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:%07lX Btn:%X Cnt:%04lX\r\n" + "Pt: %s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt, + instance->protocol_name); +} diff --git a/applications/main/subghz/protocols/scher_khan.h b/applications/main/subghz/protocols/scher_khan.h new file mode 100644 index 000000000..b7e84ea1f --- /dev/null +++ b/applications/main/subghz/protocols/scher_khan.h @@ -0,0 +1,73 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME "Scher-Khan" + +typedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan; +typedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan; + +extern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder; +extern const SubGhzProtocol subghz_protocol_scher_khan; + +/** + * Allocate SubGhzProtocolDecoderScherKhan. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void subghz_protocol_decoder_scher_khan_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + */ +void subghz_protocol_decoder_scher_khan_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_scher_khan_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderScherKhan. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance + * @param output Resulting text + */ +void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v1.c b/applications/main/subghz/protocols/secplus_v1.c new file mode 100644 index 000000000..3ef95db36 --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v1.c @@ -0,0 +1,634 @@ +#include "secplus_v1.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* +* Help +* https://github.com/argilo/secplus +* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c +*/ + +#define TAG "SubGhzProtocoSecPlus_v1" + +#define SECPLUS_V1_BIT_ERR -1 //0b0000 +#define SECPLUS_V1_BIT_0 0 //0b0001 +#define SECPLUS_V1_BIT_1 1 //0b0011 +#define SECPLUS_V1_BIT_2 2 //0b0111 + +#define SECPLUS_V1_PACKET_1_HEADER 0x00 +#define SECPLUS_V1_PACKET_2_HEADER 0x02 +#define SECPLUS_V1_PACKET_1_INDEX_BASE 0 +#define SECPLUS_V1_PACKET_2_INDEX_BASE 21 +#define SECPLUS_V1_PACKET_1_ACCEPTED (1 << 0) +#define SECPLUS_V1_PACKET_2_ACCEPTED (1 << 1) + +static const SubGhzBlockConst subghz_protocol_secplus_v1_const = { + .te_short = 500, + .te_long = 1500, + .te_delta = 100, + .min_count_bit_for_found = 21, +}; + +struct SubGhzProtocolDecoderSecPlus_v1 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint8_t packet_accepted; + uint8_t base_packet_index; + uint8_t data_array[44]; +}; + +struct SubGhzProtocolEncoderSecPlus_v1 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint8_t data_array[44]; +}; + +typedef enum { + SecPlus_v1DecoderStepReset = 0, + SecPlus_v1DecoderStepSearchStartBit, + SecPlus_v1DecoderStepSaveDuration, + SecPlus_v1DecoderStepDecoderData, +} SecPlus_v1DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = { + .alloc = subghz_protocol_decoder_secplus_v1_alloc, + .free = subghz_protocol_decoder_secplus_v1_free, + + .feed = subghz_protocol_decoder_secplus_v1_feed, + .reset = subghz_protocol_decoder_secplus_v1_reset, + + .get_hash_data = subghz_protocol_decoder_secplus_v1_get_hash_data, + .serialize = subghz_protocol_decoder_secplus_v1_serialize, + .deserialize = subghz_protocol_decoder_secplus_v1_deserialize, + .get_string = subghz_protocol_decoder_secplus_v1_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = { + .alloc = subghz_protocol_encoder_secplus_v1_alloc, + .free = subghz_protocol_encoder_secplus_v1_free, + + .deserialize = subghz_protocol_encoder_secplus_v1_deserialize, + .stop = subghz_protocol_encoder_secplus_v1_stop, + .yield = subghz_protocol_encoder_secplus_v1_yield, +}; + +const SubGhzProtocol subghz_protocol_secplus_v1 = { + .name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Save, + + .decoder = &subghz_protocol_secplus_v1_decoder, + .encoder = &subghz_protocol_secplus_v1_encoder, +}; + +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1)); + + instance->base.protocol = &subghz_protocol_secplus_v1; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_secplus_v1_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return true On success + */ +static bool + subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* 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, "Encoder size upload exceeds allocated encoder buffer."); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + //Send header packet 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + + //Send data packet 1 + for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type"); + return false; + break; + } + } + + //Send header packet 2 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116)); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + + //Send data packet 2 + for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21; + i++) { + switch(instance->data_array[i]) { + case SECPLUS_V1_BIT_0: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + break; + case SECPLUS_V1_BIT_1: + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); + break; + case SECPLUS_V1_BIT_2: + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); + break; + + default: + FURI_LOG_E(TAG, "Encoder error, wrong bit type."); + return false; + break; + } + } + + return true; +} + +/** + * Security+ 1.0 message encoding + * @param instance SubGhzProtocolEncoderSecPlus_v1* + */ + +static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) { + uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; + uint32_t rolling = instance->generic.data & 0xFFFFFFFF; + + uint8_t rolling_array[20] = {0}; + uint8_t fixed_array[20] = {0}; + uint32_t acc = 0; + + //increment the counter + rolling += 2; + + //update data + instance->generic.data &= 0xFFFFFFFF00000000; + instance->generic.data |= rolling; + + if(rolling == 0xFFFFFFFF) { + rolling = 0xE6000000; + } + if(fixed > 0xCFD41B90) { + FURI_LOG_E("TAG", "Encode wrong fixed data"); + return false; + } + + rolling = subghz_protocol_blocks_reverse_key(rolling, 32); + + for(int i = 19; i > -1; i--) { + rolling_array[i] = rolling % 3; + rolling /= 3; + fixed_array[i] = fixed % 3; + fixed /= 3; + } + + instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER; + instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER; + + //encode packet 1 + for(uint8_t i = 1; i < 11; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2 - 1] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2] = acc % 3; + } + + acc = 0; + //encode packet 2 + for(uint8_t i = 11; i < 21; i++) { + acc += rolling_array[i - 1]; + instance->data_array[i * 2] = rolling_array[i - 1]; + acc += fixed_array[i - 1]; + instance->data_array[i * 2 + 1] = acc % 3; + } + + return true; +} + +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v1* 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 != + 2 * subghz_protocol_secplus_v1_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); + + if(!subghz_protocol_secplus_v1_encode(instance)) { + break; + } + if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) { + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_secplus_v1_stop(void* context) { + SubGhzProtocolEncoderSecPlus_v1* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) { + SubGhzProtocolEncoderSecPlus_v1* 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_secplus_v1_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1)); + instance->base.protocol = &subghz_protocol_secplus_v1; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_secplus_v1_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + free(instance); +} + +void subghz_protocol_decoder_secplus_v1_reset(void* context) { + furi_assert(context); + // SubGhzProtocolDecoderSecPlus_v1* instance = context; + // does not reset the decoder because you need to get 2 parts of the package +} + +/** + * Security+ 1.0 message decoding + * @param instance SubGhzProtocolDecoderSecPlus_v1* + */ + +static void subghz_protocol_secplus_v1_decode(SubGhzProtocolDecoderSecPlus_v1* instance) { + uint32_t rolling = 0; + uint32_t fixed = 0; + uint32_t acc = 0; + uint8_t digit = 0; + + //decode packet 1 + for(uint8_t i = 1; i < 21; i += 2) { + digit = instance->data_array[i]; + rolling = (rolling * 3) + digit; + acc += digit; + + digit = (60 + instance->data_array[i + 1] - acc) % 3; + fixed = (fixed * 3) + digit; + acc += digit; + } + + acc = 0; + //decode packet 2 + for(uint8_t i = 22; i < 42; i += 2) { + digit = instance->data_array[i]; + rolling = (rolling * 3) + digit; + acc += digit; + + digit = (60 + instance->data_array[i + 1] - acc) % 3; + fixed = (fixed * 3) + digit; + acc += digit; + } + + rolling = subghz_protocol_blocks_reverse_key(rolling, 32); + instance->generic.data = (uint64_t)fixed << 32 | rolling; + + instance->generic.data_count_bit = + subghz_protocol_secplus_v1_const.min_count_bit_for_found * 2; +} + +void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + + switch(instance->decoder.parser_step) { + case SecPlus_v1DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < + subghz_protocol_secplus_v1_const.te_delta * 120)) { + //Found header Security+ 1.0 + instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->packet_accepted = 0; + memset(instance->data_array, 0, sizeof(instance->data_array)); + } + break; + case SecPlus_v1DecoderStepSearchStartBit: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta) { + instance->base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE; + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_long) < + subghz_protocol_secplus_v1_const.te_delta) { + instance->base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE; + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_2; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + case SecPlus_v1DecoderStepSaveDuration: + if(!level) { //save interval + if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < + subghz_protocol_secplus_v1_const.te_delta * 120) { + if(instance->decoder.decode_count_bit == + subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + if(instance->base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE) + instance->packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED; + if(instance->base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE) + instance->packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED; + + if(instance->packet_accepted == + (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) { + subghz_protocol_secplus_v1_decode(instance); + + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } + instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = SecPlus_v1DecoderStepDecoderData; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + case SecPlus_v1DecoderStepDecoderData: + if(level && (instance->decoder.decode_count_bit <= + subghz_protocol_secplus_v1_const.min_count_bit_for_found)) { + if((DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 3) < + subghz_protocol_secplus_v1_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_0; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 2) < + subghz_protocol_secplus_v1_const.te_delta * 2) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 2) < + subghz_protocol_secplus_v1_const.te_delta * 2)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_1; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else if( + (DURATION_DIFF( + instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short) < + subghz_protocol_secplus_v1_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 3) < + subghz_protocol_secplus_v1_const.te_delta * 3)) { + instance + ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = + SECPLUS_V1_BIT_2; + instance->decoder.decode_count_bit++; + instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + } else { + instance->decoder.parser_step = SecPlus_v1DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_secplus_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + ret = true; + } while(false); + return ret; +} + +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { + //uint8_t id0 = (fixed / 3) % 3; + uint8_t id1 = (fixed / 9) % 3; + uint8_t btn = fixed % 3; + + do { + if(id1 == 0) return false; + if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560 + } while(false); + return true; +} + +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v1* instance = context; + + uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; + instance->generic.cnt = instance->generic.data & 0xFFFFFFFF; + + instance->generic.btn = fixed % 3; + uint8_t id0 = (fixed / 3) % 3; + uint8_t id1 = (fixed / 9) % 3; + uint16_t pin = 0; + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "id1:%d id0:%d", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + id1, + id0); + + if(id1 == 0) { + // (fixed // 3**3) % (3**7) 3^3=27 3^73=72187 + + instance->generic.serial = (fixed / 27) % 2187; + // pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683 + pin = (fixed / 59049) % 19683; + + if(pin <= 9999) { + furi_string_cat_printf(output, " pin:%d", pin); + } else if(pin <= 11029) { + furi_string_cat_printf(output, " pin:enter"); + } + + int pin_suffix = 0; + // pin_suffix = (fixed // 3**19) % 3 3^19=1162261467 + pin_suffix = (fixed / 1162261467) % 3; + + if(pin_suffix == 1) { + furi_string_cat_printf(output, " #\r\n"); + } else if(pin_suffix == 2) { + furi_string_cat_printf(output, " *\r\n"); + } else { + furi_string_cat_printf(output, "\r\n"); + } + furi_string_cat_printf( + output, + "Sn:0x%08lX\r\n" + "Cnt:0x%03lX " + "Sw_id:0x%X\r\n", + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } else { + //id = fixed / 27; + instance->generic.serial = fixed / 27; + if(instance->generic.btn == 1) { + furi_string_cat_printf(output, " Btn:left\r\n"); + } else if(instance->generic.btn == 0) { + furi_string_cat_printf(output, " Btn:middle\r\n"); + } else if(instance->generic.btn == 2) { //-V547 + furi_string_cat_printf(output, " Btn:right\r\n"); + } + + furi_string_cat_printf( + output, + "Sn:0x%08lX\r\n" + "Cnt:0x%03lX " + "Sw_id:0x%X\r\n", + instance->generic.serial, + instance->generic.cnt, + instance->generic.btn); + } +} diff --git a/applications/main/subghz/protocols/secplus_v1.h b/applications/main/subghz/protocols/secplus_v1.h new file mode 100644 index 000000000..99480b62b --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v1.h @@ -0,0 +1,113 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME "Security+ 1.0" + +typedef struct SubGhzProtocolDecoderSecPlus_v1 SubGhzProtocolDecoderSecPlus_v1; +typedef struct SubGhzProtocolEncoderSecPlus_v1 SubGhzProtocolEncoderSecPlus_v1; + +extern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder; +extern const SubGhzProtocol subghz_protocol_secplus_v1; + +/** + * Allocate SubGhzProtocolEncoderSecPlus_v1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + */ +void subghz_protocol_encoder_secplus_v1_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSecPlus_v1. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSecPlus_v1* pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void subghz_protocol_decoder_secplus_v1_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + */ +void subghz_protocol_decoder_secplus_v1_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v1_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSecPlus_v1. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. + * @param fixed fixed parts + * @return true On success + */ +bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v2.c b/applications/main/subghz/protocols/secplus_v2.c new file mode 100644 index 000000000..bcef90dad --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v2.c @@ -0,0 +1,833 @@ +#include "secplus_v2.h" +#include +#include +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* +* Help +* https://github.com/argilo/secplus +* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c +*/ + +#define TAG "SubGhzProtocoSecPlus_v2" + +#define SECPLUS_V2_HEADER 0x3C0000000000 +#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000 +#define SECPLUS_V2_PACKET_1 0x000000000000 +#define SECPLUS_V2_PACKET_2 0x010000000000 +#define SECPLUS_V2_PACKET_MASK 0x30000000000 + +static const SubGhzBlockConst subghz_protocol_secplus_v2_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 110, + .min_count_bit_for_found = 62, +}; + +struct SubGhzProtocolDecoderSecPlus_v2 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + ManchesterState manchester_saved_state; + uint64_t secplus_packet_1; +}; + +struct SubGhzProtocolEncoderSecPlus_v2 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + uint64_t secplus_packet_1; +}; + +typedef enum { + SecPlus_v2DecoderStepReset = 0, + SecPlus_v2DecoderStepDecoderData, +} SecPlus_v2DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = { + .alloc = subghz_protocol_decoder_secplus_v2_alloc, + .free = subghz_protocol_decoder_secplus_v2_free, + + .feed = subghz_protocol_decoder_secplus_v2_feed, + .reset = subghz_protocol_decoder_secplus_v2_reset, + + .get_hash_data = subghz_protocol_decoder_secplus_v2_get_hash_data, + .serialize = subghz_protocol_decoder_secplus_v2_serialize, + .deserialize = subghz_protocol_decoder_secplus_v2_deserialize, + .get_string = subghz_protocol_decoder_secplus_v2_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = { + .alloc = subghz_protocol_encoder_secplus_v2_alloc, + .free = subghz_protocol_encoder_secplus_v2_free, + + .deserialize = subghz_protocol_encoder_secplus_v2_deserialize, + .stop = subghz_protocol_encoder_secplus_v2_stop, + .yield = subghz_protocol_encoder_secplus_v2_yield, +}; + +const SubGhzProtocol subghz_protocol_secplus_v2 = { + .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_secplus_v2_decoder, + .encoder = &subghz_protocol_secplus_v2_encoder, +}; + +void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2)); + + instance->base.protocol = &subghz_protocol_secplus_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_secplus_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) { + // selectively invert buffers + switch(invert) { + case 0x00: // 0b0000 (True, True, False), + p[0] = ~p[0] & 0x03FF; + p[1] = ~p[1] & 0x03FF; + break; + case 0x01: // 0b0001 (False, True, False), + p[1] = ~p[1] & 0x03FF; + break; + case 0x02: // 0b0010 (False, False, True), + p[2] = ~p[2] & 0x03FF; + break; + case 0x04: // 0b0100 (True, True, True), + p[0] = ~p[0] & 0x03FF; + p[1] = ~p[1] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x05: // 0b0101 (True, False, True), + case 0x0a: // 0b1010 (True, False, True), + p[0] = ~p[0] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x06: // 0b0110 (False, True, True), + p[1] = ~p[1] & 0x03FF; + p[2] = ~p[2] & 0x03FF; + break; + case 0x08: // 0b1000 (True, False, False), + p[0] = ~p[0] & 0x03FF; + break; + case 0x09: // 0b1001 (False, False, False), + break; + default: + FURI_LOG_E(TAG, "Invert FAIL"); + return false; + } + return true; +} + +static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) { + uint16_t a = p[0], b = p[1], c = p[2]; + + // selectively reorder buffers + switch(order) { + case 0x06: // 0b0110 2, 1, 0], + case 0x09: // 0b1001 2, 1, 0], + p[2] = a; + // p[1]: no change + p[0] = c; + break; + case 0x08: // 0b1000 1, 2, 0], + case 0x04: // 0b0100 1, 2, 0], + p[1] = a; + p[2] = b; + p[0] = c; + break; + case 0x01: // 0b0001 2, 0, 1], + p[2] = a; + p[0] = b; + p[1] = c; + break; + case 0x00: // 0b0000 0, 2, 1], + // p[0]: no change + p[2] = b; + p[1] = c; + break; + case 0x05: // 0b0101 1, 0, 2], + p[1] = a; + p[0] = b; + // p[2]: no change + break; + case 0x02: // 0b0010 0, 1, 2], + case 0x0A: // 0b1010 0, 1, 2], + // no reordering + break; + default: + FURI_LOG_E(TAG, "Order FAIL"); + return false; + } + return true; +} + +static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) { + uint16_t a, b, c; + + // selectively reorder buffers + switch(order) { + case 0x06: // 0b0110 2, 1, 0], + case 0x09: // 0b1001 2, 1, 0], + a = p[2]; + b = p[1]; + c = p[0]; + break; + case 0x08: // 0b1000 1, 2, 0], + case 0x04: // 0b0100 1, 2, 0], + a = p[1]; + b = p[2]; + c = p[0]; + break; + case 0x01: // 0b0001 2, 0, 1], + a = p[2]; + b = p[0]; + c = p[1]; + break; + case 0x00: // 0b0000 0, 2, 1], + a = p[0]; + b = p[2]; + c = p[1]; + break; + case 0x05: // 0b0101 1, 0, 2], + a = p[1]; + b = p[0]; + c = p[2]; + break; + case 0x02: // 0b0010 0, 1, 2], + case 0x0A: // 0b1010 0, 1, 2], + a = p[0]; + b = p[1]; + c = p[2]; + break; + default: + FURI_LOG_E(TAG, "Order FAIL"); + return false; + } + + p[0] = a; + p[1] = b; + p[2] = c; + return true; +} + +/** + * Security+ 2.0 half-message decoding + * @param data data + * @param roll_array[] return roll_array part + * @param fixed[] return fixed part + * @return true On success + */ + +static bool + subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) { + uint8_t order = (data >> 34) & 0x0f; + uint8_t invert = (data >> 30) & 0x0f; + uint16_t p[3] = {0}; + + for(int i = 29; i >= 0; i -= 3) { + p[0] = p[0] << 1 | bit_read(data, i); + p[1] = p[1] << 1 | bit_read(data, i - 1); + p[2] = p[2] << 1 | bit_read(data, i - 2); + } + + if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false; + if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false; + + data = order << 4 | invert; + int k = 0; + for(int i = 6; i >= 0; i -= 2) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { + FURI_LOG_E(TAG, "Roll_Array FAIL"); + return false; + } + } + + for(int i = 8; i >= 0; i -= 2) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { + FURI_LOG_E(TAG, "Roll_Array FAIL"); + return false; + } + } + + fixed[0] = p[0] << 10 | p[1]; + return true; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param packet_1 first part of the message + */ +static void + subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) { + uint32_t fixed_1[1]; + uint8_t roll_1[9] = {0}; + uint32_t fixed_2[1]; + uint8_t roll_2[9] = {0}; + uint8_t rolling_digits[18] = {0}; + + if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) && + subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) { + rolling_digits[0] = roll_2[8]; + rolling_digits[1] = roll_1[8]; + + rolling_digits[2] = roll_2[4]; + rolling_digits[3] = roll_2[5]; + rolling_digits[4] = roll_2[6]; + rolling_digits[5] = roll_2[7]; + + rolling_digits[6] = roll_1[4]; + rolling_digits[7] = roll_1[5]; + rolling_digits[8] = roll_1[6]; + rolling_digits[9] = roll_1[7]; + + rolling_digits[10] = roll_2[0]; + rolling_digits[11] = roll_2[1]; + rolling_digits[12] = roll_2[2]; + rolling_digits[13] = roll_2[3]; + + rolling_digits[14] = roll_1[0]; + rolling_digits[15] = roll_1[1]; + rolling_digits[16] = roll_1[2]; + rolling_digits[17] = roll_1[3]; + + uint32_t rolling = 0; + for(int i = 0; i < 18; i++) { + rolling = (rolling * 3) + rolling_digits[i]; + } + // Max value = 2^28 (268435456) + if(rolling >= 0x10000000) { + FURI_LOG_E(TAG, "Rolling FAIL"); + instance->cnt = 0; + instance->btn = 0; + instance->serial = 0; + } else { + instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28); + instance->btn = fixed_1[0] >> 12; + instance->serial = fixed_1[0] << 20 | fixed_2[0]; + } + } else { + instance->cnt = 0; + instance->btn = 0; + instance->serial = 0; + } +} + +/** + * Security+ 2.0 half-message encoding + * @param roll_array[] roll_array part + * @param fixed[] fixed part + * @return return data + */ + +static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) { + uint64_t data = 0; + uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0}; + uint8_t order = roll_array[0] << 2 | roll_array[1]; + uint8_t invert = roll_array[2] << 2 | roll_array[3]; + p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 | + roll_array[7] << 2 | roll_array[8]; + + if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0; + if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0; + + for(int i = 0; i < 10; i++) { + data <<= 3; + data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i); + } + data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30; + + return data; +} + +/** + * Security+ 2.0 message encoding + * @param instance SubGhzProtocolEncoderSecPlus_v2* + */ + +static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) { + uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; + uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; + uint8_t rolling_digits[18] = {0}; + uint8_t roll_1[9] = {0}; + uint8_t roll_2[9] = {0}; + + instance->generic.cnt++; + //ToDo it is not known what value the counter starts + if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000; + uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28); + + for(int8_t i = 17; i > -1; i--) { + rolling_digits[i] = rolling % 3; + rolling /= 3; + } + + roll_2[8] = rolling_digits[0]; + roll_1[8] = rolling_digits[1]; + + roll_2[4] = rolling_digits[2]; + roll_2[5] = rolling_digits[3]; + roll_2[6] = rolling_digits[4]; + roll_2[7] = rolling_digits[5]; + + roll_1[4] = rolling_digits[6]; + roll_1[5] = rolling_digits[7]; + roll_1[6] = rolling_digits[8]; + roll_1[7] = rolling_digits[9]; + + roll_2[0] = rolling_digits[10]; + roll_2[1] = rolling_digits[11]; + roll_2[2] = rolling_digits[12]; + roll_2[3] = rolling_digits[13]; + + roll_1[0] = rolling_digits[14]; + roll_1[1] = rolling_digits[15]; + roll_1[2] = rolling_digits[16]; + roll_1[3] = rolling_digits[17]; + + instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 | + subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]); + instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 | + subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]); +} + +static LevelDuration + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) { + LevelDuration data = {.duration = 0, .level = 0}; + switch(result) { + case ManchesterEncoderResultShortLow: + data.duration = subghz_protocol_secplus_v2_const.te_short; + data.level = false; + break; + case ManchesterEncoderResultLongLow: + data.duration = subghz_protocol_secplus_v2_const.te_long; + data.level = false; + break; + case ManchesterEncoderResultLongHigh: + data.duration = subghz_protocol_secplus_v2_const.te_long; + data.level = true; + break; + case ManchesterEncoderResultShortHigh: + data.duration = subghz_protocol_secplus_v2_const.te_short; + data.level = true; + break; + + default: + furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); + break; + } + return level_duration_make(data.level, data.duration); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +static void + subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) { + furi_assert(instance); + size_t index = 0; + + ManchesterEncoderState enc_state; + manchester_encoder_reset(&enc_state); + ManchesterEncoderResult result; + + //Send data packet 1 + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); + + //Send data packet 2 + manchester_encoder_reset(&enc_state); + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(!manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result)) { + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + manchester_encoder_advance( + &enc_state, bit_read(instance->generic.data, i - 1), &result); + } + instance->encoder.upload[index++] = + subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); + } + instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( + manchester_encoder_finish(&enc_state)); + if(level_duration_get_level(instance->encoder.upload[index])) { + index++; + } + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); + + instance->encoder.size_upload = index; +} + +bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* 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_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Secplus_packet_1"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; + } + + subghz_protocol_secplus_v2_remote_controller( + &instance->generic, instance->secplus_packet_1); + subghz_protocol_secplus_v2_encode(instance); + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + subghz_protocol_encoder_secplus_v2_get_upload(instance); + + //update data + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + if(!flipper_format_update_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_secplus_v2_stop(void* context) { + SubGhzProtocolEncoderSecPlus_v2* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) { + SubGhzProtocolEncoderSecPlus_v2* 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; +} + +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSecPlus_v2* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.btn = btn; + instance->generic.data_count_bit = + (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; + subghz_protocol_secplus_v2_encode(instance); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + + if(res && + !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + res = false; + } + return res; +} + +void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2)); + instance->base.protocol = &subghz_protocol_secplus_v2; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_secplus_v2_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + free(instance); +} + +void subghz_protocol_decoder_secplus_v2_reset(void* context) { + furi_assert(context); + // SubGhzProtocolDecoderSecPlus_v2* instance = context; + // does not reset the decoder because you need to get 2 parts of the package +} + +static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) { + if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) { + if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) { + instance->secplus_packet_1 = instance->decoder.decode_data; + } else if( + ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) && + (instance->secplus_packet_1)) { + return true; + } + } + return false; +} + +void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SecPlus_v2DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) < + subghz_protocol_secplus_v2_const.te_delta * 100)) { + //Found header Security+ 2.0 + instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->secplus_packet_1 = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } + break; + case SecPlus_v2DecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL + + subghz_protocol_secplus_v2_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_secplus_v2_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(subghz_protocol_secplus_v2_check_packet(instance)) { + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventShortLow, + &instance->manchester_saved_state, + NULL); + } else { + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < + subghz_protocol_secplus_v2_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SecPlus_v2DecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_secplus_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; + } + + if(res && + !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* 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_secplus_v2_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex( + flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Secplus_packet_1"); + break; + } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSecPlus_v2* instance = context; + subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Pk1:0x%lX%08lX\r\n" + "Pk2:0x%lX%08lX\r\n" + "Sn:0x%08lX Btn:0x%01X\r\n" + "Cnt:0x%03lX\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->secplus_packet_1 >> 32), + (uint32_t)instance->secplus_packet_1, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.btn, + instance->generic.cnt); +} diff --git a/applications/main/subghz/protocols/secplus_v2.h b/applications/main/subghz/protocols/secplus_v2.h new file mode 100644 index 000000000..bea8cdb5d --- /dev/null +++ b/applications/main/subghz/protocols/secplus_v2.h @@ -0,0 +1,125 @@ +#pragma once +#include "base.h" + +#define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME "Security+ 2.0" + +typedef struct SubGhzProtocolDecoderSecPlus_v2 SubGhzProtocolDecoderSecPlus_v2; +typedef struct SubGhzProtocolEncoderSecPlus_v2 SubGhzProtocolEncoderSecPlus_v2; + +extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; +extern const SubGhzProtocol subghz_protocol_secplus_v2; + +/** + * Allocate SubGhzProtocolEncoderSecPlus_v2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void subghz_protocol_encoder_secplus_v2_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + */ +void subghz_protocol_encoder_secplus_v2_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 32 bit + * @param btn Button number, 8 bit + * @param cnt Container value, 28 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_secplus_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint32_t cnt, + SubGhzRadioPreset* preset); + +/** + * Allocate SubGhzProtocolDecoderSecPlus_v2. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSecPlus_v2* pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void subghz_protocol_decoder_secplus_v2_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + */ +void subghz_protocol_decoder_secplus_v2_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v2_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSecPlus_v2. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/smc5326.c b/applications/main/subghz/protocols/smc5326.c new file mode 100644 index 000000000..9c9b5d4fd --- /dev/null +++ b/applications/main/subghz/protocols/smc5326.c @@ -0,0 +1,387 @@ +#include "smc5326.h" + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +/* + * Help + * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1 + * + */ + +#define TAG "SubGhzProtocolSMC5326" + +#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_smc5326_const = { + .te_short = 300, + .te_long = 900, + .te_delta = 200, + .min_count_bit_for_found = 25, +}; + +struct SubGhzProtocolDecoderSMC5326 { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint32_t te; + uint32_t last_data; +}; + +struct SubGhzProtocolEncoderSMC5326 { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + uint32_t te; +}; + +typedef enum { + SMC5326DecoderStepReset = 0, + SMC5326DecoderStepSaveDuration, + SMC5326DecoderStepCheckDuration, +} SMC5326DecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = { + .alloc = subghz_protocol_decoder_smc5326_alloc, + .free = subghz_protocol_decoder_smc5326_free, + + .feed = subghz_protocol_decoder_smc5326_feed, + .reset = subghz_protocol_decoder_smc5326_reset, + + .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data, + .serialize = subghz_protocol_decoder_smc5326_serialize, + .deserialize = subghz_protocol_decoder_smc5326_deserialize, + .get_string = subghz_protocol_decoder_smc5326_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = { + .alloc = subghz_protocol_encoder_smc5326_alloc, + .free = subghz_protocol_encoder_smc5326_free, + + .deserialize = subghz_protocol_encoder_smc5326_deserialize, + .stop = subghz_protocol_encoder_smc5326_stop, + .yield = subghz_protocol_encoder_smc5326_yield, +}; + +const SubGhzProtocol subghz_protocol_smc5326 = { + .name = SUBGHZ_PROTOCOL_SMC5326_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | + SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | + SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_smc5326_decoder, + .encoder = &subghz_protocol_smc5326_encoder, +}; + +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326)); + + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 128; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return true On success + */ +static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) { + furi_assert(instance); + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2) + 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; + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)instance->te * 3); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)instance->te * 3); + } + } + + //Send Stop bit + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); + //Send PT_GUARD + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25); + + return true; +} + +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSMC5326* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_smc5326_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); + + if(!subghz_protocol_encoder_smc5326_get_upload(instance)) break; + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_smc5326_stop(void* context) { + SubGhzProtocolEncoderSMC5326* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context) { + SubGhzProtocolEncoderSMC5326* 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_smc5326_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326)); + instance->base.protocol = &subghz_protocol_smc5326; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_smc5326_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + free(instance); +} + +void subghz_protocol_decoder_smc5326_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + instance->decoder.parser_step = SMC5326DecoderStepReset; + instance->last_data = 0; +} + +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + + switch(instance->decoder.parser_step) { + case SMC5326DecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) < + subghz_protocol_smc5326_const.te_delta * 12)) { + //Found Preambula + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + } + break; + case SMC5326DecoderStepSaveDuration: + //save duration + if(level) { + instance->decoder.te_last = duration; + instance->te += duration; + instance->decoder.parser_step = SMC5326DecoderStepCheckDuration; + } + break; + case SMC5326DecoderStepCheckDuration: + if(!level) { + if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) { + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + if(instance->decoder.decode_count_bit == + subghz_protocol_smc5326_const.min_count_bit_for_found) { + if((instance->last_data == instance->decoder.decode_data) && + instance->last_data) { + instance->te /= (instance->decoder.decode_count_bit * 4 + 1); + + 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->last_data = instance->decoder.decode_data; + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->te = 0; + break; + } + + instance->te += duration; + + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) < + subghz_protocol_smc5326_const.te_delta * 3) && + (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) < + subghz_protocol_smc5326_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + } else { + instance->decoder.parser_step = SMC5326DecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { + FURI_LOG_E(TAG, "Unable to add TE"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* 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_smc5326_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { + FURI_LOG_E(TAG, "Missing TE"); + break; + } + res = true; + } while(false); + + return res; +} + +static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { + furi_string_cat_printf( + output, + "%s%s%s%s\r\n", + (((event >> 6) & 0x3) == 0x3 ? "B1 " : ""), + (((event >> 4) & 0x3) == 0x3 ? "B2 " : ""), + (((event >> 2) & 0x3) == 0x3 ? "B3 " : ""), + (((event >> 0) & 0x3) == 0x3 ? "B4 " : "")); +} + +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSMC5326* instance = context; + uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF); + + furi_string_cat_printf( + output, + "%s %ubit\r\n" + "Key:%07lX Te:%luus\r\n" + " +: " DIP_PATTERN "\r\n" + " o: " DIP_PATTERN " ", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0x1FFFFFF), + instance->te, + SHOW_DIP_P(data, DIP_P), + SHOW_DIP_P(data, DIP_O)); + subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output); + furi_string_cat_printf(output, " -: " DIP_PATTERN "\r\n", SHOW_DIP_P(data, DIP_N)); +} diff --git a/applications/main/subghz/protocols/smc5326.h b/applications/main/subghz/protocols/smc5326.h new file mode 100644 index 000000000..ddc954bd5 --- /dev/null +++ b/applications/main/subghz/protocols/smc5326.h @@ -0,0 +1,107 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SMC5326_NAME "SMC5326" + +typedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326; +typedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326; + +extern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder; +extern const SubGhzProtocol subghz_protocol_smc5326; + +/** + * Allocate SubGhzProtocolEncoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSMC5326. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + */ +void subghz_protocol_encoder_smc5326_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_smc5326_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSMC5326. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + */ +void subghz_protocol_decoder_smc5326_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSMC5326. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance + * @param output Resulting text + */ +void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_keytis.c b/applications/main/subghz/protocols/somfy_keytis.c new file mode 100644 index 000000000..ab9202cc3 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_keytis.c @@ -0,0 +1,797 @@ +#include "somfy_keytis.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolSomfyKeytis" + +static const SubGhzBlockConst subghz_protocol_somfy_keytis_const = { + .te_short = 640, + .te_long = 1280, + .te_delta = 250, + .min_count_bit_for_found = 80, +}; + +struct SubGhzProtocolDecoderSomfyKeytis { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + ManchesterState manchester_saved_state; + uint32_t press_duration_counter; +}; + +struct SubGhzProtocolEncoderSomfyKeytis { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + SomfyKeytisDecoderStepReset = 0, + SomfyKeytisDecoderStepCheckPreambula, + SomfyKeytisDecoderStepFoundPreambula, + SomfyKeytisDecoderStepStartDecode, + SomfyKeytisDecoderStepDecoderData, +} SomfyKeytisDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = { + .alloc = subghz_protocol_decoder_somfy_keytis_alloc, + .free = subghz_protocol_decoder_somfy_keytis_free, + + .feed = subghz_protocol_decoder_somfy_keytis_feed, + .reset = subghz_protocol_decoder_somfy_keytis_reset, + + .get_hash_data = subghz_protocol_decoder_somfy_keytis_get_hash_data, + .serialize = subghz_protocol_decoder_somfy_keytis_serialize, + .deserialize = subghz_protocol_decoder_somfy_keytis_deserialize, + .get_string = subghz_protocol_decoder_somfy_keytis_get_string, +}; + +const SubGhzProtocol subghz_protocol_somfy_keytis = { + .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_somfy_keytis_decoder, + .encoder = &subghz_protocol_somfy_keytis_encoder, +}; + +const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { + .alloc = subghz_protocol_encoder_somfy_keytis_alloc, + .free = subghz_protocol_encoder_somfy_keytis_free, + + .deserialize = subghz_protocol_encoder_somfy_keytis_deserialize, + .stop = subghz_protocol_encoder_somfy_keytis_stop, + .yield = subghz_protocol_encoder_somfy_keytis_yield, +}; + +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis)); + + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis)); + instance->base.protocol = &subghz_protocol_somfy_keytis; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_encoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_decoder_somfy_keytis_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + free(instance); +} + +void subghz_protocol_decoder_somfy_keytis_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +static bool + subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 48) & 0xF; + instance->generic.cnt = (data >> 24) & 0xFFFF; + instance->generic.serial = data & 0xFFFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[10]; + frame[0] = (0xA << 4) | instance->generic.btn; + frame[1] = 0xF << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + frame[7] = 0xC4; + frame[8] = 0x00; + frame[9] = 0x19; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + data = 0; + for(uint8_t i = 7; i < 10; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data_2 = data; + return true; +} + +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 80; + bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_keytis_get_upload( + SubGhzProtocolEncoderSomfyKeytis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + + for(uint8_t i = 0; i < 2; ++i) { + //Hardware sync + for(uint8_t i = 0; i < 6; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + + for(uint8_t i = 24; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 + } + } + } + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); + } + } + + //Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; + + size_t size_upload = index; + + 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; + } + return true; +} + +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyKeytis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_keytis_stop(void* context) { + SubGhzProtocolEncoderSomfyKeytis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) { + SubGhzProtocolEncoderSomfyKeytis* 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; +} + +/** + * Сhecksum calculation. + * @param data Вata for checksum calculation + * @return CRC + */ +static uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} + +void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SomfyKeytisDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->header_count++; + } + break; + case SomfyKeytisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyKeytisDecoderStepCheckPreambula; + } else { + instance->header_count = 0; + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + break; + case SomfyKeytisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < + subghz_protocol_somfy_keytis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->header_count++; + } else if( + (instance->header_count > 1) && + (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 7) < + subghz_protocol_somfy_keytis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->press_duration_counter = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + } + } + + break; + + case SomfyKeytisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_somfy_keytis_const.te_long + + subghz_protocol_somfy_keytis_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { + //check crc + uint64_t data_tmp = instance->generic.data ^ (instance->generic.data >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) { + 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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } else { + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < + subghz_protocol_somfy_keytis_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SomfyKeytisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + if(instance->decoder.decode_count_bit < 56) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + } else { + instance->press_duration_counter = (instance->press_duration_counter << 1) | + data; + } + + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+ + * + * | hw. sync. | soft. | | + * | | sync. | data | + * + * + * encrypt | decrypt + * + * package 80 bit pdc key btn crc cnt serial + * + * 0xA453537C4B9855 C40019 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 C80026 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 CC0033 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D00049 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D4005C => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D80063 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 DC0076 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E00086 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E40093 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E800AC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 EC00B9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F000C3 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F400D6 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F800E9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC00FC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0102 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0113 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0120 => 0xA 4 F 7 002F 37D3CD + * .......... + * 0xA453537C4B9855 FC048F => 0xA 4 F 7 002F 37D3CD + * + * Pdc: "Press Duration Counter" the total delay of the button is sent 72 parcels, + * pdc cnt4b cnt8b pdc_crc + * C40019 => 11 0001 00 0000 00000001 1001 + * C80026 => 11 0010 00 0000 00000010 0110 + * CC0033 => 11 0011 00 0000 00000011 0011 + * D00049 => 11 0100 00 0000 00000100 1001 + * D4005C => 11 0101 00 0000 00000101 1100 + * D80063 => 11 0110 00 0000 00000110 0011 + * DC0076 => 11 0111 00 0000 00000111 0110 + * E00086 => 11 1000 00 0000 00001000 0110 + * E40093 => 11 1001 00 0000 00001001 0011 + * E800AC => 11 1010 00 0000 00001010 1100 + * EC00B9 => 11 1011 00 0000 00001011 1001 + * F000C3 => 11 1100 00 0000 00001100 0011 + * F400D6 => 11 1101 00 0000 00001101 0110 + * F800E9 => 11 1110 00 0000 00001110 1001 + * FC00FC => 11 1111 00 0000 00001111 1100 + * FC0102 => 11 1111 00 0000 00010000 0010 + * FC0113 => 11 1111 00 0000 00010001 0011 + * FC0120 => 11 1111 00 0000 00010010 0000 + * + * Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15 + * Cnt8b: 8-bit counter changes from 1 to 72 (0x48) + * Ppdc_crc: + * uint8_t crc=0; + * for(i=4; i<24; i+=4){ + * crc ^=(pdc>>i); + * } + * return crc; + * example: crc = 1^0^0^4^C = 9 + * 11, 00, 0000: const + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * crc = crc ^ frame[i] ^ (frame[i] >> 4); + * } + * crc = crc & 0xf; + * + */ + + uint64_t data = instance->data ^ (instance->data >> 8); + instance->btn = (data >> 48) & 0xF; + instance->cnt = (data >> 24) & 0xFFFF; + instance->serial = data & 0xFFFFFF; +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) { + const char* name_btn[0x10] = { + "Unknown", + "0x01", + "0x02", + "Prog", + "Key_1", + "0x05", + "0x06", + "0x07", + "0x08", + "0x09", + "0x0A", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_somfy_keytis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + if(res && !flipper_format_write_uint32( + flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { + FURI_LOG_E(TAG, "Unable to add Duration_Counter"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* 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_somfy_keytis_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_read_uint32( + flipper_format, + "Duration_Counter", + (uint32_t*)&instance->press_duration_counter, + 1)) { + FURI_LOG_E(TAG, "Missing Duration_Counter"); + break; + } + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSomfyKeytis* instance = context; + + subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "%lX%08lX%06lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04lX\r\n" + "Btn:%s\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->press_duration_counter, + instance->generic.serial, + instance->generic.cnt, + subghz_protocol_somfy_keytis_get_name_button(instance->generic.btn)); +} diff --git a/applications/main/subghz/protocols/somfy_keytis.h b/applications/main/subghz/protocols/somfy_keytis.h new file mode 100644 index 000000000..b08da3641 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_keytis.h @@ -0,0 +1,125 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME "Somfy Keytis" + +typedef struct SubGhzProtocolDecoderSomfyKeytis SubGhzProtocolDecoderSomfyKeytis; +typedef struct SubGhzProtocolEncoderSomfyKeytis SubGhzProtocolEncoderSomfyKeytis; + +extern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder; +extern const SubGhzProtocol subghz_protocol_somfy_keytis; + +/** + * Allocate SubGhzProtocolEncoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyKeytis* pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_keytis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + */ +void subghz_protocol_encoder_somfy_keytis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSomfyKeytis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSomfyKeytis* pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void subghz_protocol_decoder_somfy_keytis_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + */ +void subghz_protocol_decoder_somfy_keytis_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_somfy_keytis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSomfyKeytis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance + * @param output Resulting text + */ +void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_telis.c b/applications/main/subghz/protocols/somfy_telis.c new file mode 100644 index 000000000..96997c581 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_telis.c @@ -0,0 +1,663 @@ +#include "somfy_telis.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolSomfyTelis" + +static const SubGhzBlockConst subghz_protocol_somfy_telis_const = { + .te_short = 640, + .te_long = 1280, + .te_delta = 250, + .min_count_bit_for_found = 56, +}; + +struct SubGhzProtocolDecoderSomfyTelis { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + ManchesterState manchester_saved_state; +}; + +struct SubGhzProtocolEncoderSomfyTelis { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + SomfyTelisDecoderStepReset = 0, + SomfyTelisDecoderStepCheckPreambula, + SomfyTelisDecoderStepFoundPreambula, + SomfyTelisDecoderStepStartDecode, + SomfyTelisDecoderStepDecoderData, +} SomfyTelisDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = { + .alloc = subghz_protocol_decoder_somfy_telis_alloc, + .free = subghz_protocol_decoder_somfy_telis_free, + + .feed = subghz_protocol_decoder_somfy_telis_feed, + .reset = subghz_protocol_decoder_somfy_telis_reset, + + .get_hash_data = subghz_protocol_decoder_somfy_telis_get_hash_data, + .serialize = subghz_protocol_decoder_somfy_telis_serialize, + .deserialize = subghz_protocol_decoder_somfy_telis_deserialize, + .get_string = subghz_protocol_decoder_somfy_telis_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = { + .alloc = subghz_protocol_encoder_somfy_telis_alloc, + .free = subghz_protocol_encoder_somfy_telis_free, + + .deserialize = subghz_protocol_encoder_somfy_telis_deserialize, + .stop = subghz_protocol_encoder_somfy_telis_stop, + .yield = subghz_protocol_encoder_somfy_telis_yield, +}; + +const SubGhzProtocol subghz_protocol_somfy_telis = { + .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_somfy_telis_decoder, + .encoder = &subghz_protocol_somfy_telis_encoder, +}; + +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); + + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + free(instance->encoder.upload); + free(instance); +} + +static bool + subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { + UNUSED(btn); + uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); + instance->generic.btn = (data >> 44) & 0xF; // ctrl + instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code + instance->generic.serial = data & 0xFFFFFF; // address + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t frame[7]; + frame[0] = data >> 48; + frame[1] = instance->generic.btn << 4; + frame[2] = instance->generic.cnt >> 8; + frame[3] = instance->generic.cnt; + frame[4] = instance->generic.serial >> 16; + frame[5] = instance->generic.serial >> 8; + frame[6] = instance->generic.serial; + + uint8_t checksum = 0; + for(uint8_t i = 0; i < 7; i++) { + checksum = checksum ^ frame[i] ^ (frame[i] >> 4); + } + checksum &= 0xF; + + frame[1] |= checksum; + + for(uint8_t i = 1; i < 7; i++) { + frame[i] ^= frame[i - 1]; + } + data = 0; + for(uint8_t i = 0; i < 7; ++i) { + data <<= 8; + data |= frame[i]; + } + instance->generic.data = data; + return true; +} + +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 56; + bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_somfy_telis_get_upload( + SubGhzProtocolEncoderSomfyTelis* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + //Send header + //Wake up + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 + //Hardware sync + for(uint8_t i = 0; i < 2; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + + //Retransmission + for(uint8_t i = 0; i < 2; i++) { + //Hardware sync + for(uint8_t i = 0; i < 7; ++i) { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 + } + //Software sync + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + + //Send key data MSB manchester + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration *= 2; // 00 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } else { + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + } + + } else { + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { + instance->encoder.upload[index - 1].duration *= 2; // 11 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } else { + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 + } + } + } + + //Inter-frame silence + if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { + instance->encoder.upload[index - 1].duration += (uint32_t)30415; + } else { + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); + } + } + + size_t size_upload = index; + + 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; + } + return true; +} + +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderSomfyTelis* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_somfy_telis_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_somfy_telis_stop(void* context) { + SubGhzProtocolEncoderSomfyTelis* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context) { + SubGhzProtocolEncoderSomfyTelis* 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_somfy_telis_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis)); + instance->base.protocol = &subghz_protocol_somfy_telis; + instance->generic.protocol_name = instance->base.protocol->name; + + return instance; +} + +void subghz_protocol_decoder_somfy_telis_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + free(instance); +} + +void subghz_protocol_decoder_somfy_telis_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +/** + * Сhecksum calculation. + * @param data Вata for checksum calculation + * @return CRC + */ +static uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} + +void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + + ManchesterEvent event = ManchesterEventReset; + switch(instance->decoder.parser_step) { + case SomfyTelisDecoderStepReset: + if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->header_count++; + } + break; + case SomfyTelisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyTelisDecoderStepCheckPreambula; + } else { + instance->header_count = 0; + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + break; + case SomfyTelisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < + subghz_protocol_somfy_telis_const.te_delta * 4) { + instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->header_count++; + } else if( + (instance->header_count > 1) && + (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 7) < + subghz_protocol_somfy_telis_const.te_delta * 4)) { + instance->decoder.parser_step = SomfyTelisDecoderStepDecoderData; + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + } + } + + break; + + case SomfyTelisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventShortLow; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventLongLow; + } else if( + duration >= (subghz_protocol_somfy_telis_const.te_long + + subghz_protocol_somfy_telis_const.te_delta)) { + if(instance->decoder.decode_count_bit == + subghz_protocol_somfy_telis_const.min_count_bit_for_found) { + //check crc + uint64_t data_tmp = instance->decoder.decode_data ^ + (instance->decoder.decode_data >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) { + 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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); + manchester_advance( + instance->manchester_saved_state, + ManchesterEventLongHigh, + &instance->manchester_saved_state, + NULL); + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } else { + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventShortHigh; + } else if( + DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < + subghz_protocol_somfy_telis_const.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->decoder.parser_step = SomfyTelisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; + instance->decoder.decode_count_bit++; + } + } + break; + } +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | 67648 us | 30415 us | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+-----...----- + * + * | hw. sync. | soft. | | Inter-frame + * | | sync. | data | gap + * + * + * encrypt | decrypt + * + * package 56 bit cnt key btn|crc cnt serial + * 0xA7232323312222 - 0 => A7 8 0 | 00 00 | 12 13 00 + * 0xA7222223312222 - 1 => A7 8 5 | 00 01 | 12 13 00 + * 0xA7212123312222 - 2 => A7 8 6 | 00 02 | 12 13 00 + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * Btn + * + * Value Button(s) Description + * 0x1 My Stop or move to favourite position + * 0x2 Up Move up + * 0x3 My + Up Set upper motor limit in initial programming mode + * 0x4 Down Move down + * 0x5 My + Down Set lower motor limit in initial programming mode + * 0x6 Up + Down Change motor limit and initial programming mode + * 0x8 Prog Used for (de-)registering remotes, see below + * 0x9 Sun + Flag Enable sun and wind detector (SUN and FLAG symbol on the Telis Soliris RC) + * 0xA Flag Disable sun detector (FLAG symbol on the Telis Soliris RC) + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * cksum = cksum ^ frame[i] ^ (frame[i] >> 4); + * } + * cksum = cksum & 0xf; + * + */ + + uint64_t data = instance->data ^ (instance->data >> 8); + instance->btn = (data >> 44) & 0xF; // ctrl + instance->cnt = (data >> 24) & 0xFFFF; // rolling code + instance->serial = data & 0xFFFFFF; // address +} + +/** + * Get button name. + * @param btn Button number, 4 bit + */ +static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { + const char* name_btn[16] = { + "Unknown", + "My", + "Up", + "My+Up", + "Down", + "My+Down", + "Up+Down", + "0x07", + "Prog", + "Sun+Flag", + "Flag", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_somfy_telis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + bool ret = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + break; + } + if(instance->generic.data_count_bit != + subghz_protocol_somfy_telis_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_somfy_telis_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderSomfyTelis* instance = context; + + subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04lX\r\n" + "Btn:%s\r\n", + + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data >> 32), + (uint32_t)instance->generic.data, + instance->generic.serial, + instance->generic.cnt, + subghz_protocol_somfy_telis_get_name_button(instance->generic.btn)); +} diff --git a/applications/main/subghz/protocols/somfy_telis.h b/applications/main/subghz/protocols/somfy_telis.h new file mode 100644 index 000000000..b5e989866 --- /dev/null +++ b/applications/main/subghz/protocols/somfy_telis.h @@ -0,0 +1,125 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME "Somfy Telis" + +typedef struct SubGhzProtocolDecoderSomfyTelis SubGhzProtocolDecoderSomfyTelis; +typedef struct SubGhzProtocolEncoderSomfyTelis SubGhzProtocolEncoderSomfyTelis; + +extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; +extern const SubGhzProtocol subghz_protocol_somfy_telis; + +/** + * Allocate SubGhzProtocolEncoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderSomfyTelis* pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_somfy_telis_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + */ +void subghz_protocol_encoder_somfy_telis_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderSomfyTelis. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderSomfyTelis* pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void subghz_protocol_decoder_somfy_telis_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + */ +void subghz_protocol_decoder_somfy_telis_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_somfy_telis_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderSomfyTelis. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance + * @param output Resulting text + */ +void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/star_line.c b/applications/main/subghz/protocols/star_line.c new file mode 100644 index 000000000..3066c6e2b --- /dev/null +++ b/applications/main/subghz/protocols/star_line.c @@ -0,0 +1,780 @@ +#include "star_line.h" +#include "keeloq_common.h" + +#include "../subghz_keystore.h" +#include + +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#define TAG "SubGhzProtocolStarLine" + +static const SubGhzBlockConst subghz_protocol_star_line_const = { + .te_short = 250, + .te_long = 500, + .te_delta = 120, + .min_count_bit_for_found = 64, +}; + +struct SubGhzProtocolDecoderStarLine { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; + + uint16_t header_count; + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +struct SubGhzProtocolEncoderStarLine { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; + + SubGhzKeystore* keystore; + const char* manufacture_name; + + FuriString* manufacture_from_file; +}; + +typedef enum { + StarLineDecoderStepReset = 0, + StarLineDecoderStepCheckPreambula, + StarLineDecoderStepSaveDuration, + StarLineDecoderStepCheckDuration, +} StarLineDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_star_line_decoder = { + .alloc = subghz_protocol_decoder_star_line_alloc, + .free = subghz_protocol_decoder_star_line_free, + + .feed = subghz_protocol_decoder_star_line_feed, + .reset = subghz_protocol_decoder_star_line_reset, + + .get_hash_data = subghz_protocol_decoder_star_line_get_hash_data, + .serialize = subghz_protocol_decoder_star_line_serialize, + .deserialize = subghz_protocol_decoder_star_line_deserialize, + .get_string = subghz_protocol_decoder_star_line_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_star_line_encoder = { + .alloc = subghz_protocol_encoder_star_line_alloc, + .free = subghz_protocol_encoder_star_line_free, + + .deserialize = subghz_protocol_encoder_star_line_deserialize, + .stop = subghz_protocol_encoder_star_line_stop, + .yield = subghz_protocol_encoder_star_line_yield, +}; + +const SubGhzProtocol subghz_protocol_star_line = { + .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, + .type = SubGhzProtocolTypeDynamic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_star_line_decoder, + .encoder = &subghz_protocol_star_line_encoder, +}; + +static const char* mfname; + +static int kl_type; + +void star_line_reset_mfname() { + mfname = ""; +} + +void star_line_reset_kl_type() { + kl_type = 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_star_line_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name); + +void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderStarLine* instance = malloc(sizeof(SubGhzProtocolEncoderStarLine)); + + instance->base.protocol = &subghz_protocol_star_line; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->manufacture_from_file = furi_string_alloc(); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_star_line_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + furi_string_free(instance->manufacture_from_file); + free(instance->encoder.upload); + free(instance); +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance + * @param btn Button number, 4 bit + */ +static bool + subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + uint32_t fix = btn << 24 | instance->generic.serial; + uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; + uint32_t hop = 0; + uint64_t man = 0; + uint64_t code_found_reverse; + int res = 0; + + if(instance->manufacture_name == 0x0) { + instance->manufacture_name = ""; + } + + if(strcmp(instance->manufacture_name, "Unknown") == 0) { + code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + hop = code_found_reverse & 0x00000000ffffffff; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + //Simple Learning + hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + break; + case KEELOQ_LEARNING_NORMAL: + //Normal Learning + man = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + break; + case KEELOQ_LEARNING_UNKNOWN: + if(kl_type == 1) { + hop = + subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); + } + if(kl_type == 2) { + man = subghz_protocol_keeloq_common_normal_learning( + fix, manufacture_code->key); + hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); + } + break; + } + break; + } + } + } + if(hop) { + uint64_t yek = (uint64_t)fix << 32 | hop; + instance->generic.data = + subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); + return true; + } else { + instance->manufacture_name = "Unknown"; + return false; + } +} + +bool subghz_protocol_star_line_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->manufacture_name = manufacture_name; + instance->generic.data_count_bit = 64; + bool res = subghz_protocol_star_line_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_star_line_get_upload( + SubGhzProtocolEncoderStarLine* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_star_line_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + size_t size_upload = 6 * 2 + (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; + } + + //Send header + for(uint8_t i = 6; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long * 2); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long * 2); + } + + //Send key data + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_short); + } + } + + return true; +} + +bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderStarLine* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); + } + + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_star_line_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_encoder_star_line_stop(void* context) { + SubGhzProtocolEncoderStarLine* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_star_line_yield(void* context) { + SubGhzProtocolEncoderStarLine* 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_star_line_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolDecoderStarLine* instance = malloc(sizeof(SubGhzProtocolDecoderStarLine)); + instance->base.protocol = &subghz_protocol_star_line; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->manufacture_from_file = furi_string_alloc(); + + instance->keystore = subghz_environment_get_keystore(environment); + + return instance; +} + +void subghz_protocol_decoder_star_line_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + furi_string_free(instance->manufacture_from_file); + + free(instance); +} + +void subghz_protocol_decoder_star_line_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + instance->decoder.parser_step = StarLineDecoderStepReset; + mfname = ""; + kl_type = 0; +} + +void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + + switch(instance->decoder.parser_step) { + case StarLineDecoderStepReset: + if(level) { + if(DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < + subghz_protocol_star_line_const.te_delta * 2) { + instance->decoder.parser_step = StarLineDecoderStepCheckPreambula; + instance->header_count++; + } else if(instance->header_count > 4) { + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.te_last = duration; + instance->decoder.parser_step = StarLineDecoderStepCheckDuration; + } + } else { + instance->header_count = 0; + } + break; + case StarLineDecoderStepCheckPreambula: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < + subghz_protocol_star_line_const.te_delta * 2)) { + //Found Preambula + instance->decoder.parser_step = StarLineDecoderStepReset; + } else { + instance->header_count = 0; + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + case StarLineDecoderStepSaveDuration: + if(level) { + if(duration >= (subghz_protocol_star_line_const.te_long + + subghz_protocol_star_line_const.te_delta)) { + instance->decoder.parser_step = StarLineDecoderStepReset; + if(instance->decoder.decode_count_bit >= + subghz_protocol_star_line_const.min_count_bit_for_found) { + if(instance->generic.data != instance->decoder.decode_data) { + 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.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->header_count = 0; + break; + } else { + instance->decoder.te_last = duration; + instance->decoder.parser_step = StarLineDecoderStepCheckDuration; + } + + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + case StarLineDecoderStepCheckDuration: + if(!level) { + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_short) < + subghz_protocol_star_line_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_short) < + subghz_protocol_star_line_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = StarLineDecoderStepSaveDuration; + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_long) < + subghz_protocol_star_line_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long) < + subghz_protocol_star_line_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = StarLineDecoderStepSaveDuration; + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + } else { + instance->decoder.parser_step = StarLineDecoderStepReset; + } + break; + } +} + +/** + * Validation of decrypt data. + * @param instance Pointer to a SubGhzBlockGeneric instance + * @param decrypt Decrypd data + * @param btn Button number, 4 bit + * @param end_serial decrement the last 10 bits of the serial number + * @return true On success + */ +static inline bool subghz_protocol_star_line_check_decrypt( + SubGhzBlockGeneric* instance, + uint32_t decrypt, + uint8_t btn, + uint32_t end_serial) { + furi_assert(instance); + if((decrypt >> 24 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x00FF) == end_serial)) { + instance->cnt = decrypt & 0x0000FFFF; + return true; + } + return false; +} + +/** + * Checking the accepted code against the database manafacture key + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param fix Fix part of the parcel + * @param hop Hop encrypted part of the parcel + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + * @return true on successful search + */ +static uint8_t subghz_protocol_star_line_check_remote_controller_selector( + SubGhzBlockGeneric* instance, + uint32_t fix, + uint32_t hop, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint16_t end_serial = (uint16_t)(fix & 0xFF); + uint8_t btn = (uint8_t)(fix >> 24); + uint32_t decrypt = 0; + uint64_t man_normal_learning; + int res = 0; + if(mfname == 0x0) { + mfname = ""; + } + + if(strcmp(mfname, "") == 0) { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + // Check for mirrored man + man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + break; + } + } + } else if(strcmp(mfname, "Unknown") == 0) { + return 1; + } else { + for + M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); + if(res == 0) { + switch(manufacture_code->type) { + case KEELOQ_LEARNING_SIMPLE: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_NORMAL: + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + return 1; + } + break; + case KEELOQ_LEARNING_UNKNOWN: + // Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + // Check for mirrored man + uint64_t man_rev = 0; + uint64_t man_rev_byte = 0; + for(uint8_t i = 0; i < 64; i += 8) { + man_rev_byte = (uint8_t)(manufacture_code->key >> i); + man_rev = man_rev | man_rev_byte << (56 - i); + } + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 1; + return 1; + } + //########################### + // Normal Learning + // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + + // Check for mirrored man + man_normal_learning = + subghz_protocol_keeloq_common_normal_learning(fix, man_rev); + decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); + if(subghz_protocol_star_line_check_decrypt( + instance, decrypt, btn, end_serial)) { + *manufacture_name = furi_string_get_cstr(manufacture_code->name); + mfname = *manufacture_name; + kl_type = 2; + return 1; + } + break; + } + } + } + } + + *manufacture_name = "Unknown"; + mfname = "Unknown"; + instance->cnt = 0; + + return 0; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + * @param keystore Pointer to a SubGhzKeystore* instance + * @param manufacture_name + */ +static void subghz_protocol_star_line_check_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore, + const char** manufacture_name) { + uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); + uint32_t key_fix = key >> 32; + uint32_t key_hop = key & 0x00000000ffffffff; + + subghz_protocol_star_line_check_remote_controller_selector( + instance, key_fix, key_hop, keystore, manufacture_name); + + instance->serial = key_fix & 0x00FFFFFF; + instance->btn = key_fix >> 24; +} + +uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +bool subghz_protocol_decoder_star_line_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + + if(res && !flipper_format_write_string_cstr( + flipper_format, "Manufacture", instance->manufacture_name)) { + FURI_LOG_E(TAG, "Unable to add manufacture name"); + res = false; + } + if(res && instance->generic.data_count_bit != + subghz_protocol_star_line_const.min_count_bit_for_found) { + FURI_LOG_E(TAG, "Wrong number of bits in key"); + res = false; + } + return res; +} + +bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + // Read manufacturer from file + if(flipper_format_read_string( + flipper_format, "Manufacture", instance->manufacture_from_file)) { + instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); + mfname = furi_string_get_cstr(instance->manufacture_from_file); + } else { + FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); + } + + res = true; + } while(false); + + return res; +} + +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderStarLine* instance = context; + + subghz_protocol_star_line_check_remote_controller( + &instance->generic, instance->keystore, &instance->manufacture_name); + + uint32_t code_found_hi = instance->generic.data >> 32; + uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; + + uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit); + uint32_t code_found_reverse_hi = code_found_reverse >> 32; + uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + + furi_string_cat_printf( + output, + "%s %dbit\r\n" + "Key:%08lX%08lX\r\n" + "Fix:0x%08lX Cnt:%04lX\r\n" + "Hop:0x%08lX Btn:%02X\r\n" + "MF:%s\r\n", + instance->generic.protocol_name, + instance->generic.data_count_bit, + code_found_hi, + code_found_lo, + code_found_reverse_hi, + instance->generic.cnt, + code_found_reverse_lo, + instance->generic.btn, + instance->manufacture_name); +} diff --git a/applications/main/subghz/protocols/star_line.h b/applications/main/subghz/protocols/star_line.h new file mode 100644 index 000000000..e8873d41a --- /dev/null +++ b/applications/main/subghz/protocols/star_line.h @@ -0,0 +1,131 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_STAR_LINE_NAME "Star Line" + +typedef struct SubGhzProtocolDecoderStarLine SubGhzProtocolDecoderStarLine; +typedef struct SubGhzProtocolEncoderStarLine SubGhzProtocolEncoderStarLine; + +extern const SubGhzProtocolDecoder subghz_protocol_star_line_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_star_line_encoder; +extern const SubGhzProtocol subghz_protocol_star_line; + +void star_line_reset_mfname(); + +void star_line_reset_kl_type(); + +/** + * Allocate SubGhzProtocolEncoderStarLine. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderStarLine* pointer to a SubGhzProtocolEncoderStarLine instance + */ +void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderStarLine. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + */ +void subghz_protocol_encoder_star_line_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param manufacture_name Name of manufacturer's key + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_star_line_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + const char* manufacture_name, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + */ +void subghz_protocol_encoder_star_line_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderStarLine instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_star_line_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderStarLine. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderStarLine* pointer to a SubGhzProtocolDecoderStarLine instance + */ +void* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + */ +void subghz_protocol_decoder_star_line_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + */ +void subghz_protocol_decoder_star_line_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_decoder_star_line_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderStarLine. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderStarLine instance + * @param output Resulting text + */ +void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/receiver.c b/applications/main/subghz/receiver.c new file mode 100644 index 000000000..698fe098e --- /dev/null +++ b/applications/main/subghz/receiver.c @@ -0,0 +1,124 @@ +#include "receiver.h" + +#include "registry.h" +#include "protocols/protocol_items.h" + +#include + +typedef struct { + SubGhzProtocolEncoderBase* base; +} SubGhzReceiverSlot; + +ARRAY_DEF(SubGhzReceiverSlotArray, SubGhzReceiverSlot, M_POD_OPLIST); +#define M_OPL_SubGhzReceiverSlotArray_t() ARRAY_OPLIST(SubGhzReceiverSlotArray, M_POD_OPLIST) + +struct SubGhzReceiver { + SubGhzReceiverSlotArray_t slots; + SubGhzProtocolFlag filter; + + SubGhzReceiverCallback callback; + void* context; +}; + +SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { + SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); + SubGhzReceiverSlotArray_init(instance->slots); + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_index(protocol_registry_items, i); + + if(protocol->decoder && protocol->decoder->alloc) { + SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); + slot->base = protocol->decoder->alloc(environment); + } + } + + instance->callback = NULL; + instance->context = NULL; + return instance; +} + +void subghz_receiver_free(SubGhzReceiver* instance) { + furi_assert(instance); + + instance->callback = NULL; + instance->context = NULL; + + // Release allocated slots + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + slot->base->protocol->decoder->free(slot->base); + slot->base = NULL; + } + SubGhzReceiverSlotArray_clear(instance->slots); + + free(instance); +} + +void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration) { + furi_assert(instance); + furi_assert(instance->slots); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + if((slot->base->protocol->flag & instance->filter) != 0) { + slot->base->protocol->decoder->feed(slot->base, level, duration); + } + } +} + +void subghz_receiver_reset(SubGhzReceiver* instance) { + furi_assert(instance); + furi_assert(instance->slots); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + slot->base->protocol->decoder->reset(slot->base); + } +} + +static void subghz_receiver_rx_callback(SubGhzProtocolDecoderBase* decoder_base, void* context) { + SubGhzReceiver* instance = context; + if(instance->callback) { + instance->callback(instance, decoder_base, instance->context); + } +} + +void subghz_receiver_set_rx_callback( + SubGhzReceiver* instance, + SubGhzReceiverCallback callback, + void* context) { + furi_assert(instance); + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + subghz_protocol_decoder_base_set_decoder_callback( + (SubGhzProtocolDecoderBase*)slot->base, subghz_receiver_rx_callback, instance); + } + + instance->callback = callback; + instance->context = context; +} + +void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter) { + furi_assert(instance); + instance->filter = filter; +} + +SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name( + SubGhzReceiver* instance, + const char* decoder_name) { + SubGhzProtocolDecoderBase* result = NULL; + + for + M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { + if(strcmp(slot->base->protocol->name, decoder_name) == 0) { + result = (SubGhzProtocolDecoderBase*)slot->base; + break; + } + } + return result; +} diff --git a/applications/main/subghz/receiver.h b/applications/main/subghz/receiver.h new file mode 100644 index 000000000..2ef722d1f --- /dev/null +++ b/applications/main/subghz/receiver.h @@ -0,0 +1,73 @@ +#pragma once + +#include "types.h" +#include "protocols/base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzReceiver SubGhzReceiver; + +typedef void (*SubGhzReceiverCallback)( + SubGhzReceiver* decoder, + SubGhzProtocolDecoderBase* decoder_base, + void* context); + +/** + * Allocate and init SubGhzReceiver. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzReceiver* pointer to a SubGhzReceiver instance + */ +SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment); + +/** + * Free SubGhzReceiver. + * @param instance Pointer to a SubGhzReceiver instance + */ +void subghz_receiver_free(SubGhzReceiver* instance); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param instance Pointer to a SubGhzReceiver instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration); + +/** + * Reset decoder SubGhzReceiver. + * @param instance Pointer to a SubGhzReceiver instance + */ +void subghz_receiver_reset(SubGhzReceiver* instance); + +/** + * Set a callback upon completion of successful decoding of one of the protocols. + * @param instance Pointer to a SubGhzReceiver instance + * @param callback Callback, SubGhzReceiverCallback + * @param context Context + */ +void subghz_receiver_set_rx_callback( + SubGhzReceiver* instance, + SubGhzReceiverCallback callback, + void* context); + +/** + * Set the filter of receivers that will work at the moment. + * @param instance Pointer to a SubGhzReceiver instance + * @param filter Filter, SubGhzProtocolFlag + */ +void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter); + +/** + * Search for a cattery by his name. + * @param instance Pointer to a SubGhzReceiver instance + * @param decoder_name Receiver name + * @return SubGhzProtocolDecoderBase* pointer to a SubGhzProtocolDecoderBase instance + */ +SubGhzProtocolDecoderBase* + subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/registry.c b/applications/main/subghz/registry.c new file mode 100644 index 000000000..d0c22ea8c --- /dev/null +++ b/applications/main/subghz/registry.c @@ -0,0 +1,30 @@ +#include "registry.h" + +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name) { + furi_assert(protocol_registry); + + for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { + if(strcmp(name, protocol_registry->items[i]->name) == 0) { + return protocol_registry->items[i]; + } + } + return NULL; +} + +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index) { + furi_assert(protocol_registry); + if(index < subghz_protocol_registry_count(protocol_registry)) { + return protocol_registry->items[index]; + } else { + return NULL; + } +} + +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { + furi_assert(protocol_registry); + return protocol_registry->size; +} diff --git a/applications/main/subghz/registry.h b/applications/main/subghz/registry.h new file mode 100644 index 000000000..91027807e --- /dev/null +++ b/applications/main/subghz/registry.h @@ -0,0 +1,47 @@ +#pragma once + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzEnvironment SubGhzEnvironment; + +typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; + +struct SubGhzProtocolRegistry { + const SubGhzProtocol** items; + const size_t size; +}; + +/** + * Registration by name SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param name Protocol name + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_name( + const SubGhzProtocolRegistry* protocol_registry, + const char* name); + +/** + * Registration protocol by index in array SubGhzProtocol. + * @param protocol_registry SubGhzProtocolRegistry + * @param index Protocol by index in array + * @return SubGhzProtocol* pointer to a SubGhzProtocol instance + */ +const SubGhzProtocol* subghz_protocol_registry_get_by_index( + const SubGhzProtocolRegistry* protocol_registry, + size_t index); + +/** + * Getting the number of registered protocols. + * @param protocol_registry SubGhzProtocolRegistry + * @return Number of protocols + */ +size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/scenes/subghz_scene_config.h b/applications/main/subghz/scenes/subghz_scene_config.h index 1ad41a8b5..5acd534dc 100644 --- a/applications/main/subghz/scenes/subghz_scene_config.h +++ b/applications/main/subghz/scenes/subghz_scene_config.h @@ -26,6 +26,7 @@ ADD_SCENE(subghz, set_fix_bft, SetFixBft) ADD_SCENE(subghz, set_cnt_bft, SetCntBft) ADD_SCENE(subghz, set_seed_bft, SetSeedBft) ADD_SCENE(subghz, frequency_analyzer, FrequencyAnalyzer) +ADD_SCENE(subghz, ext_module_settings, ExtModuleSettings) ADD_SCENE(subghz, read_raw, ReadRAW) ADD_SCENE(subghz, more_raw, MoreRAW) ADD_SCENE(subghz, decode_raw, DecodeRAW) diff --git a/applications/main/subghz/scenes/subghz_scene_decode_raw.c b/applications/main/subghz/scenes/subghz_scene_decode_raw.c index 6194d0dba..5746efbed 100644 --- a/applications/main/subghz/scenes/subghz_scene_decode_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_decode_raw.c @@ -170,11 +170,6 @@ void subghz_scene_decode_raw_on_enter(void* context) { subghz_receiver_set_rx_callback( subghz->txrx->receiver, subghz_scene_add_to_history_callback, subghz); - // make sure we're not in auto-detect mode, which is only meant for the Read app - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - false); subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); if(subghz->decode_raw_state == SubGhzDecodeRawStateStart) { diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index 4f00df70b..de17d7a86 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -32,7 +32,6 @@ bool subghz_scene_delete_success_on_event(void* context, SceneManagerEvent event scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); } else if(scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneSaved)) { - //scene_manager_next_scene(subghz->scene_manager, SubGhzSceneSaved); } else { scene_manager_search_and_switch_to_previous_scene( subghz->scene_manager, SubGhzSceneStart); diff --git a/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c new file mode 100644 index 000000000..7d7a505cb --- /dev/null +++ b/applications/main/subghz/scenes/subghz_scene_ext_module_settings.c @@ -0,0 +1,92 @@ +#include "../subghz_i.h" +#include "../helpers/subghz_custom_event.h" + +uint8_t value_index; +uint8_t value_index2; + +#define EXT_MODULES_COUNT (sizeof(radio_modules_variables_text) / sizeof(char* const)) +const char* const radio_modules_variables_text[] = { + "Internal", + "External", +}; + +#define DEBUG_P_COUNT 2 +const char* const debug_pin_text[DEBUG_P_COUNT] = { + "OFF", + "A7", +}; + +static void subghz_scene_ext_module_changed(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + value_index = variable_item_get_current_value_index(item); + UNUSED(subghz); + + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); +} +static void subghz_ext_module_start_var_list_enter_callback(void* context, uint32_t index) { + SubGhz* subghz = context; + view_dispatcher_send_custom_event(subghz->view_dispatcher, index); +} + +static void subghz_scene_receiver_config_set_debug_pin(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, debug_pin_text[index]); + + subghz->txrx->debug_pin_state = index == 1; +} + +void subghz_scene_ext_module_settings_on_enter(void* context) { + SubGhz* subghz = context; + + VariableItemList* variable_item_list = subghz->variable_item_list; + + value_index = furi_hal_subghz.radio_type; + VariableItem* item = variable_item_list_add( + variable_item_list, "Module", EXT_MODULES_COUNT, subghz_scene_ext_module_changed, subghz); + + variable_item_list_set_enter_callback( + variable_item_list, subghz_ext_module_start_var_list_enter_callback, subghz); + + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, radio_modules_variables_text[value_index]); + + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + item = variable_item_list_add( + subghz->variable_item_list, + "Debug Pin:", + DEBUG_P_COUNT, + subghz_scene_receiver_config_set_debug_pin, + subghz); + value_index2 = subghz->txrx->debug_pin_state; + variable_item_set_current_value_index(item, value_index2); + variable_item_set_current_value_text(item, debug_pin_text[value_index2]); + } + + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); +} + +bool subghz_scene_ext_module_settings_on_event(void* context, SceneManagerEvent event) { + SubGhz* subghz = context; + UNUSED(subghz); + UNUSED(event); + + // Set selected radio module + furi_hal_subghz_set_radio_type(value_index); + + // Check if module is present, if no -> show error + if(!furi_hal_subghz_check_radio()) { + value_index = 0; + furi_hal_subghz_set_radio_type(value_index); + furi_string_set(subghz->error_str, "Please connect\nexternal radio"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + } + + return false; +} + +void subghz_scene_ext_module_settings_on_exit(void* context) { + SubGhz* subghz = context; + variable_item_list_reset(subghz->variable_item_list); +} diff --git a/applications/main/subghz/scenes/subghz_scene_read_raw.c b/applications/main/subghz/scenes/subghz_scene_read_raw.c index fea4b6aef..e396527cc 100644 --- a/applications/main/subghz/scenes/subghz_scene_read_raw.c +++ b/applications/main/subghz/scenes/subghz_scene_read_raw.c @@ -119,9 +119,6 @@ void subghz_scene_read_raw_on_enter(void* context) { subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME); furi_assert(subghz->txrx->decoder_result); - // make sure we're not in auto-detect mode, which is only meant for the Read app - subghz_protocol_decoder_raw_set_auto_mode(subghz->txrx->decoder_result, false); - //set filter RAW feed subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_RAW); view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReadRAW); @@ -422,10 +419,6 @@ void subghz_scene_read_raw_on_exit(void* context) { subghz->state_notifications = SubGhzNotificationStateIDLE; notification_message(subghz->notifications, &sequence_reset_rgb); -//filter restoration -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif + //filter restoration + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); } \ No newline at end of file diff --git a/applications/main/subghz/scenes/subghz_scene_receiver.c b/applications/main/subghz/scenes/subghz_scene_receiver.c index cab19f730..e1ea08497 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver.c @@ -1,6 +1,7 @@ #include "../subghz_i.h" #include "../views/receiver.h" #include +#include #define TAG "SubGhzSceneReceiver" @@ -53,7 +54,10 @@ static void subghz_scene_receiver_update_statusbar(void* context) { } else { subghz_get_frequency_modulation(subghz, frequency_str, NULL); furi_string_printf( - modulation_str, "Mod: %s", furi_string_get_cstr(subghz->txrx->preset->name)); + modulation_str, + "%s Mod: %s", + furi_hal_subghz_get_radio_type() ? "Ext" : "Int", + furi_string_get_cstr(subghz->txrx->preset->name)); } #else subghz_get_frequency_modulation(subghz, frequency_str, modulation_str); @@ -157,6 +161,11 @@ void subghz_scene_receiver_on_enter(void* context) { } subghz_view_receiver_set_idx_menu(subghz->subghz_receiver, subghz->txrx->idx_menu_chosen); + //to use a universal decoder, we are looking for a link to it + subghz->txrx->decoder_result = subghz_receiver_search_decoder_base_by_name( + subghz->txrx->receiver, SUBGHZ_PROTOCOL_BIN_RAW_NAME); + furi_assert(subghz->txrx->decoder_result); + view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdReceiver); } @@ -173,7 +182,6 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_sleep(subghz); } subghz->txrx->hopper_state = SubGhzHopperStateOFF; - subghz_history_set_hopper_state(subghz->txrx->history, false); subghz->txrx->idx_menu_chosen = 0; subghz_receiver_set_rx_callback(subghz->txrx->receiver, NULL, subghz); @@ -221,6 +229,13 @@ bool subghz_scene_receiver_on_event(void* context, SceneManagerEvent event) { subghz_hopper_update(subghz); subghz_scene_receiver_update_statusbar(subghz); } + + //get RSSI + float rssi = furi_hal_subghz_get_rssi(); + subghz_receiver_rssi(subghz->subghz_receiver, rssi); + subghz_protocol_decoder_bin_raw_data_input_rssi( + (SubGhzProtocolDecoderBinRAW*)subghz->txrx->decoder_result, rssi); + switch(subghz->state_notifications) { case SubGhzNotificationStateRx: notification_message(subghz->notifications, &sequence_blink_cyan_10); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_config.c b/applications/main/subghz/scenes/subghz_scene_receiver_config.c index c23d93496..af4c6aca3 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_config.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_config.c @@ -1,16 +1,11 @@ #include "../subghz_i.h" #include -#include - -#define TAG "SubGhzSceneReceiverConfig" - enum SubGhzSettingIndex { SubGhzSettingIndexFrequency, SubGhzSettingIndexHopping, SubGhzSettingIndexModulation, - SubGhzSettingIndexDetectRaw, - SubGhzSettingIndexRSSIThreshold, + SubGhzSettingIndexBinRAW, SubGhzSettingIndexSound, SubGhzSettingIndexLock, SubGhzSettingIndexRAWThesholdRSSI, @@ -45,35 +40,6 @@ const float raw_theshold_rssi_value[RAW_THRESHOLD_RSSI_COUNT] = { -40.0f, }; -#define BANDWIDTH_COUNT 16 -const char* const bandwidth_labels[BANDWIDTH_COUNT] = { - "58 kHz", - "68 kHz", - "81 kHz", - "102 kHz", - "116 kHz", - "135 kHz", - "162 kHz", - "203 kHz", - "232 kHz", - "270 kHz", - "325 kHz", - "406 kHz", - "464 kHz", - "541 kHz", - "650 kHz", - "812 kHz", -}; - -// Bandwidths values are ordered from F (58kHz) to 0 (812kHz) -#define BANDWIDTH_INDEX(value) ((uint8_t)15 - ((uint8_t)(value >> 4) & 0x0F)) - -#define MANCHESTER_FLAG_COUNT 2 -const char* const manchester_flag_text[MANCHESTER_FLAG_COUNT] = { - "OFF", - "ON", -}; - #define HOPPING_COUNT 2 const char* const hopping_text[HOPPING_COUNT] = { "OFF", @@ -84,40 +50,6 @@ const uint32_t hopping_value[HOPPING_COUNT] = { SubGhzHopperStateRunnig, }; -#define DETECT_RAW_COUNT 2 -const char* const detect_raw_text[DETECT_RAW_COUNT] = { - "OFF", - "ON", -}; - -#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING -const SubGhzProtocolFlag detect_raw_value[DETECT_RAW_COUNT] = { - SubGhzProtocolFlag_Decodable, - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW, -}; -#endif - -#define RSSI_THRESHOLD_COUNT 7 -const char* const rssi_threshold_text[RSSI_THRESHOLD_COUNT] = { - "-72db", - "-67db", - "-62db", - "-57db", - "-52db", - "-47db", - "-42db", -}; - -const int rssi_threshold_value[RSSI_THRESHOLD_COUNT] = { - -72, - -67, - -62, - -57, - -52, - -47, - -42, -}; - #define SPEAKER_COUNT 2 const char* const speaker_text[SPEAKER_COUNT] = { "OFF", @@ -127,18 +59,15 @@ const uint32_t speaker_value[SPEAKER_COUNT] = { SubGhzSpeakerStateShutdown, SubGhzSpeakerStateEnable, }; - -// Allow advanced edit only on specific preset -bool subghz_scene_receiver_config_can_edit_current_preset(SubGhz* subghz) { - SubGhzRadioPreset* preset = subghz->txrx->preset; - - bool preset_name_allow_edit = - !strcmp(furi_string_get_cstr(preset->name), ADVANCED_AM_PRESET_NAME) || - !strcmp(furi_string_get_cstr(preset->name), "CUSTOM"); - - return preset && preset_name_allow_edit && - subghz_preset_custom_is_ook_modulation(preset->data, preset->data_size); -} +#define BIN_RAW_COUNT 2 +const char* const bin_raw_text[BIN_RAW_COUNT] = { + "OFF", + "ON", +}; +const uint32_t bin_raw_value[BIN_RAW_COUNT] = { + SubGhzProtocolFlag_Decodable, + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_BinRAW, +}; uint8_t subghz_scene_receiver_config_next_frequency(const uint32_t value, void* context) { furi_assert(context); @@ -170,52 +99,6 @@ uint8_t subghz_scene_receiver_config_next_preset(const char* preset_name, void* return index; } -// Advanced settings of preset may change if preset was changed. -// In that case - update values -static void subghz_scene_receiver_config_update_advanced(SubGhz* subghz) { - uint8_t value_index; - - if(subghz->variable_item_bandwidth) { - value_index = BANDWIDTH_INDEX(subghz->txrx->raw_bandwidth); - variable_item_set_current_value_index(subghz->variable_item_bandwidth, value_index); - variable_item_set_current_value_text( - subghz->variable_item_bandwidth, bandwidth_labels[value_index]); - } - - if(subghz->variable_item_datarate) { - variable_item_set_current_value_index(subghz->variable_item_datarate, 0); - - char datarate_str[16] = {0}; - subghz_preset_custom_printf_datarate( - subghz->txrx->raw_datarate, datarate_str, sizeof(datarate_str)); - variable_item_set_current_value_text(subghz->variable_item_datarate, datarate_str); - } - - if(subghz->variable_item_manchester) { - value_index = subghz->txrx->raw_manchester_enabled ? 1 : 0; - - variable_item_set_current_value_index(subghz->variable_item_manchester, value_index); - variable_item_set_current_value_text( - subghz->variable_item_manchester, manchester_flag_text[value_index]); - } -} - -// Apply advanced configuration to advanced am preset -static void subghz_scene_receiver_config_apply_advanced(SubGhz* subghz) { - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - SubGhzRadioPreset* preset = subghz->txrx->preset; - - subghz_preset_custom_set_bandwidth( - preset->data, preset->data_size, subghz->txrx->raw_bandwidth); - - subghz_preset_custom_set_machester_enable( - preset->data, preset->data_size, subghz->txrx->raw_manchester_enabled); - - subghz_preset_custom_set_datarate( - preset->data, preset->data_size, subghz->txrx->raw_datarate); - } -} - uint8_t subghz_scene_receiver_config_hopper_value_index( const uint32_t value, const uint32_t values[], @@ -236,36 +119,6 @@ uint8_t subghz_scene_receiver_config_hopper_value_index( } } -#ifndef SUBGHZ_SAVE_DETECT_RAW_SETTING -uint8_t subghz_scene_receiver_config_detect_raw_value_index( - const SubGhzProtocolFlag value, - const SubGhzProtocolFlag values[], - uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if(value == values[i]) { - index = i; - break; - } - } - return index; -} -#endif - -uint8_t subghz_scene_receiver_config_rssi_threshold_value_index( - const int value, - const int values[], - uint8_t values_count) { - uint8_t index = 0; - for(uint8_t i = 0; i < values_count; i++) { - if(value == values[i]) { - index = i; - break; - } - } - return index; -} - static void subghz_scene_receiver_config_set_frequency(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -301,49 +154,12 @@ static void subghz_scene_receiver_config_set_preset(VariableItem* item) { subghz->txrx->preset->frequency, subghz_setting_get_preset_data(subghz->setting, index), subghz_setting_get_preset_data_size(subghz->setting, index)); - - subghz_scene_receiver_config_update_advanced(subghz); -} - -static void subghz_scene_receiver_config_set_rssi_threshold(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - variable_item_set_current_value_text(item, rssi_threshold_text[index]); - subghz_protocol_decoder_raw_set_rssi_threshold( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - rssi_threshold_value[index]); -} - -static void subghz_scene_receiver_config_set_detect_raw(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - uint8_t index = variable_item_get_current_value_index(item); - - //if(subghz->txrx->hopper_state == 0) { - variable_item_set_current_value_text(item, detect_raw_text[index]); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz->last_settings->detect_raw = index; - - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, detect_raw_value[index]); - - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - (index == 1)); -#endif - /*} else { - variable_item_set_current_value_index(item, 0); - }*/ } static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - //if(subghz_receiver_get_filter(subghz->txrx->receiver) == SubGhzProtocolFlag_Decodable) { variable_item_set_current_value_text(item, hopping_text[index]); if(hopping_value[index] == SubGhzHopperStateOFF) { char text_buf[10] = {0}; @@ -374,10 +190,6 @@ static void subghz_scene_receiver_config_set_hopping_running(VariableItem* item) } subghz->txrx->hopper_state = hopping_value[index]; - subghz_history_set_hopper_state(subghz->txrx->history, (index == 1)); - /*} else { - variable_item_set_current_value_index(item, 0); - }*/ } static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { @@ -388,6 +200,15 @@ static void subghz_scene_receiver_config_set_speaker(VariableItem* item) { subghz->txrx->speaker_state = speaker_value[index]; } +static void subghz_scene_receiver_config_set_bin_raw(VariableItem* item) { + SubGhz* subghz = variable_item_get_context(item); + uint8_t index = variable_item_get_current_value_index(item); + + variable_item_set_current_value_text(item, bin_raw_text[index]); + subghz->txrx->filter = bin_raw_value[index]; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); +} + static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* item) { SubGhz* subghz = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -396,107 +217,6 @@ static void subghz_scene_receiver_config_set_raw_threshold_rssi(VariableItem* it subghz->txrx->raw_threshold_rssi = raw_theshold_rssi_value[index]; } -static void subghz_scene_receiver_config_set_raw_ook_bandwidth(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // update bandwidth value from selected index - uint8_t index = variable_item_get_current_value_index(item); - subghz->txrx->raw_bandwidth = subghz_preset_custom_bandwidth_values[index]; - - subghz_scene_receiver_config_update_advanced(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - -static void subghz_scene_receiver_config_set_manchester_flag(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // update enable flag from view - uint8_t index = variable_item_get_current_value_index(item); - subghz->txrx->raw_manchester_enabled = index == 0 ? false : true; - - subghz_scene_receiver_config_update_advanced(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - -static void subghz_scene_receiver_config_datarate_input_callback(void* context) { - furi_assert(context); - SubGhz* subghz = context; - - float value = atoff(subghz->datarate_input_str); - if(value != 0 && value > 0) { - subghz->txrx->raw_datarate = value; - subghz_scene_receiver_config_update_advanced(subghz); - } - - // show list view - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); -} - -static bool subghz_scene_receiver_config_datarate_input_validate( - const char* text, - FuriString* error, - void* context) { - UNUSED(context); - - float value = atoff(text); - if(value == 0) { - furi_string_printf(error, "Cannot parse\r\nvalue"); - } else if(value < 0) { - furi_string_printf(error, "Value\r\nshould be\r\ngreater\r\nthan 0"); - } else { - return true; - } - - return false; -} - -static void subghz_scene_receiver_config_show_datarate_input(SubGhz* subghz) { - TextInput* text_input = subghz->text_input; - - snprintf( - subghz->datarate_input_str, - sizeof(subghz->datarate_input_str), - "%.2f", - (double)subghz->txrx->raw_datarate); - - text_input_set_header_text(text_input, "Datarate bauds (not kBauds)"); - text_input_set_result_callback( - text_input, - subghz_scene_receiver_config_datarate_input_callback, - subghz, - subghz->datarate_input_str, - sizeof(subghz->datarate_input_str), - false); - - text_input_set_validator( - text_input, subghz_scene_receiver_config_datarate_input_validate, NULL); - view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdTextInput); -} - -static void subghz_scene_receiver_config_set_datarate(VariableItem* item) { - SubGhz* subghz = variable_item_get_context(item); - if(subghz_scene_receiver_config_can_edit_current_preset(subghz)) { - // reset value index in order to show '>' symbol always - variable_item_set_current_value_index(item, 0); - subghz_scene_receiver_config_show_datarate_input(subghz); - } else { - furi_string_set( - subghz->error_str, "Read-only\nsetting!\nUse '" ADVANCED_AM_PRESET_NAME "'\npreset."); - view_dispatcher_send_custom_event( - subghz->view_dispatcher, SubGhzCustomEventSceneSettingError); - } -} - static void subghz_scene_receiver_config_var_list_enter_callback(void* context, uint32_t index) { furi_assert(context); SubGhz* subghz = context; @@ -511,13 +231,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { VariableItem* item; uint8_t value_index; -#ifdef FURI_DEBUG - FURI_LOG_D( - TAG, - "Last frequency: %ld, Preset: %ld", - subghz->last_settings->frequency, - subghz->last_settings->preset); -#endif item = variable_item_list_add( subghz->variable_item_list, "Frequency:", @@ -563,52 +276,35 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->txrx->hopper_state, hopping_value, HOPPING_COUNT, subghz); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, hopping_text[value_index]); + } - // Detect Raw + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { item = variable_item_list_add( subghz->variable_item_list, - "Detect Raw:", - DETECT_RAW_COUNT, - subghz_scene_receiver_config_set_detect_raw, + "Bin RAW:", + BIN_RAW_COUNT, + subghz_scene_receiver_config_set_bin_raw, subghz); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - value_index = subghz->last_settings->detect_raw; -#else - value_index = subghz_scene_receiver_config_detect_raw_value_index( - subghz_receiver_get_filter(subghz->txrx->receiver), - detect_raw_value, - DETECT_RAW_COUNT); -#endif + value_index = value_index_uint32(subghz->txrx->filter, bin_raw_value, BIN_RAW_COUNT); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, detect_raw_text[value_index]); + variable_item_set_current_value_text(item, bin_raw_text[value_index]); + } - // RSSI - item = variable_item_list_add( - subghz->variable_item_list, - "RSSI for Raw:", - RSSI_THRESHOLD_COUNT, - subghz_scene_receiver_config_set_rssi_threshold, - subghz); - value_index = subghz_scene_receiver_config_rssi_threshold_value_index( - subghz_protocol_encoder_get_rssi_threshold(subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME)), - rssi_threshold_value, - RSSI_THRESHOLD_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, rssi_threshold_text[value_index]); + // Enable speaker, will send all incoming noises and signals to speaker so you can listen how your remote sounds like :) + item = variable_item_list_add( + subghz->variable_item_list, + "Sound:", + SPEAKER_COUNT, + subghz_scene_receiver_config_set_speaker, + subghz); + value_index = value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); + variable_item_set_current_value_index(item, value_index); + variable_item_set_current_value_text(item, speaker_text[value_index]); + if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneReadRAW) != + SubGhzCustomEventManagerSet) { // Lock keyboard - item = variable_item_list_add( - subghz->variable_item_list, - "Sound:", - SPEAKER_COUNT, - subghz_scene_receiver_config_set_speaker, - subghz); - value_index = - value_index_uint32(subghz->txrx->speaker_state, speaker_value, SPEAKER_COUNT); - variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, speaker_text[value_index]); - variable_item_list_add(subghz->variable_item_list, "Lock Keyboard", 1, NULL, NULL); variable_item_list_set_enter_callback( subghz->variable_item_list, @@ -627,33 +323,6 @@ void subghz_scene_receiver_config_on_enter(void* context) { subghz->txrx->raw_threshold_rssi, raw_theshold_rssi_value, RAW_THRESHOLD_RSSI_COUNT); variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, raw_theshold_rssi_text[value_index]); - - // Advanced MODEM settings. RW only for ADVANCED_AM_PRESET_NAME - // Bandwidth - subghz->variable_item_bandwidth = variable_item_list_add( - subghz->variable_item_list, - "Bandwidth:", - BANDWIDTH_COUNT, - subghz_scene_receiver_config_set_raw_ook_bandwidth, - subghz); - - // Data rate (editable via OK click) - subghz->variable_item_datarate = variable_item_list_add( - subghz->variable_item_list, - "Data rate:", - 2, - subghz_scene_receiver_config_set_datarate, - subghz); - - // Manchester codec flag - subghz->variable_item_manchester = variable_item_list_add( - subghz->variable_item_list, - "Manch. Enc.:", - MANCHESTER_FLAG_COUNT, - subghz_scene_receiver_config_set_manchester_flag, - subghz); - - subghz_scene_receiver_config_update_advanced(subghz); } view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewIdVariableItemList); } @@ -667,11 +336,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even subghz->lock = SubGhzLockOn; scene_manager_previous_scene(subghz->scene_manager); consumed = true; - } else if(event.event == SubGhzCustomEventSceneSettingError) { - scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneShowErrorSub, event.event); - consumed = true; } } return consumed; @@ -679,16 +343,6 @@ bool subghz_scene_receiver_config_on_event(void* context, SceneManagerEvent even void subghz_scene_receiver_config_on_exit(void* context) { SubGhz* subghz = context; - - // reset UI variable list items (next scene may be not RAW config) - subghz->variable_item_bandwidth = NULL; - subghz->variable_item_datarate = NULL; - subghz->variable_item_manchester = NULL; - text_input_set_validator(subghz->text_input, NULL, NULL); - - // apply advanced preset variables (if applicable) - subghz_scene_receiver_config_apply_advanced(subghz); - variable_item_list_set_selected_item(subghz->variable_item_list, 0); variable_item_list_reset(subghz->variable_item_list); subghz_last_settings_save(subghz->last_settings); diff --git a/applications/main/subghz/scenes/subghz_scene_receiver_info.c b/applications/main/subghz/scenes/subghz_scene_receiver_info.c index e9c849e1e..0b265cca2 100644 --- a/applications/main/subghz/scenes/subghz_scene_receiver_info.c +++ b/applications/main/subghz/scenes/subghz_scene_receiver_info.c @@ -92,8 +92,6 @@ void subghz_scene_receiver_info_draw_widget(SubGhz* subghz) { // Removed static check if(((subghz->txrx->decoder_result->protocol->flag & SubGhzProtocolFlag_Send) == SubGhzProtocolFlag_Send) && - // disable "Send" for auto-captured RAW signals for now. They can still be saved and sent by loading them. - subghz->txrx->decoder_result->protocol->type != SubGhzProtocolTypeRAW && subghz->txrx->decoder_result->protocol->encoder->deserialize) { widget_add_button_element( subghz->widget, @@ -138,6 +136,21 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event) subghz_history_get_raw_data( subghz->txrx->history, subghz->txrx->idx_menu_chosen))) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowOnlyRx); + if(subghz->txrx->txrx_state == SubGhzTxRxStateTx) { + subghz_tx_stop(subghz); + } + if(subghz->txrx->txrx_state == SubGhzTxRxStateIDLE) { + subghz_begin( + subghz, + subghz_setting_get_preset_data_by_name( + subghz->setting, + furi_string_get_cstr(subghz->txrx->preset->name))); + subghz_rx(subghz, subghz->txrx->preset->frequency); + } + if(subghz->txrx->hopper_state == SubGhzHopperStatePause) { + subghz->txrx->hopper_state = SubGhzHopperStateRunnig; + } + subghz->state_notifications = SubGhzNotificationStateRx; } else { subghz->state_notifications = SubGhzNotificationStateTx; } diff --git a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c index 2943c764a..113e7ae74 100644 --- a/applications/main/subghz/scenes/subghz_scene_show_error_sub.c +++ b/applications/main/subghz/scenes/subghz_scene_show_error_sub.c @@ -26,16 +26,8 @@ bool subghz_scene_show_error_sub_on_event(void* context, SceneManagerEvent event SubGhz* subghz = context; if(event.type == SceneManagerEventTypeCustom) { if(event.event == SubGhzCustomEventSceneShowErrorSub) { - if(scene_manager_get_scene_state(subghz->scene_manager, SubGhzSceneShowErrorSub) == - SubGhzCustomEventSceneSettingError) { - scene_manager_set_scene_state( - subghz->scene_manager, SubGhzSceneReadRAW, SubGhzCustomEventManagerSet); - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneReceiverConfig); - } else { - scene_manager_search_and_switch_to_previous_scene( - subghz->scene_manager, SubGhzSceneStart); - } + scene_manager_search_and_switch_to_previous_scene( + subghz->scene_manager, SubGhzSceneStart); return true; } } diff --git a/applications/main/subghz/scenes/subghz_scene_start.c b/applications/main/subghz/scenes/subghz_scene_start.c index 62e30784f..69e6cbea7 100644 --- a/applications/main/subghz/scenes/subghz_scene_start.c +++ b/applications/main/subghz/scenes/subghz_scene_start.c @@ -10,27 +10,9 @@ enum SubmenuIndex { SubmenuIndexAddManually, SubmenuIndexFrequencyAnalyzer, SubmenuIndexReadRAW, + SubmenuIndexExtSettings, }; -void subghz_scene_start_remove_advanced_preset(SubGhz* subghz) { - // delete operation is harmless - subghz_setting_delete_custom_preset(subghz->setting, ADVANCED_AM_PRESET_NAME); -} - -void subghz_scene_start_load_advanced_preset(SubGhz* subghz) { - for(uint8_t i = 0; i < subghz_setting_get_preset_count(subghz->setting); i++) { - if(!strcmp(subghz_setting_get_preset_name(subghz->setting, i), ADVANCED_AM_PRESET_NAME)) { - return; // already exists - } - } - - // Load custom advanced AM preset with configurable CFGMDM settings - FlipperFormat* advanced_am_preset = subghz_preset_custom_advanced_am_preset_alloc(); - subghz_setting_load_custom_preset( - subghz->setting, ADVANCED_AM_PRESET_NAME, advanced_am_preset); - flipper_format_free(advanced_am_preset); -} - void subghz_scene_start_submenu_callback(void* context, uint32_t index) { SubGhz* subghz = context; view_dispatcher_send_custom_event(subghz->view_dispatcher, index); @@ -41,15 +23,6 @@ void subghz_scene_start_on_enter(void* context) { if(subghz->state_notifications == SubGhzNotificationStateStarting) { subghz->state_notifications = SubGhzNotificationStateIDLE; } -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - subghz->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - false); - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif submenu_add_item( subghz->submenu, "Read", SubmenuIndexRead, subghz_scene_start_submenu_callback, subghz); @@ -73,6 +46,12 @@ void subghz_scene_start_on_enter(void* context) { SubmenuIndexFrequencyAnalyzer, subghz_scene_start_submenu_callback, subghz); + submenu_add_item( + subghz->submenu, + "Radio Settings", + SubmenuIndexExtSettings, + subghz_scene_start_submenu_callback, + subghz); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { submenu_add_item( subghz->submenu, "Test", SubmenuIndexTest, subghz_scene_start_submenu_callback, subghz); @@ -91,15 +70,23 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { view_dispatcher_stop(subghz->view_dispatcher); return true; } else if(event.type == SceneManagerEventTypeCustom) { - if(event.event == SubmenuIndexReadRAW) { - subghz_scene_start_load_advanced_preset(subghz); + if(event.event == SubmenuIndexExtSettings) { + scene_manager_set_scene_state( + subghz->scene_manager, SubGhzSceneStart, SubmenuIndexExtSettings); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneExtModuleSettings); + return true; + + } else if(!furi_hal_subghz_check_radio()) { + furi_string_set(subghz->error_str, "Please connect\nexternal radio"); + scene_manager_next_scene(subghz->scene_manager, SubGhzSceneShowErrorSub); + return true; + } else if(event.event == SubmenuIndexReadRAW) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexReadRAW); subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReadRAW); return true; } else if(event.event == SubmenuIndexRead) { - subghz_scene_start_remove_advanced_preset(subghz); scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexRead); scene_manager_next_scene(subghz->scene_manager, SubGhzSceneReceiver); @@ -120,7 +107,6 @@ bool subghz_scene_start_on_event(void* context, SceneManagerEvent event) { scene_manager_next_scene(subghz->scene_manager, SubGhzSceneFrequencyAnalyzer); DOLPHIN_DEED(DolphinDeedSubGhzFrequencyAnalyzer); return true; - } else if(event.event == SubmenuIndexTest) { scene_manager_set_scene_state( subghz->scene_manager, SubGhzSceneStart, SubmenuIndexTest); diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index d95133da7..39e89e9e9 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -183,7 +183,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { //init setting subghz->setting = subghz_setting_alloc(); - subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user.txt")); + subghz_setting_load(subghz->setting, EXT_PATH("subghz/assets/setting_user")); // Custom Presets load without using config file @@ -208,37 +208,26 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset2); - // Pagers + // # HND - FM presets FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); flipper_format_write_string_cstr( - temp_fm_preset2, + temp_fm_preset3, (const char*)"Custom_preset_data", - (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3); flipper_format_free(temp_fm_preset3); - // # HND - FM presets FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_1", temp_fm_preset4); - - flipper_format_free(temp_fm_preset3); - - FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); - flipper_format_write_string_cstr( - temp_fm_preset5, - (const char*)"Custom_preset_data", (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); - flipper_format_rewind(temp_fm_preset5); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"Honda_2", temp_fm_preset5); + flipper_format_rewind(temp_fm_preset4); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4); - flipper_format_free(temp_fm_preset5); + flipper_format_free(temp_fm_preset4); // custom presets loading - end @@ -247,20 +236,11 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->last_settings = subghz_last_settings_alloc(); subghz_last_settings_load(subghz->last_settings, 0); #if FURI_DEBUG -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - FURI_LOG_D( - TAG, - "last frequency: %ld, preset: %ld, detect_raw: %d", - subghz->last_settings->frequency, - subghz->last_settings->preset, - subghz->last_settings->detect_raw); -#else FURI_LOG_D( TAG, "last frequency: %ld, preset: %ld", subghz->last_settings->frequency, subghz->last_settings->preset); -#endif #endif subghz_setting_set_default_frequency(subghz->setting, subghz->last_settings->frequency); } @@ -279,6 +259,7 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->hopper_state = SubGhzHopperStateOFF; subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; subghz->txrx->rx_key_state = SubGhzRxKeyStateIDLE; + subghz->txrx->debug_pin_state = false; if(!alloc_for_tx_only) { subghz->txrx->history = subghz_history_alloc(); } @@ -292,16 +273,15 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { subghz->txrx->environment = subghz_environment_alloc(); subghz_environment_set_came_atomo_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + subghz->txrx->environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( subghz->txrx->environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry( subghz->txrx->environment, (void*)&subghz_protocol_registry); subghz->txrx->receiver = subghz_receiver_alloc_init(subghz->txrx->environment); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - subghz_last_settings_set_detect_raw_values(subghz); -#else - subghz_receiver_set_filter(subghz->txrx->receiver, SubGhzProtocolFlag_Decodable); -#endif + subghz->txrx->filter = SubGhzProtocolFlag_Decodable; + subghz_receiver_set_filter(subghz->txrx->receiver, subghz->txrx->filter); subghz_worker_set_overrun_callback( subghz->txrx->worker, (SubGhzWorkerOverrunCallback)subghz_receiver_reset); @@ -325,6 +305,8 @@ void subghz_free(SubGhz* subghz, bool alloc_for_tx_only) { subghz->rpc_ctx = NULL; } + subghz_speaker_off(subghz); + #if FURI_DEBUG // Packet Test view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewIdTestPacket); diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index ed1648083..c047a32b3 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -42,8 +42,9 @@ void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); furi_hal_power_suppress_charge_enter(); @@ -252,6 +253,8 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { subghz_environment_load_keystore(environment, EXT_PATH("subghz/assets/keeloq_mfcodes_user")); subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -264,7 +267,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); frequency = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_power_suppress_charge_enter(); @@ -304,6 +307,81 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) { free(instance); } +void subghz_cli_command_rx_raw(Cli* cli, FuriString* args, void* context) { + UNUSED(context); + uint32_t frequency = 433920000; + + if(furi_string_size(args)) { + int ret = sscanf(furi_string_get_cstr(args), "%lu", &frequency); + if(ret != 1) { + printf("sscanf returned %d, frequency: %lu\r\n", ret, frequency); + cli_print_usage("subghz rx", "", furi_string_get_cstr(args)); + return; + } + if(!furi_hal_subghz_is_frequency_valid(frequency)) { + printf( + "Frequency must be in " SUBGHZ_FREQUENCY_RANGE_STR " range, not %lu\r\n", + frequency); + return; + } + } + + // Allocate context and buffers + SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx)); + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration)); + furi_check(instance->stream); + + // Configure radio + furi_hal_subghz_reset(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok270Async); + frequency = furi_hal_subghz_set_frequency_and_path(frequency); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_power_suppress_charge_enter(); + + // Prepare and start RX + furi_hal_subghz_start_async_rx(subghz_cli_command_rx_capture_callback, instance); + + // Wait for packets to arrive + printf("Listening at %lu. Press CTRL+C to stop\r\n", frequency); + LevelDuration level_duration; + size_t counter = 0; + while(!cli_cmd_interrupt_received(cli)) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == 0) { + continue; + } + if(ret != sizeof(LevelDuration)) { + puts("stream corrupt"); + break; + } + if(level_duration_is_reset(level_duration)) { + puts(". "); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + printf("%c%lu ", level ? '+' : '-', duration); + } + furi_thread_stdout_flush(); + counter++; + if(counter > 255) { + puts("\r\n"); + counter = 0; + } + } + + // Shutdown radio + furi_hal_subghz_stop_async_rx(); + furi_hal_subghz_sleep(); + + furi_hal_power_suppress_charge_exit(); + + // Cleanup + furi_stream_buffer_free(instance->stream); + free(instance); +} void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { UNUSED(context); FuriString* file_name; @@ -372,6 +450,8 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } subghz_environment_set_came_atomo_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/came_atomo")); + subghz_environment_set_alutech_at_4n_rainbow_table_file_name( + environment, EXT_PATH("subghz/assets/alutech_at_4n")); subghz_environment_set_nice_flor_s_rainbow_table_file_name( environment, EXT_PATH("subghz/assets/nice_flor_s")); subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry); @@ -387,7 +467,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) { } printf( - "Listening at %s.\r\n\r\nPress CTRL+C to stop\r\n\r\n", + "Listening at \033[0;33m%s\033[0m.\r\n\r\nPress CTRL+C to stop\r\n\r\n", furi_string_get_cstr(file_name)); LevelDuration level_duration; @@ -426,7 +506,8 @@ static void subghz_cli_command_print_usage() { printf("\tchat \t - Chat with other Flippers\r\n"); printf( "\ttx <3 byte Key: in hex> \t - Transmitting key\r\n"); - printf("\trx \t - Reception key\r\n"); + printf("\trx \t - Receive\r\n"); + printf("\trx_raw \t - Receive RAW\r\n"); printf("\tdecode_raw \t - Testing\r\n"); if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { @@ -521,8 +602,7 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { - UNUSED(context); +static void subghz_cli_command_chat(Cli* cli, FuriString* args) { uint32_t frequency = 433920000; if(furi_string_size(args)) { @@ -578,7 +658,7 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { NotificationApp* notification = furi_record_open(RECORD_NOTIFICATION); - furi_string_printf(name, "%s: ", furi_hal_version_get_name_ptr()); + furi_string_printf(name, "\033[0;33m%s\033[0m: ", furi_hal_version_get_name_ptr()); furi_string_set(input, name); printf("%s", furi_string_get_cstr(input)); fflush(stdout); @@ -659,14 +739,18 @@ static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { notification_message(notification, &sequence_single_vibro); break; case SubGhzChatEventUserEntrance: - furi_string_printf(sysmsg, "%s joined chat.\r\n", furi_hal_version_get_name_ptr()); + furi_string_printf( + sysmsg, + "\033[0;34m%s joined chat.\033[0m\r\n", + furi_hal_version_get_name_ptr()); subghz_chat_worker_write( subghz_chat, (uint8_t*)furi_string_get_cstr(sysmsg), strlen(furi_string_get_cstr(sysmsg))); break; case SubGhzChatEventUserExit: - furi_string_printf(sysmsg, "%s left chat.\r\n", furi_hal_version_get_name_ptr()); + furi_string_printf( + sysmsg, "\033[0;31m%s left chat.\033[0m\r\n", furi_hal_version_get_name_ptr()); subghz_chat_worker_write( subghz_chat, (uint8_t*)furi_string_get_cstr(sysmsg), @@ -711,7 +795,7 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args, NULL); + subghz_cli_command_chat(cli, args); break; } @@ -725,6 +809,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { break; } + if(furi_string_cmp_str(cmd, "rx_raw") == 0) { + subghz_cli_command_rx_raw(cli, args, context); + break; + } + if(furi_string_cmp_str(cmd, "decode_raw") == 0) { subghz_cli_command_decode_raw(cli, args, context); break; @@ -763,11 +852,9 @@ void subghz_on_system_start() { Cli* cli = furi_record_open(RECORD_CLI); cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); - // psst RM... i know you dont care much about errors, but if you ever see this... incompatible pointer type :3 - cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL); furi_record_close(RECORD_CLI); #else UNUSED(subghz_cli_command); #endif -} \ No newline at end of file +} diff --git a/applications/main/subghz/subghz_file_encoder_worker.c b/applications/main/subghz/subghz_file_encoder_worker.c new file mode 100644 index 000000000..a8c6519ef --- /dev/null +++ b/applications/main/subghz/subghz_file_encoder_worker.c @@ -0,0 +1,244 @@ +#include "subghz_file_encoder_worker.h" + +#include +#include +#include + +#define TAG "SubGhzFileEncoderWorker" + +#define SUBGHZ_FILE_ENCODER_LOAD 512 + +struct SubGhzFileEncoderWorker { + FuriThread* thread; + FuriStreamBuffer* stream; + + Storage* storage; + FlipperFormat* flipper_format; + + volatile bool worker_running; + volatile bool worker_stoping; + bool level; + bool is_storage_slow; + FuriString* str_data; + FuriString* file_path; + + SubGhzFileEncoderWorkerCallbackEnd callback_end; + void* context_end; +}; + +void subghz_file_encoder_worker_callback_end( + SubGhzFileEncoderWorker* instance, + SubGhzFileEncoderWorkerCallbackEnd callback_end, + void* context_end) { + furi_assert(instance); + furi_assert(callback_end); + instance->callback_end = callback_end; + instance->context_end = context_end; +} + +void subghz_file_encoder_worker_add_level_duration( + SubGhzFileEncoderWorker* instance, + int32_t duration) { + bool res = true; + if(duration < 0 && !instance->level) { + res = false; + } else if(duration > 0 && instance->level) { + res = false; + } + + if(res) { + instance->level = !instance->level; + furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); + } else { + FURI_LOG_E(TAG, "Invalid level in the stream"); + } +} + +bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { + char* str1; + bool res = false; + // Line sample: "RAW_Data: -1, 2, -2..." + + // Look for a key in the line + str1 = strstr(strStart, "RAW_Data: "); + + if(str1 != NULL) { + // Skip key + str1 = strchr(str1, ' '); + + // Check that there is still an element in the line + while(strchr(str1, ' ') != NULL) { + str1 = strchr(str1, ' '); + + // Skip space + str1 += 1; + subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); + } + res = true; + } + return res; +} + +void subghz_file_encoder_worker_get_text_progress( + SubGhzFileEncoderWorker* instance, + FuriString* output) { + UNUSED(output); + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + size_t total_size = stream_size(stream); + size_t current_offset = stream_tell(stream); + size_t buffer_avail = furi_stream_buffer_bytes_available(instance->stream); + + furi_string_printf(output, "%03u%%", 100 * (current_offset - buffer_avail) / total_size); +} + +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { + furi_assert(context); + SubGhzFileEncoderWorker* instance = context; + int32_t duration; + int ret = furi_stream_buffer_receive(instance->stream, &duration, sizeof(int32_t), 0); + if(ret == sizeof(int32_t)) { + LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; + if(duration < 0) { + level_duration = level_duration_make(false, -duration); + } else if(duration > 0) { + level_duration = level_duration_make(true, duration); + } else if(duration == 0) { //-V547 + level_duration = level_duration_reset(); + FURI_LOG_I(TAG, "Stop transmission"); + instance->worker_stoping = true; + } + return level_duration; + } else { + instance->is_storage_slow = true; + return level_duration_wait(); + } +} + +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t subghz_file_encoder_worker_thread(void* context) { + SubGhzFileEncoderWorker* instance = context; + FURI_LOG_I(TAG, "Worker start"); + bool res = false; + instance->is_storage_slow = false; + Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); + do { + if(!flipper_format_file_open_existing( + instance->flipper_format, furi_string_get_cstr(instance->file_path))) { + FURI_LOG_E( + TAG, + "Unable to open file for read: %s", + furi_string_get_cstr(instance->file_path)); + break; + } + if(!flipper_format_read_string(instance->flipper_format, "Protocol", instance->str_data)) { + FURI_LOG_E(TAG, "Missing Protocol"); + break; + } + + //skip the end of the previous line "\n" + stream_seek(stream, 1, StreamOffsetFromCurrent); + res = true; + instance->worker_stoping = false; + FURI_LOG_I(TAG, "Start transmission"); + } while(0); + + while(res && instance->worker_running) { + size_t stream_free_byte = furi_stream_buffer_spaces_available(instance->stream); + if((stream_free_byte / sizeof(int32_t)) >= SUBGHZ_FILE_ENCODER_LOAD) { + if(stream_read_line(stream, instance->str_data)) { + furi_string_trim(instance->str_data); + if(!subghz_file_encoder_worker_data_parse( + instance, furi_string_get_cstr(instance->str_data))) { + subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); + break; + } + } else { + subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); + break; + } + } else { + furi_delay_ms(1); + } + } + //waiting for the end of the transfer + if(instance->is_storage_slow) { + FURI_LOG_E(TAG, "Storage is slow"); + } + FURI_LOG_I(TAG, "End read file"); + while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { + furi_delay_ms(5); + } + FURI_LOG_I(TAG, "End transmission"); + while(instance->worker_running) { + if(instance->worker_stoping) { + if(instance->callback_end) instance->callback_end(instance->context_end); + } + furi_delay_ms(50); + } + flipper_format_file_close(instance->flipper_format); + + FURI_LOG_I(TAG, "Worker stop"); + return 0; +} + +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { + SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); + instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); + + instance->storage = furi_record_open(RECORD_STORAGE); + instance->flipper_format = flipper_format_file_alloc(instance->storage); + + instance->str_data = furi_string_alloc(); + instance->file_path = furi_string_alloc(); + instance->level = false; + instance->worker_stoping = true; + + return instance; +} + +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + + furi_stream_buffer_free(instance->stream); + furi_thread_free(instance->thread); + + furi_string_free(instance->str_data); + furi_string_free(instance->file_path); + + flipper_format_free(instance->flipper_format); + furi_record_close(RECORD_STORAGE); + + free(instance); +} + +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { + furi_assert(instance); + furi_assert(!instance->worker_running); + + furi_stream_buffer_reset(instance->stream); + furi_string_set(instance->file_path, file_path); + instance->worker_running = true; + furi_thread_start(instance->thread); + + return true; +} + +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + furi_thread_join(instance->thread); +} + +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) { + furi_assert(instance); + return instance->worker_running; +} diff --git a/applications/main/subghz/subghz_file_encoder_worker.h b/applications/main/subghz/subghz_file_encoder_worker.h new file mode 100644 index 000000000..19a46f1e6 --- /dev/null +++ b/applications/main/subghz/subghz_file_encoder_worker.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context); + +typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker; + +/** + * End callback SubGhzWorker. + * @param instance SubGhzFileEncoderWorker instance + * @param callback SubGhzFileEncoderWorkerCallbackEnd callback + */ +void subghz_file_encoder_worker_callback_end( + SubGhzFileEncoderWorker* instance, + SubGhzFileEncoderWorkerCallbackEnd callback_end, + void* context_end); + +/** + * Allocate SubGhzFileEncoderWorker. + * @return SubGhzFileEncoderWorker* pointer to a SubGhzFileEncoderWorker instance + */ +SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(); + +/** + * Free SubGhzFileEncoderWorker. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance); + +/** + * Get a description of the progress. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @param output + */ +void subghz_file_encoder_worker_get_text_progress( + SubGhzFileEncoderWorker* instance, + FuriString* output); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzFileEncoderWorker instance + * @return LevelDuration + */ +LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); + +/** + * Start SubGhzFileEncoderWorker. + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @return bool - true if ok + */ +bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); + +/** + * Stop SubGhzFileEncoderWorker + * @param instance Pointer to a SubGhzFileEncoderWorker instance + */ +void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance); + +/** + * Check if worker is running + * @param instance Pointer to a SubGhzFileEncoderWorker instance + * @return bool - true if running + */ +bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance); diff --git a/applications/main/subghz/subghz_history.c b/applications/main/subghz/subghz_history.c index e8d3acfd7..184146698 100644 --- a/applications/main/subghz/subghz_history.c +++ b/applications/main/subghz/subghz_history.c @@ -1,33 +1,15 @@ #include "subghz_history.h" -#include "subghz_history_private.h" #include -#include -#include -#include "flipper_format_stream_i.h" -#include -#define SUBGHZ_HISTORY_MAX 60 - -/** - * @brief Settings for temporary files - * - */ -#define SUBGHZ_HISTORY_TMP_DIR EXT_PATH("subghz/tmp_history") -#define SUBGHZ_HISTORY_TMP_EXTENSION ".tmp" -#define SUBGHZ_HISTORY_TMP_SIGNAL_MAX 700 -#define SUBGHZ_HISTORY_TMP_SIGNAL_MIN 100 -#define SUBGHZ_HISTORY_TMP_REMOVE_FILES true -#define SUBGHZ_HISTORY_TMP_RAW_KEY "RAW_Data" -#define MAX_LINE 500 -const size_t buffer_size = 32; +#include +#define SUBGHZ_HISTORY_MAX 55 +#define SUBGHZ_HISTORY_FREE_HEAP 20480 #define TAG "SubGhzHistory" typedef struct { FuriString* item_str; FlipperFormat* flipper_string; - FuriString* protocol_name; - bool is_file; uint8_t type; SubGhzRadioPreset* preset; } SubGhzHistoryItem; @@ -45,146 +27,30 @@ struct SubGhzHistory { uint16_t last_index_write; uint8_t code_last_hash_data; FuriString* tmp_string; - bool write_tmp_files; - bool is_hopper_running; - Storage* storage; SubGhzHistoryStruct* history; }; -#ifdef FURI_DEBUG -#define LOG_DELAY 0 -#endif - -FuriString* subghz_history_generate_temp_filename(uint32_t index) { - FuriHalRtcDateTime datetime = {0}; - furi_hal_rtc_get_datetime(&datetime); - return furi_string_alloc_printf("%03ld%s", index, SUBGHZ_HISTORY_TMP_EXTENSION); -} - -bool subghz_history_is_tmp_dir_exists(SubGhzHistory* instance) { - FileInfo file_info; - FS_Error error = storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &file_info); - - if(error == FSE_OK) { - if(file_info.flags & FSF_DIRECTORY) { - return true; - } - } - - return false; -} - -bool subghz_history_check_sdcard(SubGhzHistory* instance) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "check_sdcard"); - uint32_t start_time = furi_get_tick(); -#endif - - bool result = false; - // Stage 0 - check SD Card - FS_Error status = storage_sd_status(instance->storage); - if(status == FSE_OK) { - result = subghz_history_is_tmp_dir_exists(instance); - if(!subghz_history_is_tmp_dir_exists(instance)) { - result = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); - } - } else { - FURI_LOG_W(TAG, "SD storage not installed! Status: %d", status); - } -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Running time (check_sdcard): %ld ms", furi_get_tick() - start_time); -#endif - - return result; -} - -void subghz_history_clear_tmp_dir(SubGhzHistory* instance) { - furi_assert(instance); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "clear_tmp_dir"); -#endif - - if(!instance->write_tmp_files) { - // Nothing to do here! - return; - } - //uint32_t start_time = furi_get_tick(); -#ifdef SUBGHZ_HISTORY_TMP_REMOVE_FILES - // Stage 0 - Dir exists? - bool res = subghz_history_is_tmp_dir_exists(instance); - if(res) { - // Stage 1 - delete all content if exists - FileInfo fileinfo; - storage_common_stat(instance->storage, SUBGHZ_HISTORY_TMP_DIR, &fileinfo); - - res = fileinfo.flags & FSF_DIRECTORY ? - storage_simply_remove_recursive(instance->storage, SUBGHZ_HISTORY_TMP_DIR) : - (storage_common_remove(instance->storage, SUBGHZ_HISTORY_TMP_DIR) == FSE_OK); - } - - // Stage 2 - create dir if necessary - res = storage_simply_mkdir(instance->storage, SUBGHZ_HISTORY_TMP_DIR); - if(!res) { - FURI_LOG_E(TAG, "Cannot process temp dir!"); - } -#endif - /* uint32_t stop_time = furi_get_tick() - start_time; - FURI_LOG_I(TAG, "Running time (clear_tmp_dir): %d ms", stop_time);*/ -} - SubGhzHistory* subghz_history_alloc(void) { SubGhzHistory* instance = malloc(sizeof(SubGhzHistory)); instance->tmp_string = furi_string_alloc(); instance->history = malloc(sizeof(SubGhzHistoryStruct)); SubGhzHistoryItemArray_init(instance->history->data); - instance->storage = furi_record_open(RECORD_STORAGE); - instance->write_tmp_files = subghz_history_check_sdcard(instance); - - instance->is_hopper_running = false; - - if(!instance->write_tmp_files) { - FURI_LOG_E(TAG, "Unstable work! Cannot use SD Card!"); - } - return instance; } -void subghz_history_item_free(void* current_item) { - furi_assert(current_item); - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; - furi_string_free(item->item_str); - furi_string_free(item->preset->name); - furi_string_free(item->protocol_name); - - free(item->preset); - item->type = 0; - item->is_file = false; - - if(item->flipper_string != NULL) { - flipper_format_free(item->flipper_string); - } -} - -void subghz_history_clean_item_array(SubGhzHistory* instance) { - for - M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { - subghz_history_item_free(item); - } -} - void subghz_history_free(SubGhzHistory* instance) { furi_assert(instance); furi_string_free(instance->tmp_string); - - subghz_history_clean_item_array(instance); + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } SubGhzHistoryItemArray_clear(instance->history->data); free(instance->history); - - // Delete all temporary file, on exit it's ok - subghz_history_clear_tmp_dir(instance); - - furi_record_close(RECORD_STORAGE); - free(instance); } @@ -209,20 +75,19 @@ const char* subghz_history_get_preset(SubGhzHistory* instance, uint16_t idx) { void subghz_history_reset(SubGhzHistory* instance) { furi_assert(instance); furi_string_reset(instance->tmp_string); - - subghz_history_clean_item_array(instance); - + for + M_EACH(item, instance->history->data, SubGhzHistoryItemArray_t) { + furi_string_free(item->item_str); + furi_string_free(item->preset->name); + free(item->preset); + flipper_format_free(item->flipper_string); + item->type = 0; + } SubGhzHistoryItemArray_reset(instance->history->data); instance->last_index_write = 0; instance->code_last_hash_data = 0; } -void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state) { - furi_assert(instance); - - instance->is_hopper_running = hopper_state; -} - uint16_t subghz_history_get_item(SubGhzHistory* instance) { furi_assert(instance); return instance->last_index_write; @@ -237,8 +102,12 @@ uint8_t subghz_history_get_type_protocol(SubGhzHistory* instance, uint16_t idx) const char* subghz_history_get_protocol_name(SubGhzHistory* instance, uint16_t idx) { furi_assert(instance); SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); - - return furi_string_get_cstr(item->protocol_name); + flipper_format_rewind(item->flipper_string); + if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { + FURI_LOG_E(TAG, "Missing Protocol"); + furi_string_reset(instance->tmp_string); + } + return furi_string_get_cstr(instance->tmp_string); } FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx) { @@ -247,72 +116,27 @@ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx if(item->flipper_string) { return item->flipper_string; } else { - bool result_ok = false; - if(instance->write_tmp_files && item->is_file) { - // We have files! - FuriString* filename = subghz_history_generate_temp_filename(idx); - FuriString* dir_path; - - dir_path = furi_string_alloc_printf( - "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename)); - - if(storage_file_exists(instance->storage, furi_string_get_cstr(dir_path))) { -#ifdef FURI_DEBUG - FURI_LOG_D(TAG, "Exist: %s", furi_string_get_cstr(dir_path)); - furi_delay_ms(LOG_DELAY); -#endif - // Set to current anyway it has NULL value - item->flipper_string = flipper_format_string_alloc(); - Stream* dst_stream = flipper_format_get_raw_stream(item->flipper_string); - stream_clean(dst_stream); - - size_t size = stream_load_from_file( - dst_stream, instance->storage, furi_string_get_cstr(dir_path)); - if(size > 0) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save ok!"); - furi_delay_ms(LOG_DELAY); -#endif - // We changed contents of file, so we no needed to load - // content from disk for the next time - item->is_file = false; - result_ok = true; - } else { - FURI_LOG_E(TAG, "Stream copy failed!"); - flipper_format_free(item->flipper_string); - } - } else { - FURI_LOG_E(TAG, "Can't convert filename to file"); - } - - furi_string_free(filename); - furi_string_free(dir_path); - } else { -#ifdef FURI_DEBUG - FURI_LOG_W(TAG, "Write TMP files failed!"); - furi_delay_ms(LOG_DELAY); -#endif - } - return result_ok ? item->flipper_string : NULL; + return NULL; } } - bool subghz_history_get_text_space_left(SubGhzHistory* instance, FuriString* output) { furi_assert(instance); - if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { - if(output != NULL) furi_string_printf(output, "Memory is FULL"); + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) { + if(output != NULL) furi_string_printf(output, " Free heap LOW"); return true; } - if(output != NULL) { - furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); + if(instance->last_index_write == SUBGHZ_HISTORY_MAX) { + if(output != NULL) furi_string_printf(output, " Memory is FULL"); + return true; } + if(output != NULL) + furi_string_printf(output, "%02u/%02u", instance->last_index_write, SUBGHZ_HISTORY_MAX); return false; } uint16_t subghz_history_get_last_index(SubGhzHistory* instance) { return instance->last_index_write; } - void subghz_history_get_text_item_menu(SubGhzHistory* instance, FuriString* output, uint16_t idx) { SubGhzHistoryItem* item = SubGhzHistoryItemArray_get(instance->history->data, idx); furi_string_set(output, item->item_str); @@ -325,9 +149,8 @@ bool subghz_history_add_to_history( furi_assert(instance); furi_assert(context); - if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) { - return false; - } + if(memmgr_get_free_heap() < SUBGHZ_HISTORY_FREE_HEAP) return false; + if(instance->last_index_write >= SUBGHZ_HISTORY_MAX) return false; SubGhzProtocolDecoderBase* decoder_base = context; if((instance->code_last_hash_data == @@ -339,6 +162,7 @@ bool subghz_history_add_to_history( instance->code_last_hash_data = subghz_protocol_decoder_base_get_hash_data(decoder_base); instance->last_update_timestamp = furi_get_tick(); + FuriString* text; text = furi_string_alloc(); SubGhzHistoryItem* item = SubGhzHistoryItemArray_push_raw(instance->history->data); @@ -351,11 +175,6 @@ bool subghz_history_add_to_history( item->preset->data_size = preset->data_size; item->item_str = furi_string_alloc(); - item->protocol_name = furi_string_alloc(); - - bool tmp_file_for_raw = false; - - // At this point file mapped to memory otherwise file cannot decode item->flipper_string = flipper_format_string_alloc(); subghz_protocol_decoder_base_serialize(decoder_base, item->flipper_string, preset); @@ -367,30 +186,8 @@ bool subghz_history_add_to_history( if(!flipper_format_read_string(item->flipper_string, "Protocol", instance->tmp_string)) { FURI_LOG_E(TAG, "Missing Protocol"); break; - } else { - furi_string_printf( - item->protocol_name, "%s", furi_string_get_cstr(instance->tmp_string)); } - if(!strcmp(furi_string_get_cstr(instance->tmp_string), "RAW")) { - // Check if hopper enabled we need to add little delay - if(instance->is_hopper_running) { - furi_delay_ms(40); - } - // Enable writing temp files to micro sd - tmp_file_for_raw = true; - // Write display name - furi_string_printf( - item->item_str, - "RAW %03ld.%02ld", - preset->frequency / 1000000 % 1000, - preset->frequency / 10000 % 100); - // Rewind - if(!flipper_format_rewind(item->flipper_string)) { - FURI_LOG_E(TAG, "Rewind error"); - } - - break; - } else if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) { + if(!strcmp(furi_string_get_cstr(instance->tmp_string), "KeeLoq")) { furi_string_set(instance->tmp_string, "KL "); if(!flipper_format_read_string(item->flipper_string, "Manufacture", text)) { FURI_LOG_E(TAG, "Missing Protocol"); @@ -411,484 +208,34 @@ bool subghz_history_add_to_history( } uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(item->flipper_string, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; + FURI_LOG_D(TAG, "No Key"); } uint64_t data = 0; for(uint8_t i = 0; i < sizeof(uint64_t); i++) { data = (data << 8) | key_data[i]; } - if(!(uint32_t)(data >> 32)) { - furi_string_printf( - item->item_str, - "%s %lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data & 0xFFFFFFFF)); + if(data != 0) { + if(!(uint32_t)(data >> 32)) { + furi_string_printf( + item->item_str, + "%s %lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data & 0xFFFFFFFF)); + } else { + furi_string_printf( + item->item_str, + "%s %lX%08lX", + furi_string_get_cstr(instance->tmp_string), + (uint32_t)(data >> 32), + (uint32_t)(data & 0xFFFFFFFF)); + } } else { - furi_string_printf( - item->item_str, - "%s %lX%08lX", - furi_string_get_cstr(instance->tmp_string), - (uint32_t)(data >> 32), - (uint32_t)(data & 0xFFFFFFFF)); + furi_string_printf(item->item_str, "%s", furi_string_get_cstr(instance->tmp_string)); } + } while(false); - // If we can write to files - if(instance->write_tmp_files && tmp_file_for_raw) { - FuriString* filename = subghz_history_generate_temp_filename(instance->last_index_write); - FuriString* dir_path; - dir_path = furi_string_alloc(); - - furi_string_cat_printf( - dir_path, "%s/%s", SUBGHZ_HISTORY_TMP_DIR, furi_string_get_cstr(filename)); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save temp file: %s", furi_string_get_cstr(dir_path)); -#endif - if(!subghz_history_tmp_write_file_split(instance, item, furi_string_get_cstr(dir_path))) { - // Plan B! - subghz_history_tmp_write_file_full(instance, item, dir_path); - } - if(item->is_file) { - flipper_format_free(item->flipper_string); - item->flipper_string = NULL; - } - furi_string_free(filename); - furi_string_free(dir_path); - - } else { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Old fashion way"); -#endif - } - furi_string_free(text); - instance->last_index_write++; return true; } - -static inline bool is_space_playground(char c) { - return c == ' ' || c == '\t' || c == flipper_format_eolr; -} - -bool subghz_history_stream_read_valid_key(Stream* stream, FuriString* key) { - furi_string_reset(key); - uint8_t buffer[buffer_size]; - - bool found = false; - bool error = false; - bool accumulate = true; - bool new_line = true; - - while(true) { - size_t was_read = stream_read(stream, buffer, buffer_size); - if(was_read == 0) break; - - for(size_t i = 0; i < was_read; i++) { - uint8_t data = buffer[i]; - if(data == flipper_format_eoln) { - // EOL found, clean data, start accumulating data and set the new_line flag - furi_string_reset(key); - accumulate = true; - new_line = true; - } else if(data == flipper_format_eolr) { - // ignore - } else if(data == flipper_format_comment && new_line) { - // if there is a comment character and we are at the beginning of a new line - // do not accumulate comment data and reset the new_line flag - accumulate = false; - new_line = false; - } else if(data == flipper_format_delimiter) { - if(new_line) { - // we are on a "new line" and found the delimiter - // this can only be if we have previously found some kind of key, so - // clear the data, set the flag that we no longer want to accumulate data - // and reset the new_line flag - furi_string_reset(key); - accumulate = false; - new_line = false; - } else { - // parse the delimiter only if we are accumulating data - if(accumulate) { - // we found the delimiter, move the rw pointer to the delimiter location - // and signal that we have found something - if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - break; - } - - found = true; - break; - } - } - } else { - // just new symbol, reset the new_line flag - new_line = false; - if(accumulate) { - // and accumulate data if we want - furi_string_push_back(key, data); - } - } - } - - if(found || error) break; - } - - return found; -} - -bool subghz_history_stream_seek_to_key(Stream* stream, const char* key, bool strict_mode) { - bool found = false; - FuriString* read_key; - - read_key = furi_string_alloc(); - - while(!stream_eof(stream)) { - if(subghz_history_stream_read_valid_key(stream, read_key)) { - if(furi_string_cmp_str(read_key, key) == 0) { - if(!stream_seek(stream, 2, StreamOffsetFromCurrent)) { - break; - } - found = true; - break; - } else if(strict_mode) { - found = false; - break; - } - } - } - furi_string_free(read_key); - - return found; -} - -bool subghz_history_stream_read_value(Stream* stream, FuriString* value, bool* last) { - enum { LeadingSpace, ReadValue, TrailingSpace } state = LeadingSpace; - const size_t buffer_size = 32; - uint8_t buffer[buffer_size]; - bool result = false; - bool error = false; - - furi_string_reset(value); - - while(true) { - size_t was_read = stream_read(stream, buffer, buffer_size); - - if(was_read == 0) { - if(state != LeadingSpace && stream_eof(stream)) { - result = true; - *last = true; - } else { - error = true; - } - } - - for(uint16_t i = 0; i < was_read; i++) { - const uint8_t data = buffer[i]; - - if(state == LeadingSpace) { - if(is_space_playground(data)) { - continue; - } else if(data == flipper_format_eoln) { - stream_seek(stream, i - was_read, StreamOffsetFromCurrent); - error = true; - break; - } else { - state = ReadValue; - furi_string_push_back(value, data); - } - } else if(state == ReadValue) { - if(is_space_playground(data)) { - state = TrailingSpace; - } else if(data == flipper_format_eoln) { - if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - } else { - result = true; - *last = true; - } - break; - } else { - furi_string_push_back(value, data); - } - } else if(state == TrailingSpace) { - if(is_space_playground(data)) { - continue; - } else if(!stream_seek(stream, i - was_read, StreamOffsetFromCurrent)) { - error = true; - } else { - *last = (data == flipper_format_eoln); - result = true; - } - break; - } - } - - if(error || result) break; - } - - return result; -} - -bool subghz_history_read_int32(Stream* stream, int32_t* _data, const uint16_t data_size) { - bool result = false; - result = true; - FuriString* value; - value = furi_string_alloc(); - - for(size_t i = 0; i < data_size; i++) { - bool last = false; - result = subghz_history_stream_read_value(stream, value, &last); - if(result) { - int scan_values = 0; - - int32_t* data = _data; - scan_values = sscanf(furi_string_get_cstr(value), "%" PRIi32, &data[i]); - - if(scan_values != 1) { - result = false; - break; - } - } else { - break; - } - - if(last && ((i + 1) != data_size)) { - result = false; - break; - } - } - - furi_string_free(value); - return result; -} - -uint32_t subghz_history_rand_range(uint32_t min, uint32_t max) { - // size of range, inclusive - const uint32_t length_of_range = max - min + 1; - - // add n so that we don't return a number below our range - return (uint32_t)(rand() % length_of_range + min); -} - -bool subghz_history_write_file_noise( - Stream* file, - bool is_negative_start, - size_t current_position, - bool empty_line) { - size_t was_write = 0; - if(empty_line) { - was_write = stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY); - - if(was_write <= 0) { - FURI_LOG_E(TAG, "Can't write key!"); - return false; - } - } - - int8_t first; - int8_t second; - if(is_negative_start) { - first = -1; - second = 1; - } else { - first = 1; - second = -1; - } - while(current_position < MAX_LINE) { - was_write = stream_write_format( - file, - "%ld %ld ", - subghz_history_rand_range( - SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * - first, - subghz_history_rand_range( - SUBGHZ_HISTORY_TMP_SIGNAL_MIN, SUBGHZ_HISTORY_TMP_SIGNAL_MAX) * - second); - - if(was_write <= 0) { - FURI_LOG_E(TAG, "Can't write random values!"); - return false; - } - - current_position += was_write; - } - - // Step back to write \n instead of space - size_t offset = stream_tell(file); - if(stream_seek(file, offset - 1, StreamOffsetFromCurrent)) { - FURI_LOG_E(TAG, "Step back failed!"); - return false; - } - - return stream_write_char(file, flipper_format_eoln) > 0; -} - -bool subghz_history_write_file_data( - Stream* src, - Stream* file, - bool* is_negative_start, - size_t* current_position) { - size_t offset_file = 0; - bool result = false; - int32_t value = 0; - - do { - if(!subghz_history_read_int32(src, &value, 1)) { - result = true; - break; - } - offset_file = stream_tell(file); - stream_write_format(file, "%ld ", value); - *current_position += stream_tell(file) - offset_file; - - if(*current_position > MAX_LINE) { - if((is_negative_start && value > 0) || (!is_negative_start && value < 0)) { - // Align values - continue; - } - - if(stream_write_format(file, "\n%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { - FURI_LOG_E(TAG, "Can't write new line!"); - result = false; - break; - } - *current_position = 0; - } - } while(true); - - *is_negative_start = value < 0; - - return result; -} - -bool subghz_history_tmp_write_file_split( - SubGhzHistory* instance, - void* current_item, - const char* dir_path) { - furi_assert(instance); - furi_assert(current_item); - furi_assert(dir_path); -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save temp file splitted: %s", dir_path); -#endif - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; - - uint8_t buffer[buffer_size]; - Stream* src = flipper_format_get_raw_stream(item->flipper_string); - stream_rewind(src); - - FlipperFormat* flipper_format_file = flipper_format_file_alloc(instance->storage); - bool result = false; - FuriString* temp_str = furi_string_alloc(); - - do { - if(storage_file_exists(instance->storage, dir_path) && - storage_common_remove(instance->storage, dir_path) != FSE_OK) { - FURI_LOG_E(TAG, "Can't delete old file!"); - break; - } - path_extract_dirname(dir_path, temp_str); - FS_Error fs_result = - storage_common_mkdir(instance->storage, furi_string_get_cstr(temp_str)); - if(fs_result != FSE_OK && fs_result != FSE_EXIST) { - FURI_LOG_E(TAG, "Can't create dir!"); - break; - } - result = flipper_format_file_open_always(flipper_format_file, dir_path); - if(!result) { - FURI_LOG_E(TAG, "Can't open file for write!"); - break; - } - Stream* file = flipper_format_get_raw_stream(flipper_format_file); - - if(!subghz_history_stream_seek_to_key(src, SUBGHZ_HISTORY_TMP_RAW_KEY, false)) { - FURI_LOG_E(TAG, "Can't find key!"); - break; - } - bool is_negative_start = false; - bool found = false; - - size_t offset_start; - offset_start = stream_tell(src); - - // Check for negative value at the start and end to align file by correct values - size_t was_read = stream_read(src, buffer, 1); - if(was_read <= 0) { - FURI_LOG_E(TAG, "Can't obtain first mark!"); - break; - } - - is_negative_start = buffer[0] == '-'; - - // Ready to write stream to file - size_t current_position; - stream_rewind(src); - current_position = stream_copy(src, file, offset_start); - if(current_position != offset_start) { - FURI_LOG_E(TAG, "Invalid copy header data from one stream to another!"); - break; - } - - found = true; - - current_position = 0; - if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { - FURI_LOG_E(TAG, "Add start noise failed!"); - break; - } - - if(stream_write_format(file, "%s: ", SUBGHZ_HISTORY_TMP_RAW_KEY) == 0) { - FURI_LOG_E(TAG, "Can't write new line!"); - result = false; - break; - } - - if(!subghz_history_write_file_data(src, file, &is_negative_start, ¤t_position)) { - FURI_LOG_E(TAG, "Split by lines failed!"); - break; - } - - if(!subghz_history_write_file_noise(file, is_negative_start, current_position, false)) { - FURI_LOG_E(TAG, "Add end noise failed!"); - break; - } - - if(!subghz_history_write_file_noise(file, is_negative_start, 0, true)) { - FURI_LOG_E(TAG, "Add end noise failed!"); - break; - } - - result = found; - } while(false); - flipper_format_file_close(flipper_format_file); - flipper_format_free(flipper_format_file); - furi_string_free(temp_str); - - item->is_file = result; - - return result; -} - -void subghz_history_tmp_write_file_full( - SubGhzHistory* instance, - void* current_item, - FuriString* dir_path) { - SubGhzHistoryItem* item = (SubGhzHistoryItem*)current_item; -#ifdef FURI_DEBUG - FURI_LOG_W(TAG, "Save temp file full: %s", furi_string_get_cstr(dir_path)); -#endif - Stream* dst = flipper_format_get_raw_stream(item->flipper_string); - stream_rewind(dst); - if(stream_save_to_file( - dst, instance->storage, furi_string_get_cstr(dir_path), FSOM_CREATE_ALWAYS) > 0) { -#ifdef FURI_DEBUG - FURI_LOG_I(TAG, "Save done!"); -#endif - // This item contains fake data to load from SD - item->is_file = true; - } else { - FURI_LOG_E(TAG, "Stream copy failed!"); - } -} \ No newline at end of file diff --git a/applications/main/subghz/subghz_history.h b/applications/main/subghz/subghz_history.h index ee1ee1a4d..607dbeae2 100644 --- a/applications/main/subghz/subghz_history.h +++ b/applications/main/subghz/subghz_history.h @@ -110,10 +110,3 @@ bool subghz_history_add_to_history( * @return SubGhzProtocolCommonLoad* */ FlipperFormat* subghz_history_get_raw_data(SubGhzHistory* instance, uint16_t idx); - -/** Set hopper state for internal usage in history - * - * @param instance - SubGhzHistory instance - * @param hopper_state - bool is hopper running? - */ -void subghz_history_set_hopper_state(SubGhzHistory* instance, bool hopper_state); diff --git a/applications/main/subghz/subghz_i.c b/applications/main/subghz/subghz_i.c index bac25759a..1fbe662ed 100644 --- a/applications/main/subghz/subghz_i.c +++ b/applications/main/subghz/subghz_i.c @@ -30,12 +30,6 @@ void subghz_preset_init( subghz->txrx->preset->frequency = frequency; subghz->txrx->preset->data = preset_data; subghz->txrx->preset->data_size = preset_data_size; - - subghz->txrx->raw_bandwidth = - subghz_preset_custom_get_bandwidth(preset_data, preset_data_size); - subghz->txrx->raw_manchester_enabled = - subghz_preset_custom_get_machester_enable(preset_data, preset_data_size); - subghz->txrx->raw_datarate = subghz_preset_custom_get_datarate(preset_data, preset_data_size); } bool subghz_set_preset(SubGhz* subghz, const char* preset) { @@ -75,7 +69,7 @@ void subghz_begin(SubGhz* subghz, uint8_t* preset_data) { furi_hal_subghz_reset(); furi_hal_subghz_idle(); furi_hal_subghz_load_custom_preset(preset_data); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); subghz->txrx->txrx_state = SubGhzTxRxStateIDLE; } @@ -90,7 +84,7 @@ uint32_t subghz_rx(SubGhz* subghz, uint32_t frequency) { furi_hal_subghz_idle(); uint32_t value = furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_flush_rx(); subghz_speaker_on(subghz); furi_hal_subghz_rx(); @@ -109,8 +103,9 @@ static bool subghz_tx(SubGhz* subghz, uint32_t frequency) { furi_assert(subghz->txrx->txrx_state != SubGhzTxRxStateSleep); furi_hal_subghz_idle(); furi_hal_subghz_set_frequency_and_path(frequency); - furi_hal_gpio_write(&gpio_cc1101_g0, false); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); subghz_speaker_on(subghz); bool ret = furi_hal_subghz_tx(); subghz->txrx->txrx_state = SubGhzTxRxStateTx; @@ -602,9 +597,15 @@ void subghz_hopper_update(SubGhz* subghz) { } void subghz_speaker_on(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + } + if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_acquire(30)) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } } else { subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; } @@ -612,9 +613,14 @@ void subghz_speaker_on(SubGhz* subghz) { } void subghz_speaker_off(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } if(subghz->txrx->speaker_state != SubGhzSpeakerStateDisable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } furi_hal_speaker_release(); if(subghz->txrx->speaker_state == SubGhzSpeakerStateShutdown) subghz->txrx->speaker_state = SubGhzSpeakerStateDisable; @@ -623,17 +629,27 @@ void subghz_speaker_off(SubGhz* subghz) { } void subghz_speaker_mute(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(NULL); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(NULL); + } } } } void subghz_speaker_unmute(SubGhz* subghz) { + if(subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_ext_pa7); + } if(subghz->txrx->speaker_state == SubGhzSpeakerStateEnable) { if(furi_hal_speaker_is_mine()) { - furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + if(!subghz->txrx->debug_pin_state) { + furi_hal_subghz_set_async_mirror_pin(&gpio_speaker); + } } } } diff --git a/applications/main/subghz/subghz_i.h b/applications/main/subghz/subghz_i.h index a6c96cb69..393dd667d 100644 --- a/applications/main/subghz/subghz_i.h +++ b/applications/main/subghz/subghz_i.h @@ -3,7 +3,6 @@ #include "helpers/subghz_types.h" #include "helpers/subghz_error_type.h" #include -#include #include "subghz.h" #include "views/receiver.h" #include "views/transmitter.h" @@ -63,6 +62,7 @@ struct SubGhzTxRx { SubGhzEnvironment* environment; SubGhzReceiver* receiver; SubGhzTransmitter* transmitter; + SubGhzProtocolFlag filter; SubGhzProtocolDecoderBase* decoder_result; FlipperFormat* fff_data; SecureData* secure_data; @@ -77,15 +77,10 @@ struct SubGhzTxRx { uint8_t hopper_idx_frequency; SubGhzRxKeyState rx_key_state; + bool debug_pin_state; + float raw_threshold_rssi; uint8_t raw_threshold_rssi_low_count; - - // one of the 16 possible bandwidth values - uint8_t raw_bandwidth; - // datarate in bauds - float raw_datarate; - // flag if manchester encoding/decoding enabled - bool raw_manchester_enabled; }; typedef struct SubGhzTxRx SubGhzTxRx; @@ -114,13 +109,6 @@ struct SubGhz { SubGhzViewTransmitter* subghz_transmitter; VariableItemList* variable_item_list; - // Advanced config items - VariableItem* variable_item_bandwidth; // specific config list view item: bandwidth - VariableItem* variable_item_datarate; // specific config list view item: data rate - VariableItem* variable_item_manchester; // specific config list view item: manchester enc flag - // Advanced config strings - char datarate_input_str[16]; - SubGhzFrequencyAnalyzer* subghz_frequency_analyzer; SubGhzReadRAW* subghz_read_raw; bool raw_send_only; diff --git a/applications/main/subghz/subghz_keystore.c b/applications/main/subghz/subghz_keystore.c new file mode 100644 index 000000000..e0b1cf6ca --- /dev/null +++ b/applications/main/subghz/subghz_keystore.c @@ -0,0 +1,613 @@ +#include "subghz_keystore.h" + +#include +#include + +#include +#include +#include +#include +#include + +#define TAG "SubGhzKeystore" + +#define FILE_BUFFER_SIZE 64 + +#define SUBGHZ_KEYSTORE_FILE_TYPE "Flipper SubGhz Keystore File" +#define SUBGHZ_KEYSTORE_FILE_RAW_TYPE "Flipper SubGhz Keystore RAW File" +#define SUBGHZ_KEYSTORE_FILE_VERSION 0 + +#define SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT 1 +#define SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE 512 +#define SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE (SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE * 2) + +typedef enum { + SubGhzKeystoreEncryptionNone, + SubGhzKeystoreEncryptionAES256, +} SubGhzKeystoreEncryption; + +struct SubGhzKeystore { + SubGhzKeyArray_t data; +}; + +SubGhzKeystore* subghz_keystore_alloc() { + SubGhzKeystore* instance = malloc(sizeof(SubGhzKeystore)); + + SubGhzKeyArray_init(instance->data); + + return instance; +} + +void subghz_keystore_free(SubGhzKeystore* instance) { + furi_assert(instance); + + for + M_EACH(manufacture_code, instance->data, SubGhzKeyArray_t) { + furi_string_free(manufacture_code->name); + manufacture_code->key = 0; + } + SubGhzKeyArray_clear(instance->data); + + free(instance); +} + +static void subghz_keystore_add_key( + SubGhzKeystore* instance, + const char* name, + uint64_t key, + uint16_t type) { + SubGhzKey* manufacture_code = SubGhzKeyArray_push_raw(instance->data); + manufacture_code->name = furi_string_alloc_set(name); + manufacture_code->key = key; + manufacture_code->type = type; +} + +static bool subghz_keystore_process_line(SubGhzKeystore* instance, char* line) { + uint64_t key = 0; + uint16_t type = 0; + char skey[17] = {0}; + char name[65] = {0}; + int ret = sscanf(line, "%16s:%hu:%64s", skey, &type, name); + key = strtoull(skey, NULL, 16); + if(ret == 3) { + subghz_keystore_add_key(instance, name, key, type); + return true; + } else { + FURI_LOG_E(TAG, "Failed to load line: %s\r\n", line); + return false; + } +} + +static void subghz_keystore_mess_with_iv(uint8_t* iv) { + // Alignment check for `ldrd` instruction + furi_assert(((uint32_t)iv) % 4 == 0); + // Please do not share decrypted manufacture keys + // Sharing them will bring some discomfort to legal owners + // And potential legal action against you + // While you reading this code think about your own personal responsibility + asm volatile("nani%=: \n" + "ldrd r0, r2, [%0, #0x0] \n" + "lsl r1, r0, #8 \n" + "lsl r3, r2, #8 \n" + "orr r3, r3, r0, lsr #24\n" + "uadd8 r1, r1, r0 \n" + "uadd8 r3, r3, r2 \n" + "strd r1, r3, [%0, #0x0] \n" + "ldrd r1, r3, [%0, #0x8] \n" + "lsl r0, r1, #8 \n" + "orr r0, r0, r2, lsr #24\n" + "lsl r2, r3, #8 \n" + "orr r2, r2, r1, lsr #24\n" + "uadd8 r1, r1, r0 \n" + "uadd8 r3, r3, r2 \n" + "strd r1, r3, [%0, #0x8] \n" + : + : "r"(iv) + : "r0", "r1", "r2", "r3", "memory"); +} + +static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, uint8_t* iv) { + bool result = true; + uint8_t buffer[FILE_BUFFER_SIZE]; + + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + size_t encrypted_line_cursor = 0; + + do { + 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; + } + } + + 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, "Invalid encrypted data: %s", encrypted_line); + } + } 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; + } + } + } + } while(ret > 0 && result); + + if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + } while(false); + + free(encrypted_line); + free(decrypted_line); + + return result; +} + +bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { + furi_assert(instance); + bool result = false; + uint8_t iv[16]; + uint32_t version; + uint32_t encryption; + + FuriString* filetype; + filetype = furi_string_alloc(); + + FURI_LOG_I(TAG, "Loading keystore %s", file_name); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); + break; + } + if(!flipper_format_read_header(flipper_format, filetype, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + if(encryption == SubGhzKeystoreEncryptionNone) { + result = subghz_keystore_read_file(instance, stream, NULL); + } else if(encryption == SubGhzKeystoreEncryptionAES256) { + if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Missing IV"); + break; + } + subghz_keystore_mess_with_iv(iv); + result = subghz_keystore_read_file(instance, stream, iv); + } else { + FURI_LOG_E(TAG, "Unknown encryption"); + break; + } + } while(0); + flipper_format_free(flipper_format); + + furi_record_close(RECORD_STORAGE); + + furi_string_free(filetype); + + return result; +} + +bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8_t* iv) { + furi_assert(instance); + bool result = false; + + Storage* storage = furi_record_open(RECORD_STORAGE); + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_always(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", file_name); + break; + } + if(!flipper_format_write_header_cstr( + flipper_format, SUBGHZ_KEYSTORE_FILE_TYPE, SUBGHZ_KEYSTORE_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + uint32_t encryption = SubGhzKeystoreEncryptionAES256; + if(!flipper_format_write_uint32(flipper_format, "Encryption", &encryption, 1)) { + FURI_LOG_E(TAG, "Unable to add Encryption"); + break; + } + if(!flipper_format_write_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Unable to add IV"); + break; + } + + subghz_keystore_mess_with_iv(iv); + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + size_t encrypted_line_count = 0; + for + M_EACH(key, instance->data, SubGhzKeyArray_t) { + // Wipe buffer before packing + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + // Form unecreypted line + int len = snprintf( + decrypted_line, + SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE, + "%08lX%08lX:%hu:%s", + (uint32_t)(key->key >> 32), + (uint32_t)key->key, + key->type, + furi_string_get_cstr(key->name)); + // Verify length and align + furi_assert(len > 0); + if(len % 16 != 0) { + len += (16 - len % 16); + } + furi_assert(len % 16 == 0); + furi_assert(len <= SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + // Form encrypted line + if(!furi_hal_crypto_encrypt( + (uint8_t*)decrypted_line, (uint8_t*)encrypted_line, len)) { + FURI_LOG_E(TAG, "Encryption failed"); + break; + } + // HEX Encode encrypted line + const char xx[] = "0123456789ABCDEF"; + for(int i = 0; i < len; i++) { + size_t cursor = len - i - 1; + size_t hex_cursor = len * 2 - i * 2 - 1; + encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; + encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; + } + stream_write_cstring(stream, encrypted_line); + stream_write_char(stream, '\n'); + encrypted_line_count++; + } + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + size_t total_keys = SubGhzKeyArray_size(instance->data); + result = encrypted_line_count == total_keys; + if(result) { + FURI_LOG_I(TAG, "Success. Encrypted: %zu of %zu", encrypted_line_count, total_keys); + } else { + FURI_LOG_E(TAG, "Failure. Encrypted: %zu of %zu", encrypted_line_count, total_keys); + } + } while(0); + flipper_format_free(flipper_format); + + free(encrypted_line); + free(decrypted_line); + furi_record_close(RECORD_STORAGE); + + return result; +} + +SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance) { + furi_assert(instance); + return &instance->data; +} + +bool subghz_keystore_raw_encrypted_save( + const char* input_file_name, + const char* output_file_name, + uint8_t* iv) { + bool encrypted = false; + uint32_t version; + uint32_t encryption; + FuriString* filetype; + filetype = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + + char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + + FlipperFormat* input_flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(input_flipper_format, input_file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", input_file_name); + break; + } + if(!flipper_format_read_header(input_flipper_format, filetype, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32( + input_flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + if(encryption != SubGhzKeystoreEncryptionNone) { + FURI_LOG_E(TAG, "Already encryption"); + break; + } + Stream* input_stream = flipper_format_get_raw_stream(input_flipper_format); + + FlipperFormat* output_flipper_format = flipper_format_file_alloc(storage); + + if(!flipper_format_file_open_always(output_flipper_format, output_file_name)) { + FURI_LOG_E(TAG, "Unable to open file for write: %s", output_file_name); + break; + } + if(!flipper_format_write_header_cstr( + output_flipper_format, + furi_string_get_cstr(filetype), + SUBGHZ_KEYSTORE_FILE_VERSION)) { + FURI_LOG_E(TAG, "Unable to add header"); + break; + } + uint32_t encryption = SubGhzKeystoreEncryptionAES256; + if(!flipper_format_write_uint32(output_flipper_format, "Encryption", &encryption, 1)) { + FURI_LOG_E(TAG, "Unable to add Encryption"); + break; + } + if(!flipper_format_write_hex(output_flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Unable to add IV"); + break; + } + + if(!flipper_format_write_string_cstr(output_flipper_format, "Encrypt_data", "RAW")) { + FURI_LOG_E(TAG, "Unable to add Encrypt_data"); + break; + } + + subghz_keystore_mess_with_iv(iv); + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + Stream* output_stream = flipper_format_get_raw_stream(output_flipper_format); + uint8_t buffer[FILE_BUFFER_SIZE]; + bool result = true; + + size_t ret = 0; + furi_assert(FILE_BUFFER_SIZE % 16 == 0); + + //skip the end of the previous line "\n" + stream_read(input_stream, buffer, 1); + + do { + memset(buffer, 0, FILE_BUFFER_SIZE); + ret = stream_read(input_stream, buffer, FILE_BUFFER_SIZE); + if(ret == 0) { + break; + } + + for(uint16_t i = 0; i < FILE_BUFFER_SIZE - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + buffer[i / 2] = (hi_nibble << 4) | lo_nibble; + } + + memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); + // Form encrypted line + if(!furi_hal_crypto_encrypt( + (uint8_t*)buffer, (uint8_t*)encrypted_line, FILE_BUFFER_SIZE / 2)) { + FURI_LOG_E(TAG, "Encryption failed"); + result = false; + break; + } + + // HEX Encode encrypted line + const char xx[] = "0123456789ABCDEF"; + for(size_t i = 0; i < FILE_BUFFER_SIZE / 2; i++) { + size_t cursor = FILE_BUFFER_SIZE / 2 - i - 1; + size_t hex_cursor = FILE_BUFFER_SIZE - i * 2 - 1; + encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; + encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; + } + stream_write_cstring(output_stream, encrypted_line); + + } while(true); + + flipper_format_free(output_flipper_format); + + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + + if(!result) break; + + encrypted = true; + } while(0); + + flipper_format_free(input_flipper_format); + + free(encrypted_line); + + furi_record_close(RECORD_STORAGE); + + return encrypted; +} + +bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len) { + bool result = false; + uint8_t iv[16]; + uint32_t version; + uint32_t encryption; + + FuriString* str_temp; + str_temp = furi_string_alloc(); + + Storage* storage = furi_record_open(RECORD_STORAGE); + char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + + FlipperFormat* flipper_format = flipper_format_file_alloc(storage); + do { + if(!flipper_format_file_open_existing(flipper_format, file_name)) { + FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); + break; + } + if(!flipper_format_read_header(flipper_format, str_temp, &version)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { + FURI_LOG_E(TAG, "Missing encryption type"); + break; + } + + if(strcmp(furi_string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || + version != SUBGHZ_KEYSTORE_FILE_VERSION) { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + Stream* stream = flipper_format_get_raw_stream(flipper_format); + if(encryption != SubGhzKeystoreEncryptionAES256) { + FURI_LOG_E(TAG, "Unknown encryption"); + break; + } + + if(offset < 16) { + if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { + FURI_LOG_E(TAG, "Missing IV"); + break; + } + subghz_keystore_mess_with_iv(iv); + } + + if(!flipper_format_read_string(flipper_format, "Encrypt_data", str_temp)) { + FURI_LOG_E(TAG, "Missing Encrypt_data"); + break; + } + + size_t bufer_size; + if(len <= (16 - offset % 16)) { + bufer_size = 32; + } else { + bufer_size = (((len) / 16) + 2) * 32; + } + furi_assert(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE >= bufer_size / 2); + + uint8_t buffer[bufer_size]; + size_t ret = 0; + bool decrypted = true; + //skip the end of the previous line "\n" + stream_read(stream, buffer, 1); + + size_t size = stream_size(stream); + size -= stream_tell(stream); + if(size < (offset * 2 + len * 2)) { + FURI_LOG_E(TAG, "Seek position exceeds file size"); + break; + } + + if(offset >= 16) { + stream_seek(stream, ((offset / 16) - 1) * 32, StreamOffsetFromCurrent); + ret = stream_read(stream, buffer, 32); + furi_assert(ret == 32); + for(uint16_t i = 0; i < ret - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + iv[i / 2] = (hi_nibble << 4) | lo_nibble; + } + } + + if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { + FURI_LOG_E(TAG, "Unable to load encryption key"); + break; + } + + do { + memset(buffer, 0, bufer_size); + ret = stream_read(stream, buffer, bufer_size); + furi_assert(ret == bufer_size); + for(uint16_t i = 0; i < ret - 1; i += 2) { + uint8_t hi_nibble = 0; + uint8_t lo_nibble = 0; + hex_char_to_hex_nibble(buffer[i], &hi_nibble); + hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); + buffer[i / 2] = (hi_nibble << 4) | lo_nibble; + } + + memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); + + if(!furi_hal_crypto_decrypt( + (uint8_t*)buffer, (uint8_t*)decrypted_line, bufer_size / 2)) { + decrypted = false; + FURI_LOG_E(TAG, "Decryption failed"); + break; + } + memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); + + } while(0); + furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); + if(decrypted) result = true; + } while(0); + flipper_format_free(flipper_format); + + furi_record_close(RECORD_STORAGE); + + free(decrypted_line); + + furi_string_free(str_temp); + + return result; +} diff --git a/applications/main/subghz/subghz_keystore.h b/applications/main/subghz/subghz_keystore.h new file mode 100644 index 000000000..06ae8adae --- /dev/null +++ b/applications/main/subghz/subghz_keystore.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + FuriString* name; + uint64_t key; + uint16_t type; +} SubGhzKey; + +ARRAY_DEF(SubGhzKeyArray, SubGhzKey, M_POD_OPLIST) + +#define M_OPL_SubGhzKeyArray_t() ARRAY_OPLIST(SubGhzKeyArray, M_POD_OPLIST) + +typedef struct SubGhzKeystore SubGhzKeystore; + +/** + * Allocate SubGhzKeystore. + * @return SubGhzKeystore* pointer to a SubGhzKeystore instance + */ +SubGhzKeystore* subghz_keystore_alloc(); + +/** + * Free SubGhzKeystore. + * @param instance Pointer to a SubGhzKeystore instance + */ +void subghz_keystore_free(SubGhzKeystore* instance); + +/** + * Loading manufacture key from file + * @param instance Pointer to a SubGhzKeystore instance + * @param filename Full path to the file + */ +bool subghz_keystore_load(SubGhzKeystore* instance, const char* filename); + +/** + * Save manufacture key to file + * @param instance Pointer to a SubGhzKeystore instance + * @param filename Full path to the file + * @return true On success + */ +bool subghz_keystore_save(SubGhzKeystore* instance, const char* filename, uint8_t* iv); + +/** + * Get array of keys and names manufacture + * @param instance Pointer to a SubGhzKeystore instance + * @return SubGhzKeyArray_t* + */ +SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance); + +/** + * Save RAW encrypted to file + * @param input_file_name Full path to the input file + * @param output_file_name Full path to the output file + * @param iv IV, 16 bytes in hex + */ +bool subghz_keystore_raw_encrypted_save( + const char* input_file_name, + const char* output_file_name, + uint8_t* iv); + +/** + * Get decrypt RAW data to file + * @param file_name Full path to the input file + * @param offset Offset from the start of the RAW data + * @param data Returned array + * @param len Required data length + * @return true On success + */ +bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_last_settings.c b/applications/main/subghz/subghz_last_settings.c index 478e32347..8e7016df7 100644 --- a/applications/main/subghz/subghz_last_settings.c +++ b/applications/main/subghz/subghz_last_settings.c @@ -1,8 +1,5 @@ #include "subghz_last_settings.h" #include "subghz_i.h" -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#include -#endif #define TAG "SubGhzLastSettings" @@ -16,11 +13,6 @@ #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL 2 #define SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER -93.0f -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#define SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW 0 -#define SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW "DetectRaw" -#endif - #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY "Frequency" //#define SUBGHZ_LAST_SETTING_FIELD_PRESET "Preset" #define SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_FEEDBACK_LEVEL "FeedbackLevel" @@ -52,9 +44,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count //int32_t temp_preset = 0; bool frequency_analyzer_feedback_level_was_read = false; bool frequency_analyzer_trigger_was_read = false; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - uint32_t temp_read_raw = 0; -#endif if(FSE_OK == storage_sd_status(storage) && SUBGHZ_LAST_SETTINGS_PATH && flipper_format_file_open_existing(fff_data_file, SUBGHZ_LAST_SETTINGS_PATH)) { @@ -73,10 +62,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count SUBGHZ_LAST_SETTING_FIELD_FREQUENCY_ANALYZER_TRIGGER, (float*)&temp_frequency_analyzer_trigger, 1); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - flipper_format_read_uint32( - fff_data_file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, (uint32_t*)&temp_read_raw, 1); -#endif + } else { FURI_LOG_E(TAG, "Error open file %s", SUBGHZ_LAST_SETTINGS_PATH); } @@ -88,9 +74,7 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->frequency_analyzer_feedback_level = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_FEEDBACK_LEVEL; instance->frequency_analyzer_trigger = SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - instance->detect_raw = SUBGHZ_LAST_SETTING_DEFAULT_READ_RAW; -#endif + } else { instance->frequency = temp_frequency; instance->frequency_analyzer_feedback_level = @@ -101,9 +85,6 @@ void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count instance->frequency_analyzer_trigger = frequency_analyzer_trigger_was_read ? temp_frequency_analyzer_trigger : SUBGHZ_LAST_SETTING_FREQUENCY_ANALYZER_TRIGGER; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - instance->detect_raw = temp_read_raw; -#endif /*if(temp_preset > (int32_t)preset_count - 1 || temp_preset < 0) { FURI_LOG_W(TAG, "Last used preset no found");*/ @@ -164,12 +145,6 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { 1)) { break; } -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - if(!flipper_format_insert_or_update_uint32( - file, SUBGHZ_LAST_SETTING_FIELD_DETECT_RAW, &instance->detect_raw, 1)) { - break; - } -#endif saved = true; } while(0); @@ -183,17 +158,3 @@ bool subghz_last_settings_save(SubGhzLastSettings* instance) { return saved; } - -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -void subghz_last_settings_set_detect_raw_values(void* context) { - furi_assert(context); - SubGhz* instance = (SubGhz*)context; - bool is_detect_raw = instance->last_settings->detect_raw > 0; - subghz_receiver_set_filter( - instance->txrx->receiver, is_detect_raw ? DETECT_RAW_TRUE : DETECT_RAW_FALSE); - subghz_protocol_decoder_raw_set_auto_mode( - subghz_receiver_search_decoder_base_by_name( - instance->txrx->receiver, SUBGHZ_PROTOCOL_RAW_NAME), - is_detect_raw); -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/subghz_last_settings.h b/applications/main/subghz/subghz_last_settings.h index ef3674d69..f08d99c81 100644 --- a/applications/main/subghz/subghz_last_settings.h +++ b/applications/main/subghz/subghz_last_settings.h @@ -1,23 +1,12 @@ #pragma once -// Enable saving detect raw setting state -// #define SUBGHZ_SAVE_DETECT_RAW_SETTING 1 - #include #include #include #include -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -#include -#define DETECT_RAW_FALSE SubGhzProtocolFlag_Decodable -#define DETECT_RAW_TRUE SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_RAW -#endif typedef struct { uint32_t frequency; -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING - uint32_t detect_raw; -#endif int32_t preset; uint32_t frequency_analyzer_feedback_level; float frequency_analyzer_trigger; @@ -30,6 +19,3 @@ void subghz_last_settings_free(SubGhzLastSettings* instance); void subghz_last_settings_load(SubGhzLastSettings* instance, size_t preset_count); bool subghz_last_settings_save(SubGhzLastSettings* instance); -#ifdef SUBGHZ_SAVE_DETECT_RAW_SETTING -void subghz_last_settings_set_detect_raw_values(void* context); -#endif \ No newline at end of file diff --git a/applications/main/subghz/subghz_setting.c b/applications/main/subghz/subghz_setting.c new file mode 100644 index 000000000..35ba54a8a --- /dev/null +++ b/applications/main/subghz/subghz_setting.c @@ -0,0 +1,480 @@ +#include "subghz_setting.h" +#include "types.h" +//#include "subghz_i.h" + +#include +#include +#include + +#define TAG "SubGhzSetting" + +#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File" +#define SUBGHZ_SETTING_FILE_VERSION 1 + +#define FREQUENCY_FLAG_DEFAULT (1 << 31) +#define FREQUENCY_MASK (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT) + +/* Default */ +static const uint32_t subghz_frequency_list[] = { + /* 300 - 348 */ + 300000000, + 302757000, + 303875000, + 304250000, + 307000000, + 307500000, + 307800000, + 309000000, + 310000000, + 312000000, + 312100000, + 312200000, + 313000000, + 313850000, + 314000000, + 314350000, + 314980000, + 315000000, + 318000000, + 330000000, + 345000000, + 348000000, + 350000000, + + /* 387 - 464 */ + 387000000, + 390000000, + 418000000, + 433075000, /* LPD433 first */ + 433220000, + 433420000, + 433657070, + 433889000, + 433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */ + 434075000, + 434176948, + 434190000, + 434390000, + 434420000, + 434620000, + 434775000, /* LPD433 last channels */ + 438900000, + 440175000, + 464000000, + + /* 779 - 928 */ + 779000000, + 868350000, + 868400000, + 868800000, + 868950000, + 906400000, + 915000000, + 925000000, + 928000000, + 0, +}; + +static const uint32_t subghz_hopper_frequency_list[] = { + 315000000, + 330000000, + 390000000, + 433420000, + 433920000, + 868350000, + 0, +}; + +typedef struct { + FuriString* custom_preset_name; + uint8_t* custom_preset_data; + size_t custom_preset_data_size; +} SubGhzSettingCustomPresetItem; + +ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST) + +#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \ + ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST) + +LIST_DEF(FrequencyList, uint32_t) + +#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) + +typedef struct { + SubGhzSettingCustomPresetItemArray_t data; +} SubGhzSettingCustomPresetStruct; + +struct SubGhzSetting { + FrequencyList_t frequencies; + FrequencyList_t hopper_frequencies; + SubGhzSettingCustomPresetStruct* preset; +}; + +SubGhzSetting* subghz_setting_alloc(void) { + SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); + FrequencyList_init(instance->frequencies); + FrequencyList_init(instance->hopper_frequencies); + instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); + SubGhzSettingCustomPresetItemArray_init(instance->preset->data); + return instance; +} + +static void subghz_setting_preset_reset(SubGhzSetting* instance) { + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_reset(instance->preset->data); +} + +void subghz_setting_free(SubGhzSetting* instance) { + furi_assert(instance); + FrequencyList_clear(instance->frequencies); + FrequencyList_clear(instance->hopper_frequencies); + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + } + SubGhzSettingCustomPresetItemArray_clear(instance->preset->data); + free(instance->preset); + free(instance); +} + +static void subghz_setting_load_default_preset( + SubGhzSetting* instance, + const char* preset_name, + const uint8_t* preset_data, + const uint8_t preset_pa_table[8]) { + furi_assert(instance); + furi_assert(preset_data); + uint32_t preset_data_count = 0; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); + + while(preset_data[preset_data_count]) { + preset_data_count += 2; + } + preset_data_count += 2; + item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; + item->custom_preset_data = malloc(item->custom_preset_data_size); + //load preset register + memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); + //load pa table + memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); +} + +static void subghz_setting_load_default_region( + SubGhzSetting* instance, + const uint32_t frequencies[], + const uint32_t hopper_frequencies[]) { + furi_assert(instance); + + FrequencyList_reset(instance->frequencies); + FrequencyList_reset(instance->hopper_frequencies); + subghz_setting_preset_reset(instance); + + while(*frequencies) { + FrequencyList_push_back(instance->frequencies, *frequencies); + frequencies++; + } + + while(*hopper_frequencies) { + FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies); + hopper_frequencies++; + } + + subghz_setting_load_default_preset( + instance, + "AM270", + (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "AM650", + (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, + furi_hal_subghz_preset_ook_async_patable); + subghz_setting_load_default_preset( + instance, + "FM238", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); + subghz_setting_load_default_preset( + instance, + "FM476", + (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, + furi_hal_subghz_preset_2fsk_async_patable); +} + +// Region check removed +void subghz_setting_load_default(SubGhzSetting* instance) { + subghz_setting_load_default_region( + instance, subghz_frequency_list, subghz_hopper_frequency_list); +} + +void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { + furi_assert(instance); + + Storage* storage = furi_record_open(RECORD_STORAGE); + FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); + + FuriString* temp_str; + temp_str = furi_string_alloc(); + uint32_t temp_data32; + bool temp_bool; + + subghz_setting_load_default(instance); + + if(file_path) { + do { + if(!flipper_format_file_open_existing(fff_data_file, file_path)) { + FURI_LOG_I(TAG, "File is not used %s", file_path); + break; + } + + if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { + FURI_LOG_E(TAG, "Missing or incorrect header"); + break; + } + + if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) && + temp_data32 == SUBGHZ_SETTING_FILE_VERSION) { + } else { + FURI_LOG_E(TAG, "Type or version mismatch"); + break; + } + + // Standard frequencies (optional) + temp_bool = true; + flipper_format_read_bool(fff_data_file, "Add_standard_frequencies", &temp_bool, 1); + if(!temp_bool) { + FURI_LOG_I(TAG, "Removing standard frequencies"); + FrequencyList_reset(instance->frequencies); + FrequencyList_reset(instance->hopper_frequencies); + } else { + FURI_LOG_I(TAG, "Keeping standard frequencies"); + } + + // Load frequencies + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_uint32( + fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp_data32)) { + FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); + FrequencyList_push_back(instance->frequencies, temp_data32); + } else { + FURI_LOG_E(TAG, "Frequency not supported %lu", temp_data32); + } + } + + // Load hopper frequencies + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_uint32( + fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) { + if(furi_hal_subghz_is_frequency_valid(temp_data32)) { + FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32); + FrequencyList_push_back(instance->hopper_frequencies, temp_data32); + } else { + FURI_LOG_E(TAG, "Hopper frequency not supported %lu", temp_data32); + } + } + + // Default frequency (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) { + subghz_setting_set_default_frequency(instance, temp_data32); + } + + // custom preset (optional) + if(!flipper_format_rewind(fff_data_file)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { + FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); + subghz_setting_load_custom_preset( + instance, furi_string_get_cstr(temp_str), fff_data_file); + } + + } while(false); + } + + furi_string_free(temp_str); + flipper_format_free(fff_data_file); + furi_record_close(RECORD_STORAGE); + + if(!FrequencyList_size(instance->frequencies) || + !FrequencyList_size(instance->hopper_frequencies)) { + FURI_LOG_E(TAG, "Error loading user settings, loading default settings"); + subghz_setting_load_default(instance); + } +} + +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup) { + for + M_EACH(frequency, instance->frequencies, FrequencyList_t) { + *frequency &= FREQUENCY_MASK; + if(*frequency == frequency_to_setup) { + *frequency |= FREQUENCY_FLAG_DEFAULT; + } + } +} + +size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) { + furi_assert(instance); + return FrequencyList_size(instance->frequencies); +} + +size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) { + furi_assert(instance); + return FrequencyList_size(instance->hopper_frequencies); +} + +size_t subghz_setting_get_preset_count(SubGhzSetting* instance) { + furi_assert(instance); + return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); +} + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { + idx = 0; + } + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return furi_string_get_cstr(item->custom_preset_name); +} + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + size_t idx = 0; + for + M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + return idx; + } + idx++; + } + furi_crash("SubGhz: No name preset."); + return -1; +} + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file) { + furi_assert(instance); + furi_assert(preset_name); + uint32_t temp_data32; + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); + item->custom_preset_name = furi_string_alloc(); + furi_string_set(item->custom_preset_name, preset_name); + do { + if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) + break; + if(!temp_data32 || (temp_data32 % 2)) { + FURI_LOG_E(TAG, "Integrity error Custom_preset_data"); + break; + } + item->custom_preset_data_size = sizeof(uint8_t) * temp_data32; + item->custom_preset_data = malloc(item->custom_preset_data_size); + if(!flipper_format_read_hex( + fff_data_file, + "Custom_preset_data", + item->custom_preset_data, + item->custom_preset_data_size)) { + FURI_LOG_E(TAG, "Missing Custom_preset_data"); + break; + } + return true; + } while(true); + return false; +} + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + furi_assert(preset_name); + SubGhzSettingCustomPresetItemArray_it_t it; + SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data); + while(!SubGhzSettingCustomPresetItemArray_end_p(it)) { + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it); + if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { + furi_string_free(item->custom_preset_name); + free(item->custom_preset_data); + SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it); + return true; + } + SubGhzSettingCustomPresetItemArray_previous(it); + } + return false; +} + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data; +} + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = + SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); + return item->custom_preset_data_size; +} + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) { + furi_assert(instance); + SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get( + instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name)); + return item->custom_preset_data; +} + +uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx < FrequencyList_size(instance->frequencies)) { + return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK; + } else { + return 0; + } +} + +uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) { + furi_assert(instance); + if(idx < FrequencyList_size(instance->frequencies)) { + return *FrequencyList_get(instance->hopper_frequencies, idx); + } else { + return 0; + } +} + +uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) { + furi_assert(instance); + for(size_t i = 0; i < FrequencyList_size(instance->frequencies); i++) { + uint32_t frequency = *FrequencyList_get(instance->frequencies, i); + if(frequency & FREQUENCY_FLAG_DEFAULT) { + return i; + } + } + return 0; +} + +uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) { + furi_assert(instance); + return subghz_setting_get_frequency( + instance, subghz_setting_get_frequency_default_index(instance)); +} diff --git a/applications/main/subghz/subghz_setting.h b/applications/main/subghz/subghz_setting.h new file mode 100644 index 000000000..3cb07ff6d --- /dev/null +++ b/applications/main/subghz/subghz_setting.h @@ -0,0 +1,58 @@ + +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 + +typedef struct SubGhzSetting SubGhzSetting; + +SubGhzSetting* subghz_setting_alloc(void); + +void subghz_setting_free(SubGhzSetting* instance); + +void subghz_setting_load(SubGhzSetting* instance, const char* file_path); + +size_t subghz_setting_get_frequency_count(SubGhzSetting* instance); + +size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance); + +size_t subghz_setting_get_preset_count(SubGhzSetting* instance); + +const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx); + +int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name); + +uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx); + +size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx); + +uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name); + +bool subghz_setting_load_custom_preset( + SubGhzSetting* instance, + const char* preset_name, + FlipperFormat* fff_data_file); + +bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name); + +uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx); + +uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx); + +uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); + +uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); + +void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_tx_rx_worker.c b/applications/main/subghz/subghz_tx_rx_worker.c new file mode 100644 index 000000000..e380fc967 --- /dev/null +++ b/applications/main/subghz/subghz_tx_rx_worker.c @@ -0,0 +1,260 @@ +#include "subghz_tx_rx_worker.h" + +#include + +#define TAG "SubGhzTxRxWorker" + +#define SUBGHZ_TXRX_WORKER_BUF_SIZE 2048 +//you can not set more than 62 because it will not fit into the FIFO CC1101 +#define SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60 + +#define SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40 + +struct SubGhzTxRxWorker { + FuriThread* thread; + FuriStreamBuffer* stream_tx; + FuriStreamBuffer* stream_rx; + + volatile bool worker_running; + volatile bool worker_stoping; + + SubGhzTxRxWorkerStatus status; + + uint32_t frequency; + + SubGhzTxRxWorkerCallbackHaveRead callback_have_read; + void* context_have_read; +}; + +bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + furi_assert(instance); + bool ret = false; + size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx); + if(size && (stream_tx_free_byte >= size)) { + if(furi_stream_buffer_send( + instance->stream_tx, data, size, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) == + size) { + ret = true; + } + } + return ret; +} + +size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) { + furi_assert(instance); + return furi_stream_buffer_bytes_available(instance->stream_rx); +} + +size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + furi_assert(instance); + return furi_stream_buffer_receive(instance->stream_rx, data, size, 0); +} + +void subghz_tx_rx_worker_set_callback_have_read( + SubGhzTxRxWorker* instance, + SubGhzTxRxWorkerCallbackHaveRead callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + furi_assert(context); + instance->callback_have_read = callback; + instance->context_have_read = context; +} + +bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) { + uint8_t timeout = 100; + bool ret = false; + if(instance->status != SubGhzTxRxWorkerStatusRx) { + furi_hal_subghz_rx(); + instance->status = SubGhzTxRxWorkerStatusRx; + furi_delay_tick(1); + } + //waiting for reception to complete + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + break; + } + } + + if(furi_hal_subghz_rx_pipe_not_empty()) { + FURI_LOG_I( + TAG, + "RSSI: %03.1fdbm LQI: %d", + (double)furi_hal_subghz_get_rssi(), + furi_hal_subghz_get_lqi()); + if(furi_hal_subghz_is_rx_data_crc_valid()) { + furi_hal_subghz_read_packet(data, size); + ret = true; + } + furi_hal_subghz_flush_rx(); + furi_hal_subghz_rx(); + } + return ret; +} + +void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { + uint8_t timeout = 200; + if(instance->status != SubGhzTxRxWorkerStatusIDLE) { + furi_hal_subghz_idle(); + } + furi_hal_subghz_write_packet(data, size); + furi_hal_subghz_tx(); //start send + instance->status = SubGhzTxRxWorkerStatusTx; + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); + break; + } + } + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet + furi_delay_tick(1); + if(!--timeout) { + FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); + break; + } + } + furi_hal_subghz_idle(); + instance->status = SubGhzTxRxWorkerStatusIDLE; +} +/** Worker thread + * + * @param context + * @return exit code + */ +static int32_t subghz_tx_rx_worker_thread(void* context) { + SubGhzTxRxWorker* instance = context; + FURI_LOG_I(TAG, "Worker start"); + + furi_hal_subghz_reset(); + furi_hal_subghz_idle(); + furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); + //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + + furi_hal_subghz_set_frequency_and_path(instance->frequency); + furi_hal_subghz_flush_rx(); + + uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; + size_t size_tx = 0; + uint8_t size_rx[1] = {0}; + uint8_t timeout_tx = 0; + bool callback_rx = false; + + while(instance->worker_running) { + //transmit + size_tx = furi_stream_buffer_bytes_available(instance->stream_tx); + if(size_tx > 0 && !timeout_tx) { + timeout_tx = 10; //20ms + if(size_tx > SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) { + furi_stream_buffer_receive( + instance->stream_tx, + &data, + SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE, + SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); + } else { + //todo checking that he managed to write all the data to the TX buffer + furi_stream_buffer_receive( + instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + subghz_tx_rx_worker_tx(instance, data, size_tx); + } + } else { + //recive + if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { + if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) { + if(instance->callback_have_read && + furi_stream_buffer_bytes_available(instance->stream_rx) == 0) { + callback_rx = true; + } + //todo checking that he managed to write all the data to the RX buffer + furi_stream_buffer_send( + instance->stream_rx, + &data, + size_rx[0], + SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); + if(callback_rx) { + instance->callback_have_read(instance->context_have_read); + callback_rx = false; + } + } else { + //todo RX buffer overflow + } + } + } + + if(timeout_tx) timeout_tx--; + furi_delay_tick(1); + } + + furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); + furi_hal_subghz_sleep(); + + FURI_LOG_I(TAG, "Worker stop"); + return 0; +} + +SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { + SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); + instance->stream_tx = + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + instance->stream_rx = + furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); + + instance->status = SubGhzTxRxWorkerStatusIDLE; + instance->worker_stoping = true; + + return instance; +} + +void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { + furi_assert(instance); + furi_assert(!instance->worker_running); + furi_stream_buffer_free(instance->stream_tx); + furi_stream_buffer_free(instance->stream_rx); + furi_thread_free(instance->thread); + + free(instance); +} + +bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { + furi_assert(instance); + furi_assert(!instance->worker_running); + bool res = false; + furi_stream_buffer_reset(instance->stream_tx); + furi_stream_buffer_reset(instance->stream_rx); + + instance->worker_running = true; + + if(furi_hal_subghz_is_tx_allowed(frequency)) { + instance->frequency = frequency; + res = true; + } + + furi_thread_start(instance->thread); + + return res; +} + +void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) { + furi_assert(instance); + furi_assert(instance->worker_running); + + instance->worker_running = false; + + furi_thread_join(instance->thread); +} + +bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) { + furi_assert(instance); + return instance->worker_running; +} diff --git a/applications/main/subghz/subghz_tx_rx_worker.h b/applications/main/subghz/subghz_tx_rx_worker.h new file mode 100644 index 000000000..ddc02e749 --- /dev/null +++ b/applications/main/subghz/subghz_tx_rx_worker.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context); + +typedef struct SubGhzTxRxWorker SubGhzTxRxWorker; + +typedef enum { + SubGhzTxRxWorkerStatusIDLE, + SubGhzTxRxWorkerStatusTx, + SubGhzTxRxWorkerStatusRx, +} SubGhzTxRxWorkerStatus; + +/** + * SubGhzTxRxWorker, add data to transfer + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param data *data + * @param size data size + * @return bool true if ok + */ +bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); + +/** + * SubGhzTxRxWorker, get available data + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return size_t data size + */ +size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance); + +/** + * SubGhzTxRxWorker, read data + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param data *data + * @param size max data size, which can be read + * @return size_t data size, how much is actually read + */ +size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); + +/** + * Сallback SubGhzTxRxWorker when there is data to read in an empty buffer + * @param instance Pointer to a SubGhzTxRxWorker instance + * @param callback SubGhzTxRxWorkerCallbackHaveRead callback + * @param context + */ +void subghz_tx_rx_worker_set_callback_have_read( + SubGhzTxRxWorker* instance, + SubGhzTxRxWorkerCallbackHaveRead callback, + void* context); + +/** + * Allocate SubGhzTxRxWorker + * @return SubGhzTxRxWorker* Pointer to a SubGhzTxRxWorker instance + */ +SubGhzTxRxWorker* subghz_tx_rx_worker_alloc(); + +/** + * Free SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + */ +void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); + +/** + * Start SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return bool - true if ok + */ +bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); + +/** + * Stop SubGhzTxRxWorker + * @param instance Pointer to a SubGhzTxRxWorker instance + */ +void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance); + +/** + * Check if worker is running + * @param instance Pointer to a SubGhzTxRxWorker instance + * @return bool - true if running + */ +bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/subghz_worker.c b/applications/main/subghz/subghz_worker.c new file mode 100644 index 000000000..50b5aba51 --- /dev/null +++ b/applications/main/subghz/subghz_worker.c @@ -0,0 +1,149 @@ +#include "subghz_worker.h" + +#include + +#define TAG "SubGhzWorker" + +struct SubGhzWorker { + FuriThread* thread; + FuriStreamBuffer* stream; + + volatile bool running; + volatile bool overrun; + + LevelDuration filter_level_duration; + uint16_t filter_duration; + + SubGhzWorkerOverrunCallback overrun_callback; + SubGhzWorkerPairCallback pair_callback; + void* context; +}; + +/** Rx callback timer + * + * @param level received signal level + * @param duration received signal duration + * @param context + */ +void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { + SubGhzWorker* instance = context; + + LevelDuration level_duration = level_duration_make(level, duration); + if(instance->overrun) { + instance->overrun = false; + level_duration = level_duration_reset(); + } + size_t ret = + furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); + if(sizeof(LevelDuration) != ret) instance->overrun = true; +} + +/** Worker callback thread + * + * @param context + * @return exit code + */ +static int32_t subghz_worker_thread_callback(void* context) { + SubGhzWorker* instance = context; + + LevelDuration level_duration; + while(instance->running) { + int ret = furi_stream_buffer_receive( + instance->stream, &level_duration, sizeof(LevelDuration), 10); + if(ret == sizeof(LevelDuration)) { + if(level_duration_is_reset(level_duration)) { + FURI_LOG_E(TAG, "Overrun buffer"); + if(instance->overrun_callback) instance->overrun_callback(instance->context); + } else { + bool level = level_duration_get_level(level_duration); + uint32_t duration = level_duration_get_duration(level_duration); + + if((duration < instance->filter_duration) || + (instance->filter_level_duration.level == level)) { + instance->filter_level_duration.duration += duration; + + } else if(instance->filter_level_duration.level != level) { + if(instance->pair_callback) + instance->pair_callback( + instance->context, + instance->filter_level_duration.level, + instance->filter_level_duration.duration); + + instance->filter_level_duration.duration = duration; + instance->filter_level_duration.level = level; + } + } + } + } + + return 0; +} + +SubGhzWorker* subghz_worker_alloc() { + SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); + + instance->thread = + furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); + + instance->stream = + furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); + + //setting default filter in us + instance->filter_duration = 30; + + return instance; +} + +void subghz_worker_free(SubGhzWorker* instance) { + furi_assert(instance); + + furi_stream_buffer_free(instance->stream); + furi_thread_free(instance->thread); + + free(instance); +} + +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback) { + furi_assert(instance); + instance->overrun_callback = callback; +} + +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback) { + furi_assert(instance); + instance->pair_callback = callback; +} + +void subghz_worker_set_context(SubGhzWorker* instance, void* context) { + furi_assert(instance); + instance->context = context; +} + +void subghz_worker_start(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(!instance->running); + + instance->running = true; + + furi_thread_start(instance->thread); +} + +void subghz_worker_stop(SubGhzWorker* instance) { + furi_assert(instance); + furi_assert(instance->running); + + instance->running = false; + + furi_thread_join(instance->thread); +} + +bool subghz_worker_is_running(SubGhzWorker* instance) { + furi_assert(instance); + return instance->running; +} + +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { + furi_assert(instance); + instance->filter_duration = timeout; +} \ No newline at end of file diff --git a/applications/main/subghz/subghz_worker.h b/applications/main/subghz/subghz_worker.h new file mode 100644 index 000000000..657278f50 --- /dev/null +++ b/applications/main/subghz/subghz_worker.h @@ -0,0 +1,80 @@ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzWorker SubGhzWorker; + +typedef void (*SubGhzWorkerOverrunCallback)(void* context); + +typedef void (*SubGhzWorkerPairCallback)(void* context, bool level, uint32_t duration); + +void subghz_worker_rx_callback(bool level, uint32_t duration, void* context); + +/** + * Allocate SubGhzWorker. + * @return SubGhzWorker* Pointer to a SubGhzWorker instance + */ +SubGhzWorker* subghz_worker_alloc(); + +/** + * Free SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_free(SubGhzWorker* instance); + +/** + * Overrun callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param callback SubGhzWorkerOverrunCallback callback + */ +void subghz_worker_set_overrun_callback( + SubGhzWorker* instance, + SubGhzWorkerOverrunCallback callback); + +/** + * Pair callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param callback SubGhzWorkerOverrunCallback callback + */ +void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback); + +/** + * Context callback SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + * @param context + */ +void subghz_worker_set_context(SubGhzWorker* instance, void* context); + +/** + * Start SubGhzWorker. + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_start(SubGhzWorker* instance); + +/** Stop SubGhzWorker + * @param instance Pointer to a SubGhzWorker instance + */ +void subghz_worker_stop(SubGhzWorker* instance); + +/** + * Check if worker is running. + * @param instance Pointer to a SubGhzWorker instance + * @return bool - true if running + */ +bool subghz_worker_is_running(SubGhzWorker* instance); + +/** + * Short duration filter setting. + * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled + * @param instance Pointer to a SubGhzWorker instance + * @param timeout time in us + */ +void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/transmitter.c b/applications/main/subghz/transmitter.c new file mode 100644 index 000000000..8507ee054 --- /dev/null +++ b/applications/main/subghz/transmitter.c @@ -0,0 +1,64 @@ +#include "transmitter.h" + +#include "protocols/base.h" +#include "registry.h" +#include "protocols/protocol_items.h" + +struct SubGhzTransmitter { + const SubGhzProtocol* protocol; + SubGhzProtocolEncoderBase* protocol_instance; +}; + +SubGhzTransmitter* + subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { + SubGhzTransmitter* instance = NULL; + const SubGhzProtocolRegistry* protocol_registry_items = + subghz_environment_get_protocol_registry(environment); + + const SubGhzProtocol* protocol = + subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); + + if(protocol && protocol->encoder && protocol->encoder->alloc) { + instance = malloc(sizeof(SubGhzTransmitter)); + instance->protocol = protocol; + instance->protocol_instance = instance->protocol->encoder->alloc(environment); + } + return instance; +} + +void subghz_transmitter_free(SubGhzTransmitter* instance) { + furi_assert(instance); + instance->protocol->encoder->free(instance->protocol_instance); + free(instance); +} + +SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) { + furi_assert(instance); + return instance->protocol_instance; +} + +bool subghz_transmitter_stop(SubGhzTransmitter* instance) { + furi_assert(instance); + bool ret = false; + if(instance->protocol && instance->protocol->encoder && instance->protocol->encoder->stop) { + instance->protocol->encoder->stop(instance->protocol_instance); + ret = true; + } + return ret; +} + +bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) { + furi_assert(instance); + bool ret = false; + if(instance->protocol && instance->protocol->encoder && + instance->protocol->encoder->deserialize) { + ret = + instance->protocol->encoder->deserialize(instance->protocol_instance, flipper_format); + } + return ret; +} + +LevelDuration subghz_transmitter_yield(void* context) { + SubGhzTransmitter* instance = context; + return instance->protocol->encoder->yield(instance->protocol_instance); +} diff --git a/applications/main/subghz/transmitter.h b/applications/main/subghz/transmitter.h new file mode 100644 index 000000000..cce98a463 --- /dev/null +++ b/applications/main/subghz/transmitter.h @@ -0,0 +1,55 @@ +#pragma once + +#include "types.h" +#include "environment.h" +#include "protocols/base.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct SubGhzTransmitter SubGhzTransmitter; + +/** + * Allocate and init SubGhzTransmitter. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzTransmitter* pointer to a SubGhzTransmitter instance + */ +SubGhzTransmitter* + subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name); + +/** + * Free SubGhzTransmitter. + * @param instance Pointer to a SubGhzTransmitter instance + */ +void subghz_transmitter_free(SubGhzTransmitter* instance); + +/** Get protocol instance. + * @param instance Pointer to a SubGhzTransmitter instance + */ +SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance); + +/** + * Forced transmission stop. + * @param instance Pointer to a SubGhzTransmitter instance + */ +bool subghz_transmitter_stop(SubGhzTransmitter* instance); + +/** + * Deserialize and generating an upload to send. + * @param instance Pointer to a SubGhzTransmitter instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzTransmitter instance + * @return LevelDuration + */ +LevelDuration subghz_transmitter_yield(void* context); + +#ifdef __cplusplus +} +#endif diff --git a/applications/main/subghz/types.h b/applications/main/subghz/types.h new file mode 100644 index 000000000..9d121dc3c --- /dev/null +++ b/applications/main/subghz/types.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include + +#include +#include + +#include "environment.h" +#include +#include + +#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") +#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") +#define SUBGHZ_APP_EXTENSION ".sub" + +#define SUBGHZ_KEY_FILE_VERSION 1 +#define SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" + +#define SUBGHZ_RAW_FILE_VERSION 1 +#define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" + +// Radio Preset +typedef struct { + FuriString* name; + uint32_t frequency; + uint8_t* data; + size_t data_size; +} SubGhzRadioPreset; + +// Allocator and Deallocator +typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); +typedef void (*SubGhzFree)(void* context); + +// Serialize and Deserialize +typedef bool ( + *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); +typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); + +// Decoder specific +typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); +typedef void (*SubGhzDecoderReset)(void* decoder); +typedef uint8_t (*SubGhzGetHashData)(void* decoder); +typedef void (*SubGhzGetString)(void* decoder, FuriString* output); + +// Encoder specific +typedef void (*SubGhzEncoderStop)(void* encoder); +typedef LevelDuration (*SubGhzEncoderYield)(void* context); + +typedef struct { + SubGhzAlloc alloc; + SubGhzFree free; + + SubGhzDecoderFeed feed; + SubGhzDecoderReset reset; + + SubGhzGetHashData get_hash_data; + SubGhzGetString get_string; + SubGhzSerialize serialize; + SubGhzDeserialize deserialize; +} SubGhzProtocolDecoder; + +typedef struct { + SubGhzAlloc alloc; + SubGhzFree free; + + SubGhzDeserialize deserialize; + SubGhzEncoderStop stop; + SubGhzEncoderYield yield; +} SubGhzProtocolEncoder; + +typedef enum { + SubGhzProtocolTypeUnknown = 0, + SubGhzProtocolTypeStatic, + SubGhzProtocolTypeDynamic, + SubGhzProtocolTypeRAW, + SubGhzProtocolWeatherStation, + SubGhzProtocolCustom, + SubGhzProtocolTypeBinRAW, +} SubGhzProtocolType; + +typedef enum { + SubGhzProtocolFlag_RAW = (1 << 0), + SubGhzProtocolFlag_Decodable = (1 << 1), + SubGhzProtocolFlag_315 = (1 << 2), + SubGhzProtocolFlag_433 = (1 << 3), + SubGhzProtocolFlag_868 = (1 << 4), + SubGhzProtocolFlag_AM = (1 << 5), + SubGhzProtocolFlag_FM = (1 << 6), + SubGhzProtocolFlag_Save = (1 << 7), + SubGhzProtocolFlag_Load = (1 << 8), + SubGhzProtocolFlag_Send = (1 << 9), + SubGhzProtocolFlag_BinRAW = (1 << 10), +} SubGhzProtocolFlag; + +typedef struct { + const char* name; + SubGhzProtocolType type; + SubGhzProtocolFlag flag; + + const SubGhzProtocolEncoder* encoder; + const SubGhzProtocolDecoder* decoder; +} SubGhzProtocol; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 0a219a48c..74bdbf5b0 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -14,6 +14,8 @@ #define MENU_ITEMS 4u #define UNLOCK_CNT 3 +#define SUBGHZ_RAW_TRESHOLD_MIN -90.0f + typedef struct { FuriString* item_str; uint8_t type; @@ -34,7 +36,7 @@ static const Icon* ReceiverItemIcons[] = { [SubGhzProtocolTypeUnknown] = &I_Quest_7x8, [SubGhzProtocolTypeStatic] = &I_Static_9x7, [SubGhzProtocolTypeDynamic] = &I_Dynamic_9x7, - [SubGhzProtocolTypeRAW] = &I_Raw_9x7, + [SubGhzProtocolTypeBinRAW] = &I_Raw_9x7, }; typedef enum { @@ -64,6 +66,7 @@ typedef struct { uint16_t history_item; SubGhzViewReceiverBarShow bar_show; SubGhzViewReceiverMode mode; + uint8_t u_rssi; } SubGhzViewReceiverModel; void subghz_view_receiver_set_mode( @@ -73,6 +76,21 @@ void subghz_view_receiver_set_mode( subghz_receiver->view, SubGhzViewReceiverModel * model, { model->mode = mode; }, true); } +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi) { + furi_assert(instance); + with_view_model( + instance->view, + SubGhzViewReceiverModel * model, + { + if(rssi < SUBGHZ_RAW_TRESHOLD_MIN) { + model->u_rssi = 0; + } else { + model->u_rssi = (uint8_t)(rssi - SUBGHZ_RAW_TRESHOLD_MIN); + } + }, + true); +} + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock lock) { furi_assert(subghz_receiver); subghz_receiver->lock_count = 0; @@ -191,6 +209,16 @@ static void subghz_view_receiver_draw_frame(Canvas* canvas, uint16_t idx, bool s canvas_draw_dot(canvas, scrollbar ? 121 : 126, (0 + idx * FRAME_HEIGHT) + 11); } +static void subghz_view_rssi_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { + for(uint8_t i = 1; i < model->u_rssi; i++) { + if(i % 5) { + canvas_draw_dot(canvas, 46 + i, 50); + canvas_draw_dot(canvas, 47 + i, 51); + canvas_draw_dot(canvas, 46 + i, 52); + } + } +} + void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); @@ -198,8 +226,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { if(model->mode == SubGhzViewReceiverModeLive) { elements_button_left(canvas, "Config"); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); } else { + canvas_draw_line(canvas, 2, 52, 125, 52); canvas_draw_str(canvas, 3, 62, furi_string_get_cstr(model->progress_str)); } @@ -235,7 +264,7 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); canvas_set_font(canvas, FontPrimary); canvas_draw_str(canvas, 63, 46, "Scanning..."); - canvas_draw_line(canvas, 46, 51, 125, 51); + //canvas_draw_line(canvas, 46, 51, 125, 51); canvas_set_font(canvas, FontSecondary); } else { canvas_draw_icon(canvas, 0, 0, XTREME_ASSETS()->I_Scanning_123x52); @@ -245,6 +274,9 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { } } + if(model->mode == SubGhzViewReceiverModeLive) { + subghz_view_rssi_draw(canvas, model); + } switch(model->bar_show) { case SubGhzViewReceiverBarShowLock: canvas_draw_icon(canvas, 64, 55, &I_Lock_7x8); @@ -252,7 +284,28 @@ void subghz_view_receiver_draw(Canvas* canvas, SubGhzViewReceiverModel* model) { break; case SubGhzViewReceiverBarShowToUnlockPress: canvas_draw_str(canvas, 44, 62, furi_string_get_cstr(model->frequency_str)); +#ifdef SUBGHZ_EXT_PRESET_NAME + if(model->history_item == 0 && model->mode == SubGhzViewReceiverModeLive) { + canvas_draw_str( + canvas, + 44 + canvas_string_width(canvas, furi_string_get_cstr(model->frequency_str)) + 1, + 62, + "MHz"); + const char* str = furi_string_get_cstr(model->preset_str); + const uint8_t vertical_offset = 7; + const uint8_t horizontal_offset = 3; + const uint8_t string_width = canvas_string_width(canvas, str); + canvas_draw_str( + canvas, + canvas_width(canvas) - (string_width + horizontal_offset), + vertical_offset, + str); + } else { + canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); + } +#else canvas_draw_str(canvas, 79, 62, furi_string_get_cstr(model->preset_str)); +#endif canvas_draw_str(canvas, 96, 62, furi_string_get_cstr(model->history_stat_str)); canvas_set_font(canvas, FontSecondary); elements_bold_rounded_frame(canvas, 14, 8, 99, 48); diff --git a/applications/main/subghz/views/receiver.h b/applications/main/subghz/views/receiver.h index 829277174..37eb473de 100644 --- a/applications/main/subghz/views/receiver.h +++ b/applications/main/subghz/views/receiver.h @@ -12,6 +12,8 @@ void subghz_view_receiver_set_mode( SubGhzViewReceiver* subghz_receiver, SubGhzViewReceiverMode mode); +void subghz_receiver_rssi(SubGhzViewReceiver* instance, float rssi); + void subghz_view_receiver_set_lock(SubGhzViewReceiver* subghz_receiver, SubGhzLock keyboard); void subghz_view_receiver_set_callback( diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index ff971fd3b..9fa304e90 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -23,10 +23,10 @@ static const uint32_t subghz_frequency_list[] = { 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, - 315000000, 318000000, 330000000, 345000000, 348000000, 387000000, 390000000, 418000000, - 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, 434176948, - 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, 868350000, - 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, + 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, + 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, + 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, @@ -165,6 +165,7 @@ void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel // Title canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); + canvas_draw_str(canvas, 0, 7, furi_hal_subghz_get_radio_type() ? "Ext" : "Int"); canvas_draw_str(canvas, 20, 7, "Frequency Analyzer"); // RSSI diff --git a/applications/main/subghz/views/subghz_read_raw.c b/applications/main/subghz/views/subghz_read_raw.c index dcfc281d2..7ba2f4434 100644 --- a/applications/main/subghz/views/subghz_read_raw.c +++ b/applications/main/subghz/views/subghz_read_raw.c @@ -30,6 +30,7 @@ typedef struct { SubGhzReadRAWStatus status; bool raw_send_only; float raw_threshold_rssi; + bool not_showing_samples; } SubGhzReadRAWModel; void subghz_read_raw_set_callback( @@ -92,7 +93,10 @@ void subghz_read_raw_update_sample_write(SubGhzReadRAW* instance, size_t sample) with_view_model( instance->view, SubGhzReadRAWModel * model, - { furi_string_printf(model->sample_write, "%zu spl.", sample); }, + { + model->not_showing_samples = false; + furi_string_printf(model->sample_write, "%zu spl.", sample); + }, false); } @@ -280,8 +284,15 @@ void subghz_read_raw_draw(Canvas* canvas, SubGhzReadRAWModel* model) { uint8_t graphics_mode = 1; canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - canvas_draw_str(canvas, 5, 7, furi_string_get_cstr(model->frequency_str)); - canvas_draw_str(canvas, 40, 7, furi_string_get_cstr(model->preset_str)); + canvas_draw_str(canvas, 0, 7, furi_string_get_cstr(model->frequency_str)); + canvas_draw_str(canvas, 35, 7, furi_string_get_cstr(model->preset_str)); + + if(model->not_showing_samples) { + canvas_draw_str(canvas, 77, 7, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); + } else { + canvas_draw_str(canvas, 70, 7, furi_hal_subghz_get_radio_type() ? "E" : "I"); + } + canvas_draw_str_aligned( canvas, 126, 0, AlignRight, AlignTop, furi_string_get_cstr(model->sample_write)); @@ -448,6 +459,7 @@ bool subghz_read_raw_input(InputEvent* event, void* context) { model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_set(model->sample_write, "0 spl."); furi_string_reset(model->file_name); instance->callback(SubGhzCustomEventViewReadRAWErase, instance->context); @@ -509,6 +521,7 @@ void subghz_read_raw_set_status( model->status = SubGhzReadRAWStatusStart; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_reset(model->file_name); furi_string_set(model->sample_write, "0 spl."); model->raw_threshold_rssi = raw_threshold_rssi; @@ -530,6 +543,7 @@ void subghz_read_raw_set_status( model->status = SubGhzReadRAWStatusLoadKeyIDLE; model->rssi_history_end = false; model->ind_write = 0; + model->not_showing_samples = true; furi_string_set(model->file_name, file_name); furi_string_set(model->sample_write, "RAW"); }, @@ -542,6 +556,7 @@ void subghz_read_raw_set_status( { model->status = SubGhzReadRAWStatusLoadKeyIDLE; if(!model->ind_write) { + model->not_showing_samples = true; furi_string_set(model->file_name, file_name); furi_string_set(model->sample_write, "RAW"); } else { diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/main/subghz/views/subghz_test_carrier.c index e533a6aac..0cc9a2966 100644 --- a/applications/main/subghz/views/subghz_test_carrier.c +++ b/applications/main/subghz/views/subghz_test_carrier.c @@ -115,14 +115,16 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { furi_hal_subghz_set_path(model->path); if(model->status == SubGhzTestCarrierModelStatusRx) { - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_rx(); } else { furi_hal_gpio_init( - &gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, true); + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); if(!furi_hal_subghz_tx()) { - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); subghz_test_carrier->callback( SubGhzTestCarrierEventOnlyRx, subghz_test_carrier->context); } @@ -140,7 +142,7 @@ void subghz_test_carrier_enter(void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); with_view_model( subghz_test_carrier->view, diff --git a/applications/main/subghz/views/subghz_test_static.c b/applications/main/subghz/views/subghz_test_static.c index 6abefda76..b9e5a8c9c 100644 --- a/applications/main/subghz/views/subghz_test_static.c +++ b/applications/main/subghz/views/subghz_test_static.c @@ -143,8 +143,9 @@ void subghz_test_static_enter(void* context) { furi_hal_subghz_reset(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_write(&gpio_cc1101_g0, false); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); instance->status_tx = SubGhzTestStaticStatusIDLE; with_view_model( diff --git a/applications/main/subghz/views/transmitter.c b/applications/main/subghz/views/transmitter.c index dc324a6a5..102639924 100644 --- a/applications/main/subghz/views/transmitter.c +++ b/applications/main/subghz/views/transmitter.c @@ -84,10 +84,15 @@ void subghz_view_transmitter_draw(Canvas* canvas, SubGhzViewTransmitterModel* mo canvas_clear(canvas); canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontSecondary); - elements_multiline_text(canvas, 0, 7, furi_string_get_cstr(model->key_str)); + elements_multiline_text_aligned( + canvas, 0, 0, AlignLeft, AlignTop, furi_string_get_cstr(model->key_str)); canvas_draw_str(canvas, 78, 7, furi_string_get_cstr(model->frequency_str)); canvas_draw_str(canvas, 113, 7, furi_string_get_cstr(model->preset_str)); - if(model->show_button) subghz_view_transmitter_button_right(canvas, "Send"); + + if(model->show_button) { + canvas_draw_str(canvas, 58, 62, furi_hal_subghz_get_radio_type() ? "R: Ext" : "R: Int"); + subghz_view_transmitter_button_right(canvas, "Send"); + } } bool subghz_view_transmitter_input(InputEvent* event, void* context) { diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 24c2d32db..cbe0ec2ed 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,15.0,, +Version,+,15.1,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -178,6 +178,7 @@ Header,+,lib/subghz/blocks/encoder.h,, Header,+,lib/subghz/blocks/generic.h,, Header,+,lib/subghz/blocks/math.h,, Header,+,lib/subghz/environment.h,, +Header,+,lib/subghz/protocols/protocol_items.h,, Header,+,lib/subghz/protocols/raw.h,, Header,+,lib/subghz/receiver.h,, Header,+,lib/subghz/registry.h,, @@ -534,6 +535,8 @@ Function,-,atoff,float,const char* Function,+,atoi,int,const char* Function,-,atol,long,const char* Function,-,atoll,long long,const char* +Function,-,atomo_decrypt,void,uint8_t* +Function,-,atomo_encrypt,void,uint8_t* Function,-,basename,char*,const char* Function,-,bcmp,int,"const void*, const void*, size_t" Function,-,bcopy,void,"const void*, void*, size_t" @@ -1276,6 +1279,7 @@ Function,+,furi_hal_region_is_frequency_allowed,_Bool,uint32_t Function,+,furi_hal_region_is_provisioned,_Bool, Function,+,furi_hal_region_set,void,FuriHalRegion* Function,-,furi_hal_resources_deinit_early,void, +Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* Function,-,furi_hal_resources_init,void, Function,-,furi_hal_resources_init_early,void, Function,+,furi_hal_rfid_change_read_config,void,"float, float" @@ -1354,13 +1358,18 @@ Function,-,furi_hal_spi_config_init,void, Function,-,furi_hal_spi_config_init_early,void, Function,+,furi_hal_spi_dma_init,void, Function,+,furi_hal_spi_release,void,FuriHalSpiBusHandle* +Function,+,furi_hal_subghz_check_radio,_Bool, +Function,+,furi_hal_subghz_disable_ext_power,void, Function,-,furi_hal_subghz_dump_state,void, +Function,+,furi_hal_subghz_enable_ext_power,void, Function,+,furi_hal_subghz_flush_rx,void, Function,+,furi_hal_subghz_flush_tx,void, Function,+,furi_hal_subghz_get_lqi,uint8_t, +Function,+,furi_hal_subghz_get_radio_type,SubGhzRadioType, Function,+,furi_hal_subghz_get_rssi,float, Function,+,furi_hal_subghz_idle,void, Function,-,furi_hal_subghz_init,void, +Function,+,furi_hal_subghz_init_check,_Bool, Function,+,furi_hal_subghz_is_async_tx_complete,_Bool, Function,+,furi_hal_subghz_is_frequency_valid,_Bool,uint32_t Function,+,furi_hal_subghz_is_rx_data_crc_valid,_Bool, @@ -1377,6 +1386,7 @@ Function,+,furi_hal_subghz_set_async_mirror_pin,void,const GpioPin* Function,+,furi_hal_subghz_set_frequency,uint32_t,uint32_t Function,+,furi_hal_subghz_set_frequency_and_path,uint32_t,uint32_t Function,+,furi_hal_subghz_set_path,void,FuriHalSubGhzPath +Function,+,furi_hal_subghz_set_radio_type,_Bool,SubGhzRadioType Function,-,furi_hal_subghz_shutdown,void, Function,+,furi_hal_subghz_sleep,void, Function,+,furi_hal_subghz_start_async_rx,void,"FuriHalSubGhzCaptureCallback, void*" @@ -1746,6 +1756,8 @@ Function,-,j1f,float,float Function,-,jn,double,"int, double" Function,-,jnf,float,"int, float" Function,-,jrand48,long,unsigned short[3] +Function,-,keeloq_reset_kl_type,void, +Function,-,keeloq_reset_mfname,void, Function,-,l64a,char*,long Function,-,labs,long,long Function,-,lcong48,void,unsigned short[7] @@ -2505,6 +2517,8 @@ Function,+,srand,void,unsigned Function,-,srand48,void,long Function,-,srandom,void,unsigned Function,+,sscanf,int,"const char*, const char*, ..." +Function,-,star_line_reset_kl_type,void, +Function,-,star_line_reset_mfname,void, Function,+,storage_common_copy,FS_Error,"Storage*, const char*, const char*" Function,+,storage_common_fs_info,FS_Error,"Storage*, const char*, uint64_t*, uint64_t*" Function,+,storage_common_merge,FS_Error,"Storage*, const char*, const char*" @@ -2686,28 +2700,541 @@ Function,+,subghz_protocol_blocks_parity_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_reverse_key,uint64_t,"uint64_t, uint8_t" Function,+,subghz_protocol_blocks_set_bit_array,void,"_Bool, uint8_t[], size_t, size_t" Function,+,subghz_protocol_blocks_xor_bytes,uint8_t,"const uint8_t[], size_t" -Function,+,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" +Function,-,subghz_protocol_decoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_alutech_at_4n_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_alutech_at_4n_free,void,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_alutech_at_4n_reset,void,void* +Function,-,subghz_protocol_decoder_alutech_at_4n_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ansonic_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_ansonic_free,void,void* +Function,-,subghz_protocol_decoder_ansonic_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_ansonic_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_ansonic_reset,void,void* +Function,-,subghz_protocol_decoder_ansonic_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_base_deserialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*" Function,+,subghz_protocol_decoder_base_get_hash_data,uint8_t,SubGhzProtocolDecoderBase* Function,+,subghz_protocol_decoder_base_get_string,_Bool,"SubGhzProtocolDecoderBase*, FuriString*" Function,+,subghz_protocol_decoder_base_serialize,_Bool,"SubGhzProtocolDecoderBase*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_decoder_base_set_decoder_callback,void,"SubGhzProtocolDecoderBase*, SubGhzProtocolDecoderBaseRxCallback, void*" +Function,-,subghz_protocol_decoder_bett_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bett_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_bett_free,void,void* +Function,-,subghz_protocol_decoder_bett_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_bett_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_bett_reset,void,void* +Function,-,subghz_protocol_decoder_bett_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_bin_raw_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_bin_raw_data_input_rssi,void,"SubGhzProtocolDecoderBinRAW*, float" +Function,-,subghz_protocol_decoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_bin_raw_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_bin_raw_free,void,void* +Function,-,subghz_protocol_decoder_bin_raw_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_bin_raw_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_bin_raw_reset,void,void* +Function,-,subghz_protocol_decoder_bin_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_atomo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_atomo_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_atomo_free,void,void* +Function,-,subghz_protocol_decoder_came_atomo_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_atomo_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_atomo_reset,void,void* +Function,-,subghz_protocol_decoder_came_atomo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_free,void,void* +Function,-,subghz_protocol_decoder_came_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_reset,void,void* +Function,-,subghz_protocol_decoder_came_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_came_twee_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_came_twee_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_came_twee_free,void,void* +Function,-,subghz_protocol_decoder_came_twee_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_came_twee_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_came_twee_reset,void,void* +Function,-,subghz_protocol_decoder_came_twee_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_chamb_code_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_chamb_code_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_chamb_code_free,void,void* +Function,-,subghz_protocol_decoder_chamb_code_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_chamb_code_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_chamb_code_reset,void,void* +Function,-,subghz_protocol_decoder_chamb_code_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_clemsa_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_clemsa_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_clemsa_free,void,void* +Function,-,subghz_protocol_decoder_clemsa_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_clemsa_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_clemsa_reset,void,void* +Function,-,subghz_protocol_decoder_clemsa_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_doitrand_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_doitrand_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_doitrand_free,void,void* +Function,-,subghz_protocol_decoder_doitrand_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_doitrand_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_doitrand_reset,void,void* +Function,-,subghz_protocol_decoder_doitrand_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_dooya_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_dooya_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_dooya_free,void,void* +Function,-,subghz_protocol_decoder_dooya_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_dooya_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_dooya_reset,void,void* +Function,-,subghz_protocol_decoder_dooya_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_faac_slh_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_faac_slh_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_faac_slh_free,void,void* +Function,-,subghz_protocol_decoder_faac_slh_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_faac_slh_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_faac_slh_reset,void,void* +Function,-,subghz_protocol_decoder_faac_slh_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_gate_tx_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_gate_tx_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_gate_tx_free,void,void* +Function,-,subghz_protocol_decoder_gate_tx_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_gate_tx_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_gate_tx_reset,void,void* +Function,-,subghz_protocol_decoder_gate_tx_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_holtek_free,void,void* +Function,-,subghz_protocol_decoder_holtek_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_holtek_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_holtek_reset,void,void* +Function,-,subghz_protocol_decoder_holtek_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_holtek_th12x_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_holtek_th12x_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_holtek_th12x_free,void,void* +Function,-,subghz_protocol_decoder_holtek_th12x_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_holtek_th12x_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_holtek_th12x_reset,void,void* +Function,-,subghz_protocol_decoder_holtek_th12x_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_honeywell_wdb_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_honeywell_wdb_free,void,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_honeywell_wdb_reset,void,void* +Function,-,subghz_protocol_decoder_honeywell_wdb_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_hormann_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_hormann_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_hormann_free,void,void* +Function,-,subghz_protocol_decoder_hormann_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_hormann_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_hormann_reset,void,void* +Function,-,subghz_protocol_decoder_hormann_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_ido_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_ido_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_ido_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_ido_free,void,void* +Function,-,subghz_protocol_decoder_ido_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_ido_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_ido_reset,void,void* +Function,-,subghz_protocol_decoder_ido_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_intertechno_v3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_intertechno_v3_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_intertechno_v3_free,void,void* +Function,-,subghz_protocol_decoder_intertechno_v3_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_intertechno_v3_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_intertechno_v3_reset,void,void* +Function,-,subghz_protocol_decoder_intertechno_v3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_keeloq_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_keeloq_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_keeloq_free,void,void* +Function,-,subghz_protocol_decoder_keeloq_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_keeloq_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_keeloq_reset,void,void* +Function,-,subghz_protocol_decoder_keeloq_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kia_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_kia_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kia_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_kia_free,void,void* +Function,-,subghz_protocol_decoder_kia_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_kia_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_kia_reset,void,void* +Function,-,subghz_protocol_decoder_kia_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_free,void,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_reset,void,void* +Function,-,subghz_protocol_decoder_kinggates_stylo_4k_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_delta3_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_linear_delta3_free,void,void* +Function,-,subghz_protocol_decoder_linear_delta3_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_linear_delta3_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_linear_delta3_reset,void,void* +Function,-,subghz_protocol_decoder_linear_delta3_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_linear_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_linear_free,void,void* +Function,-,subghz_protocol_decoder_linear_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_linear_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_linear_reset,void,void* +Function,-,subghz_protocol_decoder_linear_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_magellan_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_magellan_free,void,void* +Function,-,subghz_protocol_decoder_magellan_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_magellan_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_magellan_reset,void,void* +Function,-,subghz_protocol_decoder_magellan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_marantec_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_marantec_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_marantec_free,void,void* +Function,-,subghz_protocol_decoder_marantec_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_marantec_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_marantec_reset,void,void* +Function,-,subghz_protocol_decoder_marantec_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_megacode_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_megacode_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_megacode_free,void,void* +Function,-,subghz_protocol_decoder_megacode_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_megacode_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_megacode_reset,void,void* +Function,-,subghz_protocol_decoder_megacode_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_radio_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_radio_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nero_radio_free,void,void* +Function,-,subghz_protocol_decoder_nero_radio_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nero_radio_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nero_radio_reset,void,void* +Function,-,subghz_protocol_decoder_nero_radio_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nero_sketch_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nero_sketch_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nero_sketch_free,void,void* +Function,-,subghz_protocol_decoder_nero_sketch_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nero_sketch_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nero_sketch_reset,void,void* +Function,-,subghz_protocol_decoder_nero_sketch_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flo_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nice_flo_free,void,void* +Function,-,subghz_protocol_decoder_nice_flo_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nice_flo_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nice_flo_reset,void,void* +Function,-,subghz_protocol_decoder_nice_flo_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_nice_flor_s_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_nice_flor_s_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_nice_flor_s_free,void,void* +Function,-,subghz_protocol_decoder_nice_flor_s_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_nice_flor_s_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_nice_flor_s_reset,void,void* +Function,-,subghz_protocol_decoder_nice_flor_s_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_phoenix_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_phoenix_v2_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_phoenix_v2_free,void,void* +Function,-,subghz_protocol_decoder_phoenix_v2_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_phoenix_v2_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_phoenix_v2_reset,void,void* +Function,-,subghz_protocol_decoder_phoenix_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_power_smart_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_power_smart_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_power_smart_free,void,void* +Function,-,subghz_protocol_decoder_power_smart_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_power_smart_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_power_smart_reset,void,void* +Function,-,subghz_protocol_decoder_power_smart_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,+,subghz_protocol_decoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_decoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_decoder_princeton_feed,void,"void*, _Bool, uint32_t" +Function,+,subghz_protocol_decoder_princeton_free,void,void* +Function,+,subghz_protocol_decoder_princeton_get_hash_data,uint8_t,void* +Function,+,subghz_protocol_decoder_princeton_get_string,void,"void*, FuriString*" +Function,+,subghz_protocol_decoder_princeton_reset,void,void* +Function,+,subghz_protocol_decoder_princeton_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,+,subghz_protocol_decoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_decoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_decoder_raw_feed,void,"void*, _Bool, uint32_t" Function,+,subghz_protocol_decoder_raw_free,void,void* -Function,+,subghz_protocol_decoder_raw_get_hash_data,uint8_t,void* Function,+,subghz_protocol_decoder_raw_get_string,void,"void*, FuriString*" Function,+,subghz_protocol_decoder_raw_reset,void,void* -Function,+,subghz_protocol_decoder_raw_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" -Function,+,subghz_protocol_decoder_raw_set_auto_mode,void,"void*, _Bool" -Function,+,subghz_protocol_decoder_raw_set_rssi_threshold,void,"void*, int" -Function,+,subghz_protocol_decoder_raw_write_data,_Bool,"void*, _Bool, uint32_t" -Function,-,subghz_protocol_encoder_get_rssi_threshold,int,void* +Function,-,subghz_protocol_decoder_scher_khan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_scher_khan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_scher_khan_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_scher_khan_free,void,void* +Function,-,subghz_protocol_decoder_scher_khan_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_scher_khan_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_scher_khan_reset,void,void* +Function,-,subghz_protocol_decoder_scher_khan_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v1_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v1_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_secplus_v1_free,void,void* +Function,-,subghz_protocol_decoder_secplus_v1_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_secplus_v1_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_secplus_v1_reset,void,void* +Function,-,subghz_protocol_decoder_secplus_v1_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_secplus_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_secplus_v2_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_secplus_v2_free,void,void* +Function,-,subghz_protocol_decoder_secplus_v2_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_secplus_v2_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_secplus_v2_reset,void,void* +Function,-,subghz_protocol_decoder_secplus_v2_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_smc5326_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_smc5326_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_smc5326_free,void,void* +Function,-,subghz_protocol_decoder_smc5326_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_smc5326_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_smc5326_reset,void,void* +Function,-,subghz_protocol_decoder_smc5326_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_keytis_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_somfy_keytis_free,void,void* +Function,-,subghz_protocol_decoder_somfy_keytis_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_somfy_keytis_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_somfy_keytis_reset,void,void* +Function,-,subghz_protocol_decoder_somfy_keytis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_somfy_telis_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_somfy_telis_free,void,void* +Function,-,subghz_protocol_decoder_somfy_telis_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_somfy_telis_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_somfy_telis_reset,void,void* +Function,-,subghz_protocol_decoder_somfy_telis_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_decoder_star_line_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_decoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_decoder_star_line_feed,void,"void*, _Bool, uint32_t" +Function,-,subghz_protocol_decoder_star_line_free,void,void* +Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* +Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" +Function,-,subghz_protocol_decoder_star_line_reset,void,void* +Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" +Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_ansonic_free,void,void* +Function,-,subghz_protocol_encoder_ansonic_stop,void,void* +Function,-,subghz_protocol_encoder_ansonic_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_bett_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_bett_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bett_free,void,void* +Function,-,subghz_protocol_encoder_bett_stop,void,void* +Function,-,subghz_protocol_encoder_bett_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_bin_raw_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_bin_raw_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_bin_raw_free,void,void* +Function,-,subghz_protocol_encoder_bin_raw_stop,void,void* +Function,-,subghz_protocol_encoder_bin_raw_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_atomo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_atomo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_atomo_free,void,void* +Function,-,subghz_protocol_encoder_came_atomo_stop,void,void* +Function,-,subghz_protocol_encoder_came_atomo_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_free,void,void* +Function,-,subghz_protocol_encoder_came_stop,void,void* +Function,-,subghz_protocol_encoder_came_twee_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_came_twee_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_came_twee_free,void,void* +Function,-,subghz_protocol_encoder_came_twee_stop,void,void* +Function,-,subghz_protocol_encoder_came_twee_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_came_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_chamb_code_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_chamb_code_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_chamb_code_free,void,void* +Function,-,subghz_protocol_encoder_chamb_code_stop,void,void* +Function,-,subghz_protocol_encoder_chamb_code_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_clemsa_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_clemsa_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_clemsa_free,void,void* +Function,-,subghz_protocol_encoder_clemsa_stop,void,void* +Function,-,subghz_protocol_encoder_clemsa_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_doitrand_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_doitrand_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_doitrand_free,void,void* +Function,-,subghz_protocol_encoder_doitrand_stop,void,void* +Function,-,subghz_protocol_encoder_doitrand_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_dooya_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_dooya_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_dooya_free,void,void* +Function,-,subghz_protocol_encoder_dooya_stop,void,void* +Function,-,subghz_protocol_encoder_dooya_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_faac_slh_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_faac_slh_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_faac_slh_free,void,void* +Function,-,subghz_protocol_encoder_faac_slh_stop,void,void* +Function,-,subghz_protocol_encoder_faac_slh_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_gate_tx_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_gate_tx_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_gate_tx_free,void,void* +Function,-,subghz_protocol_encoder_gate_tx_stop,void,void* +Function,-,subghz_protocol_encoder_gate_tx_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_holtek_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_holtek_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_free,void,void* +Function,-,subghz_protocol_encoder_holtek_stop,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_holtek_th12x_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_holtek_th12x_free,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_stop,void,void* +Function,-,subghz_protocol_encoder_holtek_th12x_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_holtek_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_honeywell_wdb_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_honeywell_wdb_free,void,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_stop,void,void* +Function,-,subghz_protocol_encoder_honeywell_wdb_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_hormann_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_hormann_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_hormann_free,void,void* +Function,-,subghz_protocol_encoder_hormann_stop,void,void* +Function,-,subghz_protocol_encoder_hormann_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_intertechno_v3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_intertechno_v3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_intertechno_v3_free,void,void* +Function,-,subghz_protocol_encoder_intertechno_v3_stop,void,void* +Function,-,subghz_protocol_encoder_intertechno_v3_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_keeloq_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_keeloq_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_keeloq_free,void,void* +Function,-,subghz_protocol_encoder_keeloq_stop,void,void* +Function,-,subghz_protocol_encoder_keeloq_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_free,void,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_stop,void,void* +Function,-,subghz_protocol_encoder_kinggates_stylo_4k_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_linear_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_linear_delta3_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_linear_delta3_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_delta3_free,void,void* +Function,-,subghz_protocol_encoder_linear_delta3_stop,void,void* +Function,-,subghz_protocol_encoder_linear_delta3_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_linear_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_linear_free,void,void* +Function,-,subghz_protocol_encoder_linear_stop,void,void* +Function,-,subghz_protocol_encoder_linear_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_magellan_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_magellan_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_magellan_free,void,void* +Function,-,subghz_protocol_encoder_magellan_stop,void,void* +Function,-,subghz_protocol_encoder_magellan_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_marantec_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_marantec_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_marantec_free,void,void* +Function,-,subghz_protocol_encoder_marantec_stop,void,void* +Function,-,subghz_protocol_encoder_marantec_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_megacode_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_megacode_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_megacode_free,void,void* +Function,-,subghz_protocol_encoder_megacode_stop,void,void* +Function,-,subghz_protocol_encoder_megacode_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nero_radio_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nero_radio_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_radio_free,void,void* +Function,-,subghz_protocol_encoder_nero_radio_stop,void,void* +Function,-,subghz_protocol_encoder_nero_radio_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nero_sketch_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nero_sketch_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nero_sketch_free,void,void* +Function,-,subghz_protocol_encoder_nero_sketch_stop,void,void* +Function,-,subghz_protocol_encoder_nero_sketch_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nice_flo_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nice_flo_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flo_free,void,void* +Function,-,subghz_protocol_encoder_nice_flo_stop,void,void* +Function,-,subghz_protocol_encoder_nice_flo_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_nice_flor_s_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_nice_flor_s_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_nice_flor_s_free,void,void* +Function,-,subghz_protocol_encoder_nice_flor_s_stop,void,void* +Function,-,subghz_protocol_encoder_nice_flor_s_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_phoenix_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_phoenix_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_phoenix_v2_free,void,void* +Function,-,subghz_protocol_encoder_phoenix_v2_stop,void,void* +Function,-,subghz_protocol_encoder_phoenix_v2_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_power_smart_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_power_smart_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_power_smart_free,void,void* +Function,-,subghz_protocol_encoder_power_smart_stop,void,void* +Function,-,subghz_protocol_encoder_power_smart_yield,LevelDuration,void* +Function,+,subghz_protocol_encoder_princeton_alloc,void*,SubGhzEnvironment* +Function,+,subghz_protocol_encoder_princeton_deserialize,_Bool,"void*, FlipperFormat*" +Function,+,subghz_protocol_encoder_princeton_free,void,void* +Function,+,subghz_protocol_encoder_princeton_stop,void,void* +Function,+,subghz_protocol_encoder_princeton_yield,LevelDuration,void* Function,+,subghz_protocol_encoder_raw_alloc,void*,SubGhzEnvironment* Function,+,subghz_protocol_encoder_raw_deserialize,_Bool,"void*, FlipperFormat*" Function,+,subghz_protocol_encoder_raw_free,void,void* Function,+,subghz_protocol_encoder_raw_stop,void,void* Function,+,subghz_protocol_encoder_raw_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_secplus_v1_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_secplus_v1_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v1_free,void,void* +Function,-,subghz_protocol_encoder_secplus_v1_stop,void,void* +Function,-,subghz_protocol_encoder_secplus_v1_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_secplus_v2_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_secplus_v2_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_secplus_v2_free,void,void* +Function,-,subghz_protocol_encoder_secplus_v2_stop,void,void* +Function,-,subghz_protocol_encoder_secplus_v2_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_smc5326_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_smc5326_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_smc5326_free,void,void* +Function,-,subghz_protocol_encoder_smc5326_stop,void,void* +Function,-,subghz_protocol_encoder_smc5326_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_somfy_keytis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_somfy_keytis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_keytis_free,void,void* +Function,-,subghz_protocol_encoder_somfy_keytis_stop,void,void* +Function,-,subghz_protocol_encoder_somfy_keytis_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_somfy_telis_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_somfy_telis_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_somfy_telis_free,void,void* +Function,-,subghz_protocol_encoder_somfy_telis_stop,void,void* +Function,-,subghz_protocol_encoder_somfy_telis_yield,LevelDuration,void* +Function,-,subghz_protocol_encoder_star_line_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_star_line_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_star_line_free,void,void* +Function,-,subghz_protocol_encoder_star_line_stop,void,void* +Function,-,subghz_protocol_encoder_star_line_yield,LevelDuration,void* +Function,-,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" +Function,-,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_nice_flor_s_encrypt,uint64_t,"uint64_t, const char*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* @@ -2717,10 +3244,14 @@ Function,+,subghz_protocol_raw_save_to_file_stop,void,SubGhzProtocolDecoderRAW* Function,+,subghz_protocol_registry_count,size_t,const SubGhzProtocolRegistry* Function,+,subghz_protocol_registry_get_by_index,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, size_t" Function,+,subghz_protocol_registry_get_by_name,const SubGhzProtocol*,"const SubGhzProtocolRegistry*, const char*" +Function,-,subghz_protocol_secplus_v1_check_fixed,_Bool,uint32_t +Function,-,subghz_protocol_secplus_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint32_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_somfy_keytis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_somfy_telis_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" +Function,-,subghz_protocol_star_line_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_receiver_alloc_init,SubGhzReceiver*,SubGhzEnvironment* Function,+,subghz_receiver_decode,void,"SubGhzReceiver*, _Bool, uint32_t" Function,+,subghz_receiver_free,void,SubGhzReceiver* -Function,+,subghz_receiver_get_filter,SubGhzProtocolFlag,SubGhzReceiver* Function,+,subghz_receiver_reset,void,SubGhzReceiver* Function,+,subghz_receiver_search_decoder_base_by_name,SubGhzProtocolDecoderBase*,"SubGhzReceiver*, const char*" Function,+,subghz_receiver_set_filter,void,"SubGhzReceiver*, SubGhzProtocolFlag" @@ -4178,12 +4709,15 @@ Variable,+,furi_hal_spi_bus_handle_nfc,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_fast,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_sd_slow,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_handle_subghz,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz_ext,FuriHalSpiBusHandle, +Variable,+,furi_hal_spi_bus_handle_subghz_int,FuriHalSpiBusHandle, Variable,+,furi_hal_spi_bus_r,FuriHalSpiBus, Variable,+,furi_hal_spi_preset_1edge_low_16m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_2m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_4m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_1edge_low_8m,const LL_SPI_InitTypeDef, Variable,+,furi_hal_spi_preset_2edge_low_8m,const LL_SPI_InitTypeDef, +Variable,+,furi_hal_subghz,volatile FuriHalSubGhz, Variable,+,gpio_button_back,const GpioPin, Variable,+,gpio_button_down,const GpioPin, Variable,+,gpio_button_left,const GpioPin, @@ -4191,6 +4725,7 @@ Variable,+,gpio_button_ok,const GpioPin, Variable,+,gpio_button_right,const GpioPin, Variable,+,gpio_button_up,const GpioPin, Variable,+,gpio_cc1101_g0,const GpioPin, +Variable,+,gpio_cc1101_g0_ext,const GpioPin, Variable,+,gpio_display_cs,const GpioPin, Variable,+,gpio_display_di,const GpioPin, Variable,+,gpio_display_rst_n,const GpioPin, @@ -4221,9 +4756,13 @@ Variable,+,gpio_spi_d_miso,const GpioPin, Variable,+,gpio_spi_d_mosi,const GpioPin, Variable,+,gpio_spi_d_sck,const GpioPin, Variable,+,gpio_spi_r_miso,const GpioPin, +Variable,+,gpio_spi_r_miso_ext,const GpioPin, Variable,+,gpio_spi_r_mosi,const GpioPin, +Variable,+,gpio_spi_r_mosi_ext,const GpioPin, Variable,+,gpio_spi_r_sck,const GpioPin, +Variable,+,gpio_spi_r_sck_ext,const GpioPin, Variable,+,gpio_subghz_cs,const GpioPin, +Variable,+,gpio_subghz_cs_ext,const GpioPin, Variable,+,gpio_usart_rx,const GpioPin, Variable,+,gpio_usart_tx,const GpioPin, Variable,+,gpio_usb_dm,const GpioPin, @@ -4426,9 +4965,133 @@ Variable,+,sequence_set_vibro_on,const NotificationSequence, Variable,+,sequence_single_vibro,const NotificationSequence, Variable,+,sequence_solid_yellow,const NotificationSequence, Variable,+,sequence_success,const NotificationSequence, +Variable,-,subghz_protocol_alutech_at_4n,const SubGhzProtocol, +Variable,-,subghz_protocol_alutech_at_4n_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_alutech_at_4n_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_ansonic,const SubGhzProtocol, +Variable,-,subghz_protocol_ansonic_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_ansonic_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_bett,const SubGhzProtocol, +Variable,-,subghz_protocol_bett_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_bett_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_bin_raw,const SubGhzProtocol, +Variable,-,subghz_protocol_bin_raw_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_bin_raw_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came,const SubGhzProtocol, +Variable,-,subghz_protocol_came_atomo,const SubGhzProtocol, +Variable,-,subghz_protocol_came_atomo_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_atomo_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_came_twee,const SubGhzProtocol, +Variable,-,subghz_protocol_came_twee_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_came_twee_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_chamb_code,const SubGhzProtocol, +Variable,-,subghz_protocol_chamb_code_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_chamb_code_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_clemsa,const SubGhzProtocol, +Variable,-,subghz_protocol_clemsa_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_clemsa_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_doitrand,const SubGhzProtocol, +Variable,-,subghz_protocol_doitrand_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_doitrand_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_dooya,const SubGhzProtocol, +Variable,-,subghz_protocol_dooya_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_dooya_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_faac_slh,const SubGhzProtocol, +Variable,-,subghz_protocol_faac_slh_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_faac_slh_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_gate_tx,const SubGhzProtocol, +Variable,-,subghz_protocol_gate_tx_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_gate_tx_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_holtek,const SubGhzProtocol, +Variable,-,subghz_protocol_holtek_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_holtek_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_holtek_th12x,const SubGhzProtocol, +Variable,-,subghz_protocol_holtek_th12x_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_holtek_th12x_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_honeywell_wdb,const SubGhzProtocol, +Variable,-,subghz_protocol_honeywell_wdb_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_honeywell_wdb_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_hormann,const SubGhzProtocol, +Variable,-,subghz_protocol_hormann_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_hormann_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_ido,const SubGhzProtocol, +Variable,-,subghz_protocol_ido_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_ido_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_intertechno_v3,const SubGhzProtocol, +Variable,-,subghz_protocol_intertechno_v3_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_intertechno_v3_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_keeloq,const SubGhzProtocol, +Variable,-,subghz_protocol_keeloq_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_keeloq_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_kia,const SubGhzProtocol, +Variable,-,subghz_protocol_kia_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_kia_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_kinggates_stylo_4k,const SubGhzProtocol, +Variable,-,subghz_protocol_kinggates_stylo_4k_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_kinggates_stylo_4k_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_linear,const SubGhzProtocol, +Variable,-,subghz_protocol_linear_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_linear_delta3,const SubGhzProtocol, +Variable,-,subghz_protocol_linear_delta3_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_linear_delta3_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_linear_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_magellan,const SubGhzProtocol, +Variable,-,subghz_protocol_magellan_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_magellan_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_marantec,const SubGhzProtocol, +Variable,-,subghz_protocol_marantec_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_marantec_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_megacode,const SubGhzProtocol, +Variable,-,subghz_protocol_megacode_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_megacode_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nero_radio,const SubGhzProtocol, +Variable,-,subghz_protocol_nero_radio_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nero_radio_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nero_sketch,const SubGhzProtocol, +Variable,-,subghz_protocol_nero_sketch_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nero_sketch_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nice_flo,const SubGhzProtocol, +Variable,-,subghz_protocol_nice_flo_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nice_flo_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_nice_flor_s,const SubGhzProtocol, +Variable,-,subghz_protocol_nice_flor_s_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_nice_flor_s_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_phoenix_v2,const SubGhzProtocol, +Variable,-,subghz_protocol_phoenix_v2_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_phoenix_v2_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_power_smart,const SubGhzProtocol, +Variable,-,subghz_protocol_power_smart_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_power_smart_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_princeton,const SubGhzProtocol, +Variable,-,subghz_protocol_princeton_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_registry,const SubGhzProtocolRegistry, +Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, +Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_secplus_v1,const SubGhzProtocol, +Variable,-,subghz_protocol_secplus_v1_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_secplus_v1_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_secplus_v2,const SubGhzProtocol, +Variable,-,subghz_protocol_secplus_v2_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_secplus_v2_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_smc5326,const SubGhzProtocol, +Variable,-,subghz_protocol_smc5326_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_smc5326_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_somfy_keytis,const SubGhzProtocol, +Variable,-,subghz_protocol_somfy_keytis_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_somfy_keytis_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_somfy_telis,const SubGhzProtocol, +Variable,-,subghz_protocol_somfy_telis_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_somfy_telis_encoder,const SubGhzProtocolEncoder, +Variable,-,subghz_protocol_star_line,const SubGhzProtocol, +Variable,-,subghz_protocol_star_line_decoder,const SubGhzProtocolDecoder, +Variable,-,subghz_protocol_star_line_encoder,const SubGhzProtocolEncoder, Variable,-,suboptarg,char*, Variable,-,u8g2_cb_mirror,const u8g2_cb_t, Variable,-,u8g2_cb_r0,const u8g2_cb_t, diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.c b/firmware/targets/f7/furi_hal/furi_hal_resources.c index 03cc6e714..4e63263cf 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.c +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.c @@ -8,9 +8,11 @@ const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; +const GpioPin gpio_cc1101_g0_ext = {.port = GPIOB, .pin = LL_GPIO_PIN_2}; const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_Pin}; const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; +const GpioPin gpio_subghz_cs_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_4}; const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; const GpioPin gpio_display_rst_n = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; @@ -31,6 +33,9 @@ const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pi const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; +const GpioPin gpio_spi_r_miso_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_6}; +const GpioPin gpio_spi_r_mosi_ext = {.port = GPIOA, .pin = LL_GPIO_PIN_7}; +const GpioPin gpio_spi_r_sck_ext = {.port = GPIOB, .pin = LL_GPIO_PIN_3}; const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = LL_GPIO_PIN_0}; const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = LL_GPIO_PIN_1}; @@ -191,3 +196,26 @@ void furi_hal_resources_init() { NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); NVIC_EnableIRQ(EXTI15_10_IRQn); } + +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { + if(gpio == &gpio_ext_pa7) + return 2; + else if(gpio == &gpio_ext_pa6) + return 3; + else if(gpio == &gpio_ext_pa4) + return 4; + else if(gpio == &gpio_ext_pb3) + return 5; + else if(gpio == &gpio_ext_pb2) + return 6; + else if(gpio == &gpio_ext_pc3) + return 7; + else if(gpio == &gpio_ext_pc1) + return 15; + else if(gpio == &gpio_ext_pc0) + return 16; + else if(gpio == &ibutton_gpio) + return 17; + else + return -1; +} diff --git a/firmware/targets/f7/furi_hal/furi_hal_resources.h b/firmware/targets/f7/furi_hal/furi_hal_resources.h index 51ea7bcc1..33e2a3289 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_resources.h +++ b/firmware/targets/f7/furi_hal/furi_hal_resources.h @@ -54,9 +54,11 @@ extern const GpioPin vibro_gpio; extern const GpioPin ibutton_gpio; extern const GpioPin gpio_cc1101_g0; +extern const GpioPin gpio_cc1101_g0_ext; extern const GpioPin gpio_rf_sw_0; extern const GpioPin gpio_subghz_cs; +extern const GpioPin gpio_subghz_cs_ext; extern const GpioPin gpio_display_cs; extern const GpioPin gpio_display_rst_n; extern const GpioPin gpio_display_di; @@ -77,6 +79,9 @@ extern const GpioPin gpio_spi_d_sck; extern const GpioPin gpio_spi_r_miso; extern const GpioPin gpio_spi_r_mosi; extern const GpioPin gpio_spi_r_sck; +extern const GpioPin gpio_spi_r_miso_ext; +extern const GpioPin gpio_spi_r_mosi_ext; +extern const GpioPin gpio_spi_r_sck_ext; extern const GpioPin gpio_ext_pc0; extern const GpioPin gpio_ext_pc1; @@ -216,6 +221,13 @@ void furi_hal_resources_deinit_early(); void furi_hal_resources_init(); +/** + * Get a corresponding external connector pin number for a gpio + * @param gpio GpioPin + * @return pin number or -1 if gpio is not on the external connector + */ +int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c index 9cf332dac..695418af1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.c +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.c @@ -2,6 +2,7 @@ #include #include #include +#include #define TAG "FuriHalSpiConfig" @@ -89,7 +90,7 @@ void furi_hal_spi_config_deinit_early() { void furi_hal_spi_config_init() { furi_hal_spi_bus_init(&furi_hal_spi_bus_r); - furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_bus_handle_init(furi_hal_subghz.spi_bus_handle); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_nfc); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_fast); furi_hal_spi_bus_handle_init(&furi_hal_spi_bus_handle_sd_slow); @@ -285,6 +286,15 @@ static void furi_hal_spi_bus_handle_subghz_event_callback( furi_hal_spi_bus_r_handle_event_callback(handle, event, &furi_hal_spi_preset_1edge_low_8m); } +FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_int = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_subghz_event_callback, + .miso = &gpio_spi_r_miso, + .mosi = &gpio_spi_r_mosi, + .sck = &gpio_spi_r_sck, + .cs = &gpio_subghz_cs, +}; + FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { .bus = &furi_hal_spi_bus_r, .callback = furi_hal_spi_bus_handle_subghz_event_callback, @@ -294,6 +304,15 @@ FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz = { .cs = &gpio_subghz_cs, }; +FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_ext = { + .bus = &furi_hal_spi_bus_r, + .callback = furi_hal_spi_bus_handle_subghz_event_callback, + .miso = &gpio_ext_pa6, + .mosi = &gpio_ext_pa7, + .sck = &gpio_ext_pb3, + .cs = &gpio_ext_pa4, +}; + static void furi_hal_spi_bus_handle_nfc_event_callback( FuriHalSpiBusHandle* handle, FuriHalSpiBusHandleEvent event) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h index eab633a19..8ea138bdc 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_spi_config.h +++ b/firmware/targets/f7/furi_hal/furi_hal_spi_config.h @@ -27,8 +27,12 @@ extern FuriHalSpiBus furi_hal_spi_bus_r; /** Furi Hal Spi Bus D (Display, SdCard) */ extern FuriHalSpiBus furi_hal_spi_bus_d; -/** CC1101 on `furi_hal_spi_bus_r` */ +/** CC1101 on current SPI bus */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz; +/** CC1101 on `furi_hal_spi_bus_r` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_int; +/** CC1101 on external `furi_hal_spi_bus_r` */ +extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_subghz_ext; /** ST25R3916 on `furi_hal_spi_bus_r` */ extern FuriHalSpiBusHandle furi_hal_spi_bus_handle_nfc; diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index ccea7728d..c2c238a13 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,111 +1,204 @@ #include #include -#include #include #include #include #include #include - -#include +#include #include +#include + #include #include #include #define TAG "FuriHalSubGhz" +//Initialisation timeout (ms) +#define INIT_TIMEOUT 10 static uint32_t furi_hal_subghz_debug_gpio_buff[2]; +static bool last_OTG_state = false; -typedef struct { - volatile SubGhzState state; - volatile SubGhzRegulation regulation; - volatile FuriHalSubGhzPreset preset; - const GpioPin* async_mirror_pin; -} FuriHalSubGhz; +/* DMA Channels definition */ +#define SUBGHZ_DMA DMA2 +#define SUBGHZ_DMA_CH1_CHANNEL LL_DMA_CHANNEL_1 +#define SUBGHZ_DMA_CH2_CHANNEL LL_DMA_CHANNEL_2 +#define SUBGHZ_DMA_CH1_IRQ FuriHalInterruptIdDma2Ch1 +#define SUBGHZ_DMA_CH1_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH1_CHANNEL +#define SUBGHZ_DMA_CH2_DEF SUBGHZ_DMA, SUBGHZ_DMA_CH2_CHANNEL volatile FuriHalSubGhz furi_hal_subghz = { .state = SubGhzStateInit, .regulation = SubGhzRegulationTxRx, .preset = FuriHalSubGhzPresetIDLE, .async_mirror_pin = NULL, + .radio_type = SubGhzRadioInternal, + .spi_bus_handle = &furi_hal_spi_bus_handle_subghz, + .cc1101_g0_pin = &gpio_cc1101_g0, }; +bool furi_hal_subghz_set_radio_type(SubGhzRadioType state) { + furi_hal_subghz.radio_type = state; + furi_hal_spi_bus_handle_deinit(furi_hal_subghz.spi_bus_handle); + if(state) { + furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz_ext; + furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0_ext; + } else { + furi_hal_subghz.spi_bus_handle = &furi_hal_spi_bus_handle_subghz; + furi_hal_subghz.cc1101_g0_pin = &gpio_cc1101_g0; + } + furi_hal_spi_bus_handle_init(furi_hal_subghz.spi_bus_handle); + furi_hal_subghz_init_check(); + return true; +} + +SubGhzRadioType furi_hal_subghz_get_radio_type(void) { + return furi_hal_subghz.radio_type; +} + void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin) { furi_hal_subghz.async_mirror_pin = pin; } -void furi_hal_subghz_init() { +void furi_hal_subghz_init(void) { + furi_hal_subghz_init_check(); +} + +void furi_hal_subghz_enable_ext_power(void) { + if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !furi_hal_power_is_otg_enabled()) { + furi_hal_power_enable_otg(); + } +} + +void furi_hal_subghz_disable_ext_power(void) { + if(furi_hal_subghz.radio_type != SubGhzRadioInternal && !last_OTG_state) { + furi_hal_power_disable_otg(); + } +} + +bool furi_hal_subghz_check_radio(void) { + bool result = true; + + furi_hal_subghz_enable_ext_power(); + + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + uint8_t ver = cc1101_get_version(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + if((ver != 0) && (ver != 255)) { + FURI_LOG_D(TAG, "Radio check ok"); + } else { + FURI_LOG_D(TAG, "Radio check failed"); + furi_hal_subghz_disable_ext_power(); + result = false; + } + return result; +} + +bool furi_hal_subghz_init_check(void) { + bool result = true; + furi_assert(furi_hal_subghz.state == SubGhzStateInit); furi_hal_subghz.state = SubGhzStateIdle; furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + last_OTG_state = furi_hal_power_is_otg_enabled(); + furi_hal_subghz_enable_ext_power(); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); #ifdef FURI_HAL_SUBGHZ_TX_GPIO furi_hal_gpio_init(&FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); #endif // Reset - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_reset(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); // Prepare GD0 for power on self test - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + // GD0 low + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + uint32_t test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != false && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; + } + } - // GD0 low - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) - ; - - // GD0 high - cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); - while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) - ; + // GD0 high + cc1101_write_reg( + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + test_start_time = furi_get_tick(); + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin) != true && result) { + if(furi_get_tick() - test_start_time > INIT_TIMEOUT) { + result = false; + } + } + } else { + // GD0 low + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != false) + ; + // GD0 high + cc1101_write_reg( + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV); + while(furi_hal_gpio_read(&gpio_cc1101_g0) != true) + ; + } // Reset GD0 to floating state - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // RF switches furi_hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); // Go to sleep - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); - FURI_LOG_I(TAG, "Init OK"); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + if(result) { + FURI_LOG_I(TAG, "Init OK"); + } else { + FURI_LOG_E(TAG, "Failed to initialization"); + furi_hal_subghz_disable_ext_power(); + } + return result; } void furi_hal_subghz_sleep() { furi_assert(furi_hal_subghz.state == SubGhzStateIdle); - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + + furi_hal_subghz_disable_ext_power(); furi_hal_subghz.preset = FuriHalSubGhzPresetIDLE; } void furi_hal_subghz_dump_state() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); printf( "[furi_hal_subghz] cc1101 chip %d, version %d\r\n", - cc1101_get_partnumber(&furi_hal_spi_bus_handle_subghz), - cc1101_get_version(&furi_hal_spi_bus_handle_subghz)); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_get_partnumber(furi_hal_subghz.spi_bus_handle), + cc1101_get_version(furi_hal_subghz.spi_bus_handle)); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { @@ -137,15 +230,15 @@ void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) { void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { //load config - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); uint32_t i = 0; uint8_t pa[8] = {0}; while(preset_data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, preset_data[i], preset_data[i + 1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, preset_data[i], preset_data[i + 1]); i += 2; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); //load pa table memcpy(&pa[0], &preset_data[i + 2], 8); @@ -167,48 +260,48 @@ void furi_hal_subghz_load_custom_preset(uint8_t* preset_data) { } void furi_hal_subghz_load_registers(uint8_t* data) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); uint32_t i = 0; while(data[i]) { - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i], data[i + 1]); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i], data[i + 1]); i += 2; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_load_patable(const uint8_t data[8]) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_set_pa_table(&furi_hal_spi_bus_handle_subghz, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_set_pa_table(furi_hal_subghz.spi_bus_handle, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_FIFO, size); - cc1101_write_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_FIFO, size); + cc1101_write_fifo(furi_hal_subghz.spi_bus_handle, data, size); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_flush_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_flush_tx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_flush_tx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } bool furi_hal_subghz_rx_pipe_not_empty() { CC1101RxBytes status[1]; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); cc1101_read_reg( - &furi_hal_spi_bus_handle_subghz, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_subghz.spi_bus_handle, (CC1101_STATUS_RXBYTES) | CC1101_BURST, (uint8_t*)status); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); // TODO: you can add a buffer overflow flag if needed if(status->NUM_RXBYTES > 0) { return true; @@ -218,10 +311,10 @@ bool furi_hal_subghz_rx_pipe_not_empty() { } bool furi_hal_subghz_is_rx_data_crc_valid() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_read_reg(furi_hal_subghz.spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); if(((data[0] >> 7) & 0x01)) { return true; } else { @@ -230,51 +323,52 @@ bool furi_hal_subghz_is_rx_data_crc_valid() { } void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_read_fifo(&furi_hal_spi_bus_handle_subghz, data, size); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_read_fifo(furi_hal_subghz.spi_bus_handle, data, size); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_shutdown() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); // Reset and shutdown - cc1101_shutdown(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_shutdown(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); + furi_hal_subghz_disable_ext_power(); } void furi_hal_subghz_reset() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - cc1101_reset(&furi_hal_spi_bus_handle_subghz); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHighImpedance); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + cc1101_reset(furi_hal_subghz.spi_bus_handle); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHighImpedance); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_idle() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } void furi_hal_subghz_rx() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } bool furi_hal_subghz_tx() { - // if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + cc1101_switch_to_tx(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return true; } float furi_hal_subghz_get_rssi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - int32_t rssi_dec = cc1101_get_rssi(&furi_hal_spi_bus_handle_subghz); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + int32_t rssi_dec = cc1101_get_rssi(furi_hal_subghz.spi_bus_handle); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); float rssi = rssi_dec; if(rssi_dec >= 128) { @@ -287,18 +381,18 @@ float furi_hal_subghz_get_rssi() { } uint8_t furi_hal_subghz_get_lqi() { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint8_t data[1]; - cc1101_read_reg(&furi_hal_spi_bus_handle_subghz, CC1101_STATUS_LQI | CC1101_BURST, data); - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + cc1101_read_reg(furi_hal_subghz.spi_bus_handle, CC1101_STATUS_LQI | CC1101_BURST, data); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return data[0] & 0x7F; } /* Modified by @tkerby & MX to the full YARD Stick One extended range of 281-361 MHz, 378-481 MHz, and 749-962 MHz. - These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage - Set flag use_ext_range_at_own_risk in extend_range.txt to use + These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage! */ + bool furi_hal_subghz_is_frequency_valid(uint32_t value) { if(!(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && @@ -325,113 +419,72 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { } bool furi_hal_subghz_is_tx_allowed(uint32_t value) { - //checking regional settings bool is_extended = false; - bool is_allowed = false; + // TODO: !!! Move file check to another place Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/extend_range.txt")) { - flipper_format_read_bool(fff_data_file, "use_ext_range_at_own_risk", &is_extended, 1); - flipper_format_read_bool(fff_data_file, "ignore_default_tx_region", &is_allowed, 1); + + if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) { + flipper_format_read_bool( + fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended, 1); } + flipper_format_free(fff_data_file); furi_record_close(RECORD_STORAGE); - switch(furi_hal_version_get_hw_region()) { - case FuriHalVersionRegionEuRu: - //433,05..434,79; 868,15..868,55 - if(!(value >= 433050000 && value <= 434790000) && - !(value >= 868150000 && value <= 868550000)) { - } else { - is_allowed = true; - } - break; - case FuriHalVersionRegionUsCaAu: - //304,10..321,95; 433,05..434,79; 915,00..928,00 - if(!(value >= 304100000 && value <= 321950000) && - !(value >= 433050000 && value <= 434790000) && - !(value >= 915000000 && value <= 928000000)) { - } else { - if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { - if(value <= 321950000 && - ((furi_hal_subghz.preset == FuriHalSubGhzPresetOok270Async) || - (furi_hal_subghz.preset == FuriHalSubGhzPresetOok650Async))) { - furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable_au); - } - } - is_allowed = true; - } - break; - case FuriHalVersionRegionJp: - //312,00..315,25; 920,50..923,50 - if(!(value >= 312000000 && value <= 315250000) && - !(value >= 920500000 && value <= 923500000)) { - } else { - is_allowed = true; - } - break; - - default: - is_allowed = true; - break; - } - // No flag - test original range, flag set, test extended range - if(!(value >= 299999755 && value <= 348000335) && + if(!(value >= 299999755 && value <= 350000335) && !(value >= 386999938 && value <= 464000000) && !(value >= 778999847 && value <= 928000000) && !(is_extended)) { - FURI_LOG_I(TAG, "Frequency blocked - outside standard range"); - is_allowed = false; + FURI_LOG_I(TAG, "Frequency blocked - outside default range"); + return false; } else if( !(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && !(value >= 749000000 && value <= 962000000) && is_extended) { - FURI_LOG_I(TAG, "Frequency blocked - outside extended range"); - is_allowed = false; + FURI_LOG_I(TAG, "Frequency blocked - outside dangerous range"); + return false; } - return is_allowed; + + return true; } uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - if(furi_hal_region_is_frequency_allowed(value)) { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } else { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - } + furi_hal_subghz.regulation = SubGhzRegulationTxRx; - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); - uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value); - cc1101_calibrate(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); + uint32_t real_frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, value); + cc1101_calibrate(furi_hal_subghz.spi_bus_handle); while(true) { - CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz); + CC1101Status status = cc1101_get_status(furi_hal_subghz.spi_bus_handle); if(status.STATE == CC1101StateIDLE) break; } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); return real_frequency; } void furi_hal_subghz_set_path(FuriHalSubGhzPath path) { - furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); if(path == FuriHalSubGhzPath433) { furi_hal_gpio_write(&gpio_rf_sw_0, 0); cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); } else if(path == FuriHalSubGhzPath315) { furi_hal_gpio_write(&gpio_rf_sw_0, 1); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); } else if(path == FuriHalSubGhzPath868) { furi_hal_gpio_write(&gpio_rf_sw_0, 1); cc1101_write_reg( - &furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); + furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV); } else if(path == FuriHalSubGhzPathIsolate) { furi_hal_gpio_write(&gpio_rf_sw_0, 0); - cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG2, CC1101IocfgHW); + cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG2, CC1101IocfgHW); } else { furi_crash("SubGhz: Incorrect path during set."); } - furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz); + furi_hal_spi_release(furi_hal_subghz.spi_bus_handle); } static bool furi_hal_subghz_start_debug() { @@ -462,31 +515,53 @@ volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL; volatile void* furi_hal_subghz_capture_callback_context = NULL; static void furi_hal_subghz_capture_ISR() { - // Channel 1 - if(LL_TIM_IsActiveFlag_CC1(TIM2)) { - LL_TIM_ClearFlag_CC1(TIM2); - furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + if(!furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - furi_hal_subghz_capture_callback( - true, - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); + furi_hal_subghz_capture_callback( + true, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } + } else { + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, TIM2->CNT, (void*)furi_hal_subghz_capture_callback_context); + } } - } - // Channel 2 - if(LL_TIM_IsActiveFlag_CC2(TIM2)) { - LL_TIM_ClearFlag_CC2(TIM2); - if(furi_hal_subghz_capture_callback) { - if(furi_hal_subghz.async_mirror_pin != NULL) - furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + //Forced correction for improved accuracy + TIM2->CNT = 9; + } else { + // Channel 1 + if(LL_TIM_IsActiveFlag_CC1(TIM2)) { + LL_TIM_ClearFlag_CC1(TIM2); + furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, false); - furi_hal_subghz_capture_callback( - false, - LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, - (void*)furi_hal_subghz_capture_callback_context); + furi_hal_subghz_capture_callback( + true, + furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); + } + } + // Channel 2 + if(LL_TIM_IsActiveFlag_CC2(TIM2)) { + LL_TIM_ClearFlag_CC2(TIM2); + if(furi_hal_subghz_capture_callback) { + if(furi_hal_subghz.async_mirror_pin != NULL) + furi_hal_gpio_write(furi_hal_subghz.async_mirror_pin, true); + + furi_hal_subghz_capture_callback( + false, + LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration, + (void*)furi_hal_subghz_capture_callback_context); + } } } } @@ -498,47 +573,64 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* furi_hal_subghz_capture_callback = callback; furi_hal_subghz_capture_callback_context = context; - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, + GpioModeInterruptRiseFall, + GpioPullUp, + GpioSpeedVeryHigh); + furi_hal_gpio_add_int_callback( + furi_hal_subghz.cc1101_g0_pin, + furi_hal_subghz_capture_ISR, + furi_hal_subghz_capture_callback); + furi_hal_gpio_enable_int_callback(furi_hal_subghz.cc1101_g0_pin); + } else { + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2); + } // Timer: base LL_TIM_InitTypeDef TIM_InitStruct = {0}; TIM_InitStruct.Prescaler = 64 - 1; TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; TIM_InitStruct.Autoreload = 0x7FFFFFFE; - TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; + TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV4; // Clock division for capture filter LL_TIM_Init(TIM2, &TIM_InitStruct); // Timer: advanced LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM2); - LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); - LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); - LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); - LL_TIM_EnableMasterSlaveMode(TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2); + LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET); + LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET); + LL_TIM_EnableMasterSlaveMode(TIM2); + } LL_TIM_DisableDMAReq_TRIG(TIM2); LL_TIM_DisableIT_TRIG(TIM2); - // Timer: channel 1 indirect - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + // Timer: channel 1 indirect + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1); - // Timer: channel 2 direct - LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); - LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); - LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); - LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); + // Timer: channel 2 direct + LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI); + LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1); + LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING); + LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV32_N8); - // ISR setup - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); + // ISR setup + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_capture_ISR, NULL); - // Interrupts and channels - LL_TIM_EnableIT_CC1(TIM2); - LL_TIM_EnableIT_CC2(TIM2); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); - LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + // Interrupts and channels + LL_TIM_EnableIT_CC1(TIM2); + LL_TIM_EnableIT_CC2(TIM2); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1); + LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2); + } // Start timer LL_TIM_SetCounter(TIM2, 0); @@ -549,6 +641,9 @@ void furi_hal_subghz_start_async_rx(FuriHalSubGhzCaptureCallback callback, void* // Switch to RX furi_hal_subghz_rx(); + + // Clear the variable after the end of the session + furi_hal_subghz_capture_delta_duration = 0; } void furi_hal_subghz_stop_async_rx() { @@ -565,9 +660,11 @@ void furi_hal_subghz_stop_async_rx() { furi_hal_subghz_stop_debug(); FURI_CRITICAL_EXIT(); - furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + if(furi_hal_subghz.radio_type == SubGhzRadioInternal) { + furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); + } - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); } typedef struct { @@ -601,8 +698,8 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { *buffer = 0; buffer++; samples--; - LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_DisableIT_TC(SUBGHZ_DMA_CH1_DEF); LL_TIM_EnableIT_UPDATE(TIM2); break; } else { @@ -643,17 +740,22 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { static void furi_hal_subghz_async_tx_dma_isr() { furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx); - if(LL_DMA_IsActiveFlag_HT1(DMA1)) { - LL_DMA_ClearFlag_HT1(DMA1); + +#if SUBGHZ_DMA_CH1_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_HT1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } - if(LL_DMA_IsActiveFlag_TC1(DMA1)) { - LL_DMA_ClearFlag_TC1(DMA1); + if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) { + LL_DMA_ClearFlag_TC1(SUBGHZ_DMA); furi_hal_subghz_async_tx_refill( furi_hal_subghz_async_tx.buffer + API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF); } +#else +#error Update this code. Would you kindly? +#endif } static void furi_hal_subghz_async_tx_timer_isr() { @@ -662,11 +764,12 @@ static void furi_hal_subghz_async_tx_timer_isr() { if(LL_TIM_GetAutoReload(TIM2) == 0) { if(furi_hal_subghz.state == SubGhzStateAsyncTx) { furi_hal_subghz.state = SubGhzStateAsyncTxLast; - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF); } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) { furi_hal_subghz.state = SubGhzStateAsyncTxEnd; //forcibly pulls the pin to the ground so that there is no carrier - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullDown, GpioSpeedLow); LL_TIM_DisableCounter(TIM2); } else { furi_crash(NULL); @@ -680,7 +783,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_assert(callback); //If transmission is prohibited by regional settings - // if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; + if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false; furi_hal_subghz_async_tx.callback = callback; furi_hal_subghz_async_tx.callback_context = context; @@ -693,9 +796,19 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* furi_hal_subghz_async_tx.buffer = malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t)); - // Connect CC1101_GD0 to TIM2 as output - furi_hal_gpio_init_ex( - &gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2); + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); + furi_hal_gpio_init( + furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + // Connect CC1101_GD0 to TIM2 as output + furi_hal_gpio_init_ex( + &gpio_cc1101_g0, + GpioModeAltFunctionPushPull, + GpioPullDown, + GpioSpeedLow, + GpioAltFn1TIM2); + } // Configure DMA LL_DMA_InitTypeDef dma_config = {0}; @@ -710,11 +823,11 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_MODE_NORMAL; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, furi_hal_subghz_async_tx_dma_isr, NULL); - LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_1); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL); + LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableIT_HT(SUBGHZ_DMA_CH1_DEF); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH1_DEF); // Configure TIM2 LL_TIM_InitTypeDef TIM_InitStruct = {0}; @@ -755,9 +868,20 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* LL_TIM_SetCounter(TIM2, 0); LL_TIM_EnableCounter(TIM2); - // Start debug - if(furi_hal_subghz_start_debug()) { - const GpioPin* gpio = furi_hal_subghz.async_mirror_pin; + //Signal generation for external module + + // Start debug (and speaker) + furi_hal_subghz_start_debug(); + + const GpioPin* gpio = furi_hal_subghz.cc1101_g0_pin; + + if((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) { + gpio = furi_hal_subghz.async_mirror_pin; + } + if(((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) || + (furi_hal_subghz.radio_type == SubGhzRadioExternal)) { furi_hal_subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER; furi_hal_subghz_debug_gpio_buff[1] = gpio->pin; @@ -772,9 +896,9 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* dma_config.NbData = 2; dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP; dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH; - LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config); - LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, 2); - LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); + LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config); + LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2); + LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF); } return true; @@ -792,9 +916,9 @@ void furi_hal_subghz_stop_async_tx() { // Shutdown radio furi_hal_subghz_idle(); -#ifdef FURI_HAL_SUBGHZ_TX_GPIO - furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false); -#endif + if(furi_hal_subghz.radio_type == SubGhzRadioExternal) { + furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, false); + } // Deinitialize Timer FURI_CRITICAL_ENTER(); @@ -802,16 +926,20 @@ void furi_hal_subghz_stop_async_tx() { furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL); // Deinitialize DMA - LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1); + LL_DMA_DeInit(SUBGHZ_DMA_CH1_DEF); - furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch1, NULL, NULL); + furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, NULL, NULL); // Deinitialize GPIO - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); // Stop debug - if(furi_hal_subghz_stop_debug()) { - LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); + furi_hal_subghz_stop_debug(); + + if(((furi_hal_subghz.async_mirror_pin != NULL) && + (furi_hal_subghz.radio_type == SubGhzRadioInternal)) || + (furi_hal_subghz.radio_type == SubGhzRadioExternal)) { + LL_DMA_DisableChannel(SUBGHZ_DMA_CH2_DEF); } FURI_CRITICAL_EXIT(); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.h b/firmware/targets/f7/furi_hal/furi_hal_subghz.h index b3319e226..b19a71f9a 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.h @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef __cplusplus extern "C" { @@ -61,6 +62,25 @@ typedef enum { SubGhzRegulationTxRx, /**TxRx*/ } SubGhzRegulation; +/** SubGhz radio types */ +typedef enum { + SubGhzRadioInternal, + SubGhzRadioExternal, +} SubGhzRadioType; + +/** Structure for accessing SubGhz settings*/ +typedef struct { + volatile SubGhzState state; + volatile SubGhzRegulation regulation; + volatile FuriHalSubGhzPreset preset; + const GpioPin* async_mirror_pin; + SubGhzRadioType radio_type; + FuriHalSpiBusHandle* spi_bus_handle; + const GpioPin* cc1101_g0_pin; +} FuriHalSubGhz; + +extern volatile FuriHalSubGhz furi_hal_subghz; + /* Mirror RX/TX async modulation signal to specified pin * * @warning Configures pin to output mode. Make sure it is not connected @@ -76,6 +96,13 @@ void furi_hal_subghz_set_async_mirror_pin(const GpioPin* pin); */ void furi_hal_subghz_init(); +/** Initialize and switch to power save mode Used by internal API-HAL + * initialization routine Can be used to reinitialize device to safe state and + * send it to sleep + * @return true if initialisation is successfully + */ +bool furi_hal_subghz_init_check(void); + /** Send device to sleep mode */ void furi_hal_subghz_sleep(); @@ -258,6 +285,30 @@ bool furi_hal_subghz_is_async_tx_complete(); */ void furi_hal_subghz_stop_async_tx(); +/** Switching between internal and external radio + * @param state SubGhzRadioInternal or SubGhzRadioExternal + * @return true if switching is successful + */ +bool furi_hal_subghz_set_radio_type(SubGhzRadioType state); + +/** Get current radio + * @return SubGhzRadioInternal or SubGhzRadioExternal + */ +SubGhzRadioType furi_hal_subghz_get_radio_type(void); + +/** Check for a radio module + * @return true if check is successful + */ +bool furi_hal_subghz_check_radio(void); + +/** Turn on the power of the external radio module + */ +void furi_hal_subghz_enable_ext_power(void); + +/** Turn off the power of the external radio module + */ +void furi_hal_subghz_disable_ext_power(void); + #ifdef __cplusplus } #endif diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h index 5ea17b6dd..b2b5760fd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h @@ -273,16 +273,6 @@ static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { 0x00, 0x00}; -static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { - 0x00, - 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00}; - static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 0x00, diff --git a/lib/subghz/SConscript b/lib/subghz/SConscript index 6d091114b..8fbec94ad 100644 --- a/lib/subghz/SConscript +++ b/lib/subghz/SConscript @@ -11,6 +11,7 @@ env.Append( File("subghz_tx_rx_worker.h"), File("transmitter.h"), File("registry.h"), + File("protocols/protocol_items.h"), File("protocols/raw.h"), File("blocks/const.h"), File("blocks/decoder.h"), diff --git a/lib/subghz/blocks/encoder.c b/lib/subghz/blocks/encoder.c index e325be21c..49ec4f177 100644 --- a/lib/subghz/blocks/encoder.c +++ b/lib/subghz/blocks/encoder.c @@ -55,4 +55,4 @@ size_t subghz_protocol_blocks_get_upload_from_bit_array( upload[size_upload++] = level_duration_make( subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); return size_upload; -} \ No newline at end of file +} diff --git a/lib/subghz/blocks/generic.c b/lib/subghz/blocks/generic.c index 94114676d..3d59adc82 100644 --- a/lib/subghz/blocks/generic.c +++ b/lib/subghz/blocks/generic.c @@ -100,7 +100,7 @@ bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperForma FURI_LOG_E(TAG, "Missing Bit"); break; } - instance->data_count_bit = (uint8_t)temp_data; + instance->data_count_bit = (uint16_t)temp_data; uint8_t key_data[sizeof(uint64_t)] = {0}; if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { diff --git a/lib/subghz/blocks/generic.h b/lib/subghz/blocks/generic.h index 8839c0b54..e69de8b4f 100644 --- a/lib/subghz/blocks/generic.h +++ b/lib/subghz/blocks/generic.h @@ -20,7 +20,7 @@ struct SubGhzBlockGeneric { uint64_t data; uint64_t data_2; uint32_t serial; - uint8_t data_count_bit; + uint16_t data_count_bit; uint8_t btn; uint32_t cnt; uint8_t cnt_2; diff --git a/lib/subghz/protocols/bin_raw.c b/lib/subghz/protocols/bin_raw.c index 3fef76af2..67e0467ee 100644 --- a/lib/subghz/protocols/bin_raw.c +++ b/lib/subghz/protocols/bin_raw.c @@ -111,7 +111,7 @@ const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { const SubGhzProtocol subghz_protocol_bin_raw = { .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, - .type = SubGhzProtocolTypeStatic, + .type = SubGhzProtocolTypeBinRAW, #ifdef BIN_RAW_DEBUG .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | @@ -1117,4 +1117,4 @@ void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* outpu } furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); -} \ No newline at end of file +} diff --git a/lib/subghz/protocols/kinggates_stylo_4k.c b/lib/subghz/protocols/kinggates_stylo_4k.c index 2c8de0d2d..5f2a83d77 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.c +++ b/lib/subghz/protocols/kinggates_stylo_4k.c @@ -23,7 +23,6 @@ struct SubGhzProtocolDecoderKingGates_stylo_4k { SubGhzBlockDecoder decoder; SubGhzBlockGeneric generic; - uint64_t data; uint16_t header_count; SubGhzKeystore* keystore; }; @@ -33,6 +32,7 @@ struct SubGhzProtocolEncoderKingGates_stylo_4k { SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + SubGhzKeystore* keystore; }; typedef enum { @@ -57,23 +57,268 @@ const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc, + .free = subghz_protocol_encoder_kinggates_stylo_4k_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize, + .stop = subghz_protocol_encoder_kinggates_stylo_4k_stop, + .yield = subghz_protocol_encoder_kinggates_stylo_4k_yield, }; const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, }; +// +// Encoder +// + +// Pre define function +static void subghz_protocol_kinggates_stylo_4k_remote_controller( + SubGhzBlockGeneric* instance, + SubGhzKeystore* keystore); + +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = + malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k)); + + instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; + instance->generic.protocol_name = instance->base.protocol->name; + instance->keystore = subghz_environment_get_keystore(environment); + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + return instance; +} + +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) { + SubGhzProtocolEncoderKingGates_stylo_4k* 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; +} + +/** + * Key generation from simple data + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance + * @param btn Button number, 4 bit + */ +static bool subghz_protocol_kinggates_stylo_4k_gen_data( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + UNUSED(btn); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32); + uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53); + int res = 0; + uint32_t decrypt = 0; + + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); + break; + } + } + instance->generic.cnt = decrypt & 0xFFFF; + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + instance->generic.btn = (fix >> 17) & 0x0F; + instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); + + uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt; + + uint64_t encrypt = 0; + for + M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { + res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); + if(res == 0) { + //Simple Learning + encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key); + encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32); + instance->generic.data_2 = encrypt << 4; + return true; + } + } + + return false; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return true On success + */ +static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( + SubGhzProtocolEncoderKingGates_stylo_4k* instance, + uint8_t btn) { + furi_assert(instance); + + // Gen new key + if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + + // Start + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500); + + // Send header + for(uint8_t i = 12; i > 0; i--) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + + // After header + instance->encoder.upload[index - 1].duration = + (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2; + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2); + + // Send key fix + for(uint8_t i = 53; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Send key hop + for(uint8_t i = 36; i > 0; i--) { + if(bit_read(instance->generic.data_2, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); + } + } + + // Set upload size after generating upload, fix it later + + instance->encoder.size_upload = index; + + return true; +} + +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + subghz_protocol_kinggates_stylo_4k_remote_controller( + &instance->generic, instance->keystore); + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Missing Data"); + break; + } + + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; + } + + subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} + +// +// Decoder +// void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { SubGhzProtocolDecoderKingGates_stylo_4k* instance = malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); @@ -130,7 +375,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; instance->decoder.decode_data = 0; - instance->data = 0; + instance->generic.data_2 = 0; instance->decoder.decode_count_bit = 0; instance->header_count = 0; } @@ -140,8 +385,8 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { if(instance->decoder.decode_count_bit == subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { - instance->generic.data = instance->data; - instance->data = instance->decoder.decode_data; + instance->generic.data = instance->generic.data_2; + instance->generic.data_2 = instance->decoder.decode_data; instance->generic.data_count_bit = instance->decoder.decode_count_bit; if(instance->base.callback) @@ -150,7 +395,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; instance->decoder.decode_data = 0; - instance->data = 0; + instance->generic.data_2 = 0; instance->decoder.decode_count_bit = 0; instance->header_count = 0; break; @@ -185,7 +430,7 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, instance->header_count = 0; } if(instance->decoder.decode_count_bit == 53) { - instance->data = instance->decoder.decode_data; + instance->generic.data_2 = instance->decoder.decode_data; instance->decoder.decode_data = 0; } } else { @@ -199,11 +444,11 @@ void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, /** * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param file_name Full path to rainbow table the file + * @param data Input encrypted data + * @param keystore Pointer to a SubGhzKeystore* instance */ static void subghz_protocol_kinggates_stylo_4k_remote_controller( SubGhzBlockGeneric* instance, - uint64_t data, SubGhzKeystore* keystore) { /** * 9500us 12*(400/400) 2200/800|1-bit|0-bit| @@ -226,7 +471,7 @@ static void subghz_protocol_kinggates_stylo_4k_remote_controller( * */ - uint32_t hop = subghz_protocol_blocks_reverse_key(data >> 4, 32); + uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32); uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); bool ret = false; uint32_t decrypt = 0; @@ -270,7 +515,7 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( uint8_t key_data[sizeof(uint64_t)] = {0}; for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF; } if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { @@ -306,8 +551,9 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( FURI_LOG_E(TAG, "Missing Data"); break; } + for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->data = instance->data << 8 | key_data[i]; + instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; } ret = true; } while(false); @@ -317,8 +563,7 @@ bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - subghz_protocol_kinggates_stylo_4k_remote_controller( - &instance->generic, instance->data, instance->keystore); + subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore); furi_string_cat_printf( output, @@ -328,9 +573,9 @@ void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriSt "Cnt:0x%04lX\r\n", instance->generic.protocol_name, instance->generic.data, - instance->data, + instance->generic.data_2, instance->generic.data_count_bit, instance->generic.serial, instance->generic.btn, instance->generic.cnt); -} +} \ No newline at end of file diff --git a/lib/subghz/protocols/kinggates_stylo_4k.h b/lib/subghz/protocols/kinggates_stylo_4k.h index c9f1cf380..9717f6715 100644 --- a/lib/subghz/protocols/kinggates_stylo_4k.h +++ b/lib/subghz/protocols/kinggates_stylo_4k.h @@ -10,6 +10,42 @@ extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; +/** + * Allocate SubGhzProtocolEncoderKingGates_stylo_4k. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKingGates_stylo_4k. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + */ +void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context); + /** * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. * @param environment Pointer to a SubGhzEnvironment instance diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index 96717e56f..74244c5ff 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -36,13 +36,13 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = { &subghz_protocol_intertechno_v3, &subghz_protocol_clemsa, &subghz_protocol_ansonic, - &subghz_protocol_pocsag, &subghz_protocol_smc5326, &subghz_protocol_holtek_th12x, &subghz_protocol_linear_delta3, &subghz_protocol_dooya, &subghz_protocol_alutech_at_4n, &subghz_protocol_kinggates_stylo_4k, + &subghz_protocol_bin_raw, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 362b0459a..b7e082bf5 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -37,12 +37,12 @@ #include "intertechno_v3.h" #include "clemsa.h" #include "ansonic.h" -#include "pocsag.h" #include "smc5326.h" #include "holtek_ht12x.h" #include "dooya.h" #include "alutech_at_4n.h" #include "kinggates_stylo_4k.h" +#include "bin_raw.h" #ifdef __cplusplus extern "C" { diff --git a/lib/subghz/protocols/raw.c b/lib/subghz/protocols/raw.c index 393d7f360..01a229047 100644 --- a/lib/subghz/protocols/raw.c +++ b/lib/subghz/protocols/raw.c @@ -8,16 +8,11 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#include #include #include -#include #define TAG "SubGhzProtocolRAW" #define SUBGHZ_DOWNLOAD_MAX_SIZE 512 -#define SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE 2048 -#define SUBGHZ_AUTO_DETECT_RAW_THRESHOLD -72.0f -#define SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES 40 static const SubGhzBlockConst subghz_protocol_raw_const = { .te_short = 50, @@ -29,8 +24,6 @@ static const SubGhzBlockConst subghz_protocol_raw_const = { struct SubGhzProtocolDecoderRAW { SubGhzProtocolDecoderBase base; - SubGhzBlockDecoder decoder; - int32_t* upload_raw; uint16_t ind_write; Storage* storage; @@ -39,10 +32,6 @@ struct SubGhzProtocolDecoderRAW { FuriString* file_name; size_t sample_write; bool last_level; - bool auto_mode; - bool has_rssi_above_threshold; - int rssi_threshold; - uint8_t postroll_frames; bool pause; }; @@ -67,8 +56,8 @@ const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { .feed = subghz_protocol_decoder_raw_feed, .reset = subghz_protocol_decoder_raw_reset, - .get_hash_data = subghz_protocol_decoder_raw_get_hash_data, - .serialize = subghz_protocol_decoder_raw_serialize, + .get_hash_data = NULL, + .serialize = NULL, .deserialize = subghz_protocol_decoder_raw_deserialize, .get_string = subghz_protocol_decoder_raw_get_string, }; @@ -213,36 +202,6 @@ void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { instance->file_is_open = RAWFileIsOpenClose; } -void subghz_protocol_decoder_raw_set_rssi_threshold(void* context, int rssi_threshold) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - FURI_LOG_D(TAG, "RSSI set: (%d)", rssi_threshold); - - instance->rssi_threshold = rssi_threshold; - - subghz_protocol_decoder_raw_reset(context); -} - -void subghz_protocol_decoder_raw_set_auto_mode(void* context, bool auto_mode) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - instance->auto_mode = auto_mode; - - if(auto_mode) { - if(instance->upload_raw == NULL) { - instance->upload_raw = malloc(SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); - } - } else { - if(instance->upload_raw != NULL) { - free(instance->upload_raw); - instance->upload_raw = NULL; - } - } - - subghz_protocol_decoder_raw_reset(context); -} - void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { furi_assert(instance); @@ -263,8 +222,6 @@ void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { instance->ind_write = 0; instance->last_level = false; instance->file_is_open = RAWFileIsOpenClose; - instance->postroll_frames = 0; - instance->rssi_threshold = SUBGHZ_AUTO_DETECT_RAW_THRESHOLD; instance->file_name = furi_string_alloc(); return instance; @@ -274,10 +231,6 @@ void subghz_protocol_decoder_raw_free(void* context) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; furi_string_free(instance->file_name); - if(instance->upload_raw != NULL) { - free(instance->upload_raw); - instance->upload_raw = NULL; - } free(instance); } @@ -285,66 +238,23 @@ void subghz_protocol_decoder_raw_reset(void* context) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; instance->ind_write = 0; - instance->has_rssi_above_threshold = false; instance->last_level = false; - instance->postroll_frames = 0; -} - -bool subghz_protocol_decoder_raw_write_data(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - bool wrote_data = false; - - if(instance->last_level != level) { - instance->last_level = (level ? true : false); - instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); - subghz_protocol_blocks_add_bit(&instance->decoder, (level) ? 1 : 0); - wrote_data = true; - } - - if(instance->ind_write == SUBGHZ_AUTO_DETECT_DOWNLOAD_MAX_SIZE) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - - return false; - } - - return wrote_data; } void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { furi_assert(context); SubGhzProtocolDecoderRAW* instance = context; - if(instance->upload_raw != NULL && !instance->pause && - duration > subghz_protocol_raw_const.te_short) { - if(instance->auto_mode) { - float rssi = furi_hal_subghz_get_rssi(); - - if(rssi >= instance->rssi_threshold) { - subghz_protocol_decoder_raw_write_data(context, level, duration); - instance->has_rssi_above_threshold = true; - instance->postroll_frames = 0; - } else if(instance->has_rssi_above_threshold) { - subghz_protocol_decoder_raw_write_data(instance, level, duration); - instance->postroll_frames++; - - if(instance->postroll_frames >= SUBGHZ_AUTO_DETECT_RAW_POSTROLL_FRAMES) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - } - } else { + if(!instance->pause && (instance->upload_raw != NULL)) { + if(duration > subghz_protocol_raw_const.te_short) { if(instance->last_level != level) { instance->last_level = (level ? true : false); instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); - subghz_protocol_blocks_add_bit(&instance->decoder, (level) ? 1 : 0); } + } - if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { - subghz_protocol_raw_save_to_file_write(instance); - } + if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { + subghz_protocol_raw_save_to_file_write(instance); } } } @@ -357,13 +267,6 @@ bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipp return true; } -uint8_t subghz_protocol_decoder_raw_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { furi_assert(context); //SubGhzProtocolDecoderRAW* instance = context; @@ -382,13 +285,6 @@ void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { return instance; } -int subghz_protocol_encoder_get_rssi_threshold(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - return instance->rssi_threshold; -} - void subghz_protocol_encoder_raw_stop(void* context) { SubGhzProtocolEncoderRAW* instance = context; instance->is_running = false; @@ -446,70 +342,6 @@ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* } while(false); } -bool subghz_protocol_decoder_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - if(instance->auto_mode) { - furi_assert(instance); - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_header_cstr( - flipper_format, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - break; - } - subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - flipper_format, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - break; - } - if(!flipper_format_write_hex( - flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - break; - } - } - if(!flipper_format_write_string_cstr( - flipper_format, "Protocol", instance->base.protocol->name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - - if(!flipper_format_write_int32( - flipper_format, "RAW_Data", instance->upload_raw, instance->ind_write)) { - FURI_LOG_E(TAG, "Unable to add Raw Data"); - break; - } else { - instance->ind_write = 0; - } - res = true; - } while(false); - furi_string_free(temp_str); - return res; - } else { - return false; - } -} - bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { furi_assert(context); SubGhzProtocolEncoderRAW* instance = context; diff --git a/lib/subghz/protocols/raw.h b/lib/subghz/protocols/raw.h index 08efff50e..44c7faec5 100644 --- a/lib/subghz/protocols/raw.h +++ b/lib/subghz/protocols/raw.h @@ -2,8 +2,6 @@ #include "base.h" -#include - #define SUBGHZ_PROTOCOL_RAW_NAME "RAW" #ifdef __cplusplus @@ -31,27 +29,6 @@ bool subghz_protocol_raw_save_to_file_init( const char* dev_name, SubGhzRadioPreset* preset); -/** - * Set SubGhzProtocolDecoderRAW to auto mode, which allows subghz_scene_receiver to capture RAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param auto_mode Whether or not to enable auto mode - */ -void subghz_protocol_decoder_raw_set_auto_mode(void* context, bool auto_mode); - -/** - * Set RSSI threshold ("sensitivity" level). - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param rssi_threshold The desired RSSI threshold - */ -void subghz_protocol_decoder_raw_set_rssi_threshold(void* context, int rssi_threshold); - -/** - * Get RSSI threshold ("sensitivity" level). - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @return rssi threshold in db - */ -int subghz_protocol_encoder_get_rssi_threshold(void* context); - /** * Stop writing file to flash * @param instance Pointer to a SubGhzProtocolDecoderRAW instance @@ -84,15 +61,6 @@ void subghz_protocol_decoder_raw_free(void* context); */ void subghz_protocol_decoder_raw_reset(void* context); -/** - * Write raw data to the instance's internal buffer. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - * @return whether or not data was written - */ -bool subghz_protocol_decoder_raw_write_data(void* context, bool level, uint32_t duration); - /** * Parse a raw sequence of levels and durations received from the air. * @param context Pointer to a SubGhzProtocolDecoderRAW instance @@ -103,19 +71,12 @@ void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t durati /** * Deserialize data SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance + * @param context Pointer to a SubGhzProtocolDecoderRAW instance + * @param flipper_format Pointer to a FlipperFormat instance * @return true On success */ bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_raw_get_hash_data(void* context); - /** * Getting a textual representation of the received data. * @param context Pointer to a SubGhzProtocolDecoderRAW instance @@ -167,19 +128,6 @@ void subghz_protocol_raw_file_encoder_worker_set_callback_end( */ void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); -/** - * Serialize data SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param frequency The frequency at which the signal was received, Hz - * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset - * @return true On success - */ -bool subghz_protocol_decoder_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - /** * Deserialize and generating an upload to send. * @param context Pointer to a SubGhzProtocolEncoderRAW instance @@ -191,7 +139,7 @@ bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipp /** * Getting the level and duration of the upload to be loaded into DMA. * @param context Pointer to a SubGhzProtocolEncoderRAW instance - * @return LevelDuration + * @return LevelDuration */ LevelDuration subghz_protocol_encoder_raw_yield(void* context); diff --git a/lib/subghz/protocols/secplus_v2.c b/lib/subghz/protocols/secplus_v2.c index 27a82beab..bcef90dad 100644 --- a/lib/subghz/protocols/secplus_v2.c +++ b/lib/subghz/protocols/secplus_v2.c @@ -261,16 +261,16 @@ static bool data = order << 4 | invert; int k = 0; for(int i = 6; i >= 0; i -= 2) { - roll_array[k++] = (data >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (data >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } } for(int i = 8; i >= 0; i -= 2) { - roll_array[k++] = (p[2] >> i) & 0x03; - if(roll_array[k] == 3) { + roll_array[k] = (p[2] >> i) & 0x03; + if(roll_array[k++] == 3) { FURI_LOG_E(TAG, "Roll_Array FAIL"); return false; } diff --git a/lib/subghz/receiver.c b/lib/subghz/receiver.c index d7b422170..698fe098e 100644 --- a/lib/subghz/receiver.c +++ b/lib/subghz/receiver.c @@ -108,11 +108,6 @@ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag fil instance->filter = filter; } -SubGhzProtocolFlag subghz_receiver_get_filter(SubGhzReceiver* instance) { - furi_assert(instance); - return instance->filter; -} - SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name( SubGhzReceiver* instance, const char* decoder_name) { diff --git a/lib/subghz/receiver.h b/lib/subghz/receiver.h index 15fc455fd..2ef722d1f 100644 --- a/lib/subghz/receiver.h +++ b/lib/subghz/receiver.h @@ -59,13 +59,6 @@ void subghz_receiver_set_rx_callback( */ void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter); -/** - * Get the filter of receivers that will work at the moment. - * @param instance Pointer to a SubGhzReceiver instance - * @return filter Filter, SubGhzProtocolFlag - */ -SubGhzProtocolFlag subghz_receiver_get_filter(SubGhzReceiver* instance); - /** * Search for a cattery by his name. * @param instance Pointer to a SubGhzReceiver instance diff --git a/lib/subghz/subghz_tx_rx_worker.c b/lib/subghz/subghz_tx_rx_worker.c index 42124bebc..e380fc967 100644 --- a/lib/subghz/subghz_tx_rx_worker.c +++ b/lib/subghz/subghz_tx_rx_worker.c @@ -70,7 +70,7 @@ bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* furi_delay_tick(1); } //waiting for reception to complete - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { + while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); @@ -104,14 +104,16 @@ void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t si furi_hal_subghz_write_packet(data, size); furi_hal_subghz_tx(); //start send instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be set -> sync transmitted + while(!furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); break; } } - while(furi_hal_gpio_read(&gpio_cc1101_g0)) { // Wait for GDO0 to be cleared -> end of packet + while(furi_hal_gpio_read( + furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet furi_delay_tick(1); if(!--timeout) { FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); @@ -134,7 +136,7 @@ static int32_t subghz_tx_rx_worker_thread(void* context) { furi_hal_subghz_idle(); furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); furi_hal_subghz_set_frequency_and_path(instance->frequency); furi_hal_subghz_flush_rx(); @@ -233,7 +235,7 @@ bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { instance->worker_running = true; - if(furi_hal_region_is_frequency_allowed(frequency)) { + if(furi_hal_subghz_is_tx_allowed(frequency)) { instance->frequency = frequency; res = true; } diff --git a/lib/subghz/types.h b/lib/subghz/types.h index 1b8ef6a14..9d121dc3c 100644 --- a/lib/subghz/types.h +++ b/lib/subghz/types.h @@ -77,6 +77,7 @@ typedef enum { SubGhzProtocolTypeRAW, SubGhzProtocolWeatherStation, SubGhzProtocolCustom, + SubGhzProtocolTypeBinRAW, } SubGhzProtocolType; typedef enum { From 708dd167c8c57b453978d406448c6769fccb5435 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 10 Feb 2023 17:36:22 +0100 Subject: [PATCH 160/231] Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into dev --- .gitignore | 2 - .../main/archive/helpers/archive_browser.c | 12 +- .../main/archive/helpers/archive_browser.h | 2 - .../main/archive/views/archive_browser_view.c | 1 - .../main/archive/views/archive_browser_view.h | 1 - .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 +- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +- .../plugins/nightstand/application.fam | 1 + applications/plugins/pomodoro/application.fam | 2 +- .../plugins/scrambler/application.fam | 2 +- .../desktop/animations/animation_manager.c | 20 +- .../desktop/animations/animation_storage.c | 10 +- applications/services/desktop/desktop.c | 5 - .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 + .../settings/xtreme_settings/xtreme_assets.c | 282 ++++++++++-------- .../settings/xtreme_settings/xtreme_assets.h | 13 +- .../xtreme_settings/xtreme_settings.h | 1 + .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 0 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 0 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 0 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 852 -> 0 bytes .../Icons/Passport/passport_happy_46x49.png | Bin 6373 -> 0 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 6373 -> 0 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 0 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 0 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 0 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 0 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 0 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 1864 -> 0 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 1895 -> 0 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 1874 -> 0 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 1863 -> 0 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 0 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 0 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 0 bytes .../nsfw}/PaxGod_TikTok_Marketing/frame_0.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_3.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_4.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_5.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_6.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_7.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_8.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_9.png | Bin .../nsfw}/PaxGod_TikTok_Marketing/meta.txt | 0 .../Anims => external/nsfw}/lvl_1/frame_0.png | Bin .../Anims => external/nsfw}/lvl_1/frame_1.png | Bin .../nsfw}/lvl_1/frame_10.png | Bin .../nsfw}/lvl_1/frame_11.png | Bin .../nsfw}/lvl_1/frame_12.png | Bin .../nsfw}/lvl_1/frame_13.png | Bin .../nsfw}/lvl_1/frame_14.png | Bin .../nsfw}/lvl_1/frame_15.png | Bin .../nsfw}/lvl_1/frame_16.png | Bin .../nsfw}/lvl_1/frame_17.png | Bin .../nsfw}/lvl_1/frame_18.png | Bin .../nsfw}/lvl_1/frame_19.png | Bin .../Anims => external/nsfw}/lvl_1/frame_2.png | Bin .../nsfw}/lvl_1/frame_20.png | Bin .../nsfw}/lvl_1/frame_21.png | Bin .../nsfw}/lvl_1/frame_22.png | Bin .../nsfw}/lvl_1/frame_23.png | Bin .../nsfw}/lvl_1/frame_24.png | Bin .../nsfw}/lvl_1/frame_25.png | Bin .../nsfw}/lvl_1/frame_26.png | Bin .../nsfw}/lvl_1/frame_27.png | Bin .../nsfw}/lvl_1/frame_28.png | Bin .../nsfw}/lvl_1/frame_29.png | Bin .../Anims => external/nsfw}/lvl_1/frame_3.png | Bin .../nsfw}/lvl_1/frame_30.png | Bin .../Anims => external/nsfw}/lvl_1/frame_4.png | Bin .../Anims => external/nsfw}/lvl_1/frame_5.png | Bin .../Anims => external/nsfw}/lvl_1/frame_6.png | Bin .../Anims => external/nsfw}/lvl_1/frame_7.png | Bin .../Anims => external/nsfw}/lvl_1/frame_8.png | Bin .../Anims => external/nsfw}/lvl_1/frame_9.png | Bin .../Anims => external/nsfw}/lvl_1/meta.txt | 0 .../nsfw}/lvl_10/frame_0.png | Bin .../nsfw}/lvl_10/frame_1.png | Bin .../nsfw}/lvl_10/frame_10.png | Bin .../nsfw}/lvl_10/frame_11.png | Bin .../nsfw}/lvl_10/frame_12.png | Bin .../nsfw}/lvl_10/frame_13.png | Bin .../nsfw}/lvl_10/frame_14.png | Bin .../nsfw}/lvl_10/frame_15.png | Bin .../nsfw}/lvl_10/frame_16.png | Bin .../nsfw}/lvl_10/frame_17.png | Bin .../nsfw}/lvl_10/frame_18.png | Bin .../nsfw}/lvl_10/frame_19.png | Bin .../nsfw}/lvl_10/frame_2.png | Bin .../nsfw}/lvl_10/frame_20.png | Bin .../nsfw}/lvl_10/frame_21.png | Bin .../nsfw}/lvl_10/frame_22.png | Bin .../nsfw}/lvl_10/frame_23.png | Bin .../nsfw}/lvl_10/frame_24.png | Bin .../nsfw}/lvl_10/frame_25.png | Bin .../nsfw}/lvl_10/frame_26.png | Bin .../nsfw}/lvl_10/frame_27.png | Bin .../nsfw}/lvl_10/frame_3.png | Bin .../nsfw}/lvl_10/frame_4.png | Bin .../nsfw}/lvl_10/frame_5.png | Bin .../nsfw}/lvl_10/frame_6.png | Bin .../nsfw}/lvl_10/frame_7.png | Bin .../nsfw}/lvl_10/frame_8.png | Bin .../nsfw}/lvl_10/frame_9.png | Bin .../Anims => external/nsfw}/lvl_10/meta.txt | 0 .../nsfw}/lvl_11/frame_0.png | Bin .../nsfw}/lvl_11/frame_1.png | Bin .../nsfw}/lvl_11/frame_10.png | Bin .../nsfw}/lvl_11/frame_11.png | Bin .../nsfw}/lvl_11/frame_12.png | Bin .../nsfw}/lvl_11/frame_13.png | Bin .../nsfw}/lvl_11/frame_14.png | Bin .../nsfw}/lvl_11/frame_15.png | Bin .../nsfw}/lvl_11/frame_16.png | Bin .../nsfw}/lvl_11/frame_17.png | Bin .../nsfw}/lvl_11/frame_18.png | Bin .../nsfw}/lvl_11/frame_19.png | Bin .../nsfw}/lvl_11/frame_2.png | Bin .../nsfw}/lvl_11/frame_20.png | Bin .../nsfw}/lvl_11/frame_21.png | Bin .../nsfw}/lvl_11/frame_22.png | Bin .../nsfw}/lvl_11/frame_23.png | Bin .../nsfw}/lvl_11/frame_24.png | Bin .../nsfw}/lvl_11/frame_25.png | Bin .../nsfw}/lvl_11/frame_26.png | Bin .../nsfw}/lvl_11/frame_27.png | Bin .../nsfw}/lvl_11/frame_28.png | Bin .../nsfw}/lvl_11/frame_29.png | Bin .../nsfw}/lvl_11/frame_3.png | Bin .../nsfw}/lvl_11/frame_30.png | Bin .../nsfw}/lvl_11/frame_31.png | Bin .../nsfw}/lvl_11/frame_32.png | Bin .../nsfw}/lvl_11/frame_33.png | Bin .../nsfw}/lvl_11/frame_34.png | Bin .../nsfw}/lvl_11/frame_35.png | Bin .../nsfw}/lvl_11/frame_36.png | Bin .../nsfw}/lvl_11/frame_37.png | Bin .../nsfw}/lvl_11/frame_38.png | Bin .../nsfw}/lvl_11/frame_39.png | Bin .../nsfw}/lvl_11/frame_4.png | Bin .../nsfw}/lvl_11/frame_40.png | Bin .../nsfw}/lvl_11/frame_41.png | Bin .../nsfw}/lvl_11/frame_42.png | Bin .../nsfw}/lvl_11/frame_43.png | Bin .../nsfw}/lvl_11/frame_44.png | Bin .../nsfw}/lvl_11/frame_45.png | Bin .../nsfw}/lvl_11/frame_46.png | Bin .../nsfw}/lvl_11/frame_47.png | Bin .../nsfw}/lvl_11/frame_48.png | Bin .../nsfw}/lvl_11/frame_49.png | Bin .../nsfw}/lvl_11/frame_5.png | Bin .../nsfw}/lvl_11/frame_6.png | Bin .../nsfw}/lvl_11/frame_7.png | Bin .../nsfw}/lvl_11/frame_8.png | Bin .../nsfw}/lvl_11/frame_9.png | Bin .../Anims => external/nsfw}/lvl_11/meta.txt | 0 .../nsfw}/lvl_12/frame_0.png | Bin .../nsfw}/lvl_12/frame_1.png | Bin .../nsfw}/lvl_12/frame_10.png | Bin .../nsfw}/lvl_12/frame_11.png | Bin .../nsfw}/lvl_12/frame_12.png | Bin .../nsfw}/lvl_12/frame_13.png | Bin .../nsfw}/lvl_12/frame_14.png | Bin .../nsfw}/lvl_12/frame_15.png | Bin .../nsfw}/lvl_12/frame_2.png | Bin .../nsfw}/lvl_12/frame_3.png | Bin .../nsfw}/lvl_12/frame_4.png | Bin .../nsfw}/lvl_12/frame_5.png | Bin .../nsfw}/lvl_12/frame_6.png | Bin .../nsfw}/lvl_12/frame_7.png | Bin .../nsfw}/lvl_12/frame_8.png | Bin .../nsfw}/lvl_12/frame_9.png | Bin .../Anims => external/nsfw}/lvl_12/meta.txt | 0 .../nsfw}/lvl_13/frame_0.png | Bin .../nsfw}/lvl_13/frame_1.png | Bin .../nsfw}/lvl_13/frame_10.png | Bin .../nsfw}/lvl_13/frame_2.png | Bin .../nsfw}/lvl_13/frame_3.png | Bin .../nsfw}/lvl_13/frame_4.png | Bin .../nsfw}/lvl_13/frame_5.png | Bin .../nsfw}/lvl_13/frame_6.png | Bin .../nsfw}/lvl_13/frame_7.png | Bin .../nsfw}/lvl_13/frame_8.png | Bin .../nsfw}/lvl_13/frame_9.png | Bin .../Anims => external/nsfw}/lvl_13/meta.txt | 0 .../nsfw}/lvl_14/frame_0.png | Bin .../nsfw}/lvl_14/frame_1.png | Bin .../nsfw}/lvl_14/frame_2.png | Bin .../nsfw}/lvl_14/frame_3.png | Bin .../nsfw}/lvl_14/frame_4.png | Bin .../nsfw}/lvl_14/frame_5.png | Bin .../nsfw}/lvl_14/frame_6.png | Bin .../nsfw}/lvl_14/frame_7.png | Bin .../Anims => external/nsfw}/lvl_14/meta.txt | 0 .../nsfw}/lvl_15/frame_0.png | Bin .../nsfw}/lvl_15/frame_1.png | Bin .../nsfw}/lvl_15/frame_10.png | Bin .../nsfw}/lvl_15/frame_11.png | Bin .../nsfw}/lvl_15/frame_12.png | Bin .../nsfw}/lvl_15/frame_13.png | Bin .../nsfw}/lvl_15/frame_14.png | Bin .../nsfw}/lvl_15/frame_15.png | Bin .../nsfw}/lvl_15/frame_16.png | Bin .../nsfw}/lvl_15/frame_17.png | Bin .../nsfw}/lvl_15/frame_18.png | Bin .../nsfw}/lvl_15/frame_19.png | Bin .../nsfw}/lvl_15/frame_2.png | Bin .../nsfw}/lvl_15/frame_20.png | Bin .../nsfw}/lvl_15/frame_21.png | Bin .../nsfw}/lvl_15/frame_3.png | Bin .../nsfw}/lvl_15/frame_4.png | Bin .../nsfw}/lvl_15/frame_5.png | Bin .../nsfw}/lvl_15/frame_6.png | Bin .../nsfw}/lvl_15/frame_7.png | Bin .../nsfw}/lvl_15/frame_8.png | Bin .../nsfw}/lvl_15/frame_9.png | Bin .../Anims => external/nsfw}/lvl_15/meta.txt | 0 .../nsfw}/lvl_16/frame_0.png | Bin .../nsfw}/lvl_16/frame_1.png | Bin .../nsfw}/lvl_16/frame_10.png | Bin .../nsfw}/lvl_16/frame_11.png | Bin .../nsfw}/lvl_16/frame_12.png | Bin .../nsfw}/lvl_16/frame_13.png | Bin .../nsfw}/lvl_16/frame_14.png | Bin .../nsfw}/lvl_16/frame_15.png | Bin .../nsfw}/lvl_16/frame_16.png | Bin .../nsfw}/lvl_16/frame_17.png | Bin .../nsfw}/lvl_16/frame_18.png | Bin .../nsfw}/lvl_16/frame_19.png | Bin .../nsfw}/lvl_16/frame_2.png | Bin .../nsfw}/lvl_16/frame_20.png | Bin .../nsfw}/lvl_16/frame_3.png | Bin .../nsfw}/lvl_16/frame_4.png | Bin .../nsfw}/lvl_16/frame_5.png | Bin .../nsfw}/lvl_16/frame_6.png | Bin .../nsfw}/lvl_16/frame_7.png | Bin .../nsfw}/lvl_16/frame_8.png | Bin .../nsfw}/lvl_16/frame_9.png | Bin .../Anims => external/nsfw}/lvl_16/meta.txt | 0 .../nsfw}/lvl_17/frame_0.png | Bin .../nsfw}/lvl_17/frame_1.png | Bin .../nsfw}/lvl_17/frame_10.png | Bin .../nsfw}/lvl_17/frame_11.png | Bin .../nsfw}/lvl_17/frame_12.png | Bin .../nsfw}/lvl_17/frame_13.png | Bin .../nsfw}/lvl_17/frame_14.png | Bin .../nsfw}/lvl_17/frame_15.png | Bin .../nsfw}/lvl_17/frame_16.png | Bin .../nsfw}/lvl_17/frame_17.png | Bin .../nsfw}/lvl_17/frame_18.png | Bin .../nsfw}/lvl_17/frame_19.png | Bin .../nsfw}/lvl_17/frame_2.png | Bin .../nsfw}/lvl_17/frame_20.png | Bin .../nsfw}/lvl_17/frame_21.png | Bin .../nsfw}/lvl_17/frame_22.png | Bin .../nsfw}/lvl_17/frame_23.png | Bin .../nsfw}/lvl_17/frame_24.png | Bin .../nsfw}/lvl_17/frame_25.png | Bin .../nsfw}/lvl_17/frame_26.png | Bin .../nsfw}/lvl_17/frame_27.png | Bin .../nsfw}/lvl_17/frame_28.png | Bin .../nsfw}/lvl_17/frame_29.png | Bin .../nsfw}/lvl_17/frame_3.png | Bin .../nsfw}/lvl_17/frame_30.png | Bin .../nsfw}/lvl_17/frame_31.png | Bin .../nsfw}/lvl_17/frame_4.png | Bin .../nsfw}/lvl_17/frame_5.png | Bin .../nsfw}/lvl_17/frame_6.png | Bin .../nsfw}/lvl_17/frame_7.png | Bin .../nsfw}/lvl_17/frame_8.png | Bin .../nsfw}/lvl_17/frame_9.png | Bin .../Anims => external/nsfw}/lvl_17/meta.txt | 0 .../nsfw}/lvl_18/frame_0.png | Bin .../nsfw}/lvl_18/frame_1.png | Bin .../nsfw}/lvl_18/frame_10.png | Bin .../nsfw}/lvl_18/frame_11.png | Bin .../nsfw}/lvl_18/frame_12.png | Bin .../nsfw}/lvl_18/frame_13.png | Bin .../nsfw}/lvl_18/frame_14.png | Bin .../nsfw}/lvl_18/frame_15.png | Bin .../nsfw}/lvl_18/frame_16.png | Bin .../nsfw}/lvl_18/frame_17.png | Bin .../nsfw}/lvl_18/frame_18.png | Bin .../nsfw}/lvl_18/frame_19.png | Bin .../nsfw}/lvl_18/frame_2.png | Bin .../nsfw}/lvl_18/frame_20.png | Bin .../nsfw}/lvl_18/frame_21.png | Bin .../nsfw}/lvl_18/frame_22.png | Bin .../nsfw}/lvl_18/frame_3.png | Bin .../nsfw}/lvl_18/frame_4.png | Bin .../nsfw}/lvl_18/frame_5.png | Bin .../nsfw}/lvl_18/frame_6.png | Bin .../nsfw}/lvl_18/frame_7.png | Bin .../nsfw}/lvl_18/frame_8.png | Bin .../nsfw}/lvl_18/frame_9.png | Bin .../Anims => external/nsfw}/lvl_18/meta.txt | 0 .../nsfw}/lvl_19/frame_0.png | Bin .../nsfw}/lvl_19/frame_1.png | Bin .../nsfw}/lvl_19/frame_10.png | Bin .../nsfw}/lvl_19/frame_11.png | Bin .../nsfw}/lvl_19/frame_12.png | Bin .../nsfw}/lvl_19/frame_13.png | Bin .../nsfw}/lvl_19/frame_14.png | Bin .../nsfw}/lvl_19/frame_15.png | Bin .../nsfw}/lvl_19/frame_16.png | Bin .../nsfw}/lvl_19/frame_17.png | Bin .../nsfw}/lvl_19/frame_18.png | Bin .../nsfw}/lvl_19/frame_19.png | Bin .../nsfw}/lvl_19/frame_2.png | Bin .../nsfw}/lvl_19/frame_20.png | Bin .../nsfw}/lvl_19/frame_21.png | Bin .../nsfw}/lvl_19/frame_3.png | Bin .../nsfw}/lvl_19/frame_4.png | Bin .../nsfw}/lvl_19/frame_5.png | Bin .../nsfw}/lvl_19/frame_6.png | Bin .../nsfw}/lvl_19/frame_7.png | Bin .../nsfw}/lvl_19/frame_8.png | Bin .../nsfw}/lvl_19/frame_9.png | Bin .../Anims => external/nsfw}/lvl_19/meta.txt | 0 .../Anims => external/nsfw}/lvl_2/frame_0.png | Bin .../Anims => external/nsfw}/lvl_2/frame_1.png | Bin .../nsfw}/lvl_2/frame_10.png | Bin .../nsfw}/lvl_2/frame_11.png | Bin .../nsfw}/lvl_2/frame_12.png | Bin .../nsfw}/lvl_2/frame_13.png | Bin .../nsfw}/lvl_2/frame_14.png | Bin .../Anims => external/nsfw}/lvl_2/frame_2.png | Bin .../Anims => external/nsfw}/lvl_2/frame_3.png | Bin .../Anims => external/nsfw}/lvl_2/frame_4.png | Bin .../Anims => external/nsfw}/lvl_2/frame_5.png | Bin .../Anims => external/nsfw}/lvl_2/frame_6.png | Bin .../Anims => external/nsfw}/lvl_2/frame_7.png | Bin .../Anims => external/nsfw}/lvl_2/frame_8.png | Bin .../Anims => external/nsfw}/lvl_2/frame_9.png | Bin .../Anims => external/nsfw}/lvl_2/meta.txt | 0 .../nsfw}/lvl_20/frame_0.png | Bin .../nsfw}/lvl_20/frame_1.png | Bin .../nsfw}/lvl_20/frame_10.png | Bin .../nsfw}/lvl_20/frame_11.png | Bin .../nsfw}/lvl_20/frame_12.png | Bin .../nsfw}/lvl_20/frame_13.png | Bin .../nsfw}/lvl_20/frame_2.png | Bin .../nsfw}/lvl_20/frame_3.png | Bin .../nsfw}/lvl_20/frame_4.png | Bin .../nsfw}/lvl_20/frame_5.png | Bin .../nsfw}/lvl_20/frame_6.png | Bin .../nsfw}/lvl_20/frame_7.png | Bin .../nsfw}/lvl_20/frame_8.png | Bin .../nsfw}/lvl_20/frame_9.png | Bin .../Anims => external/nsfw}/lvl_20/meta.txt | 0 .../nsfw}/lvl_21/frame_0.png | Bin .../nsfw}/lvl_21/frame_1.png | Bin .../nsfw}/lvl_21/frame_2.png | Bin .../nsfw}/lvl_21/frame_3.png | Bin .../nsfw}/lvl_21/frame_4.png | Bin .../nsfw}/lvl_21/frame_5.png | Bin .../Anims => external/nsfw}/lvl_21/meta.txt | 0 .../nsfw}/lvl_22/frame_0.png | Bin .../nsfw}/lvl_22/frame_1.png | Bin .../nsfw}/lvl_22/frame_10.png | Bin .../nsfw}/lvl_22/frame_11.png | Bin .../nsfw}/lvl_22/frame_12.png | Bin .../nsfw}/lvl_22/frame_13.png | Bin .../nsfw}/lvl_22/frame_14.png | Bin .../nsfw}/lvl_22/frame_15.png | Bin .../nsfw}/lvl_22/frame_16.png | Bin .../nsfw}/lvl_22/frame_17.png | Bin .../nsfw}/lvl_22/frame_18.png | Bin .../nsfw}/lvl_22/frame_19.png | Bin .../nsfw}/lvl_22/frame_2.png | Bin .../nsfw}/lvl_22/frame_20.png | Bin .../nsfw}/lvl_22/frame_21.png | Bin .../nsfw}/lvl_22/frame_22.png | Bin .../nsfw}/lvl_22/frame_23.png | Bin .../nsfw}/lvl_22/frame_24.png | Bin .../nsfw}/lvl_22/frame_25.png | Bin .../nsfw}/lvl_22/frame_26.png | Bin .../nsfw}/lvl_22/frame_27.png | Bin .../nsfw}/lvl_22/frame_28.png | Bin .../nsfw}/lvl_22/frame_29.png | Bin .../nsfw}/lvl_22/frame_3.png | Bin .../nsfw}/lvl_22/frame_30.png | Bin .../nsfw}/lvl_22/frame_31.png | Bin .../nsfw}/lvl_22/frame_32.png | Bin .../nsfw}/lvl_22/frame_33.png | Bin .../nsfw}/lvl_22/frame_34.png | Bin .../nsfw}/lvl_22/frame_35.png | Bin .../nsfw}/lvl_22/frame_36.png | Bin .../nsfw}/lvl_22/frame_37.png | Bin .../nsfw}/lvl_22/frame_38.png | Bin .../nsfw}/lvl_22/frame_39.png | Bin .../nsfw}/lvl_22/frame_4.png | Bin .../nsfw}/lvl_22/frame_40.png | Bin .../nsfw}/lvl_22/frame_41.png | Bin .../nsfw}/lvl_22/frame_42.png | Bin .../nsfw}/lvl_22/frame_43.png | Bin .../nsfw}/lvl_22/frame_44.png | Bin .../nsfw}/lvl_22/frame_45.png | Bin .../nsfw}/lvl_22/frame_46.png | Bin .../nsfw}/lvl_22/frame_47.png | Bin .../nsfw}/lvl_22/frame_48.png | Bin .../nsfw}/lvl_22/frame_49.png | Bin .../nsfw}/lvl_22/frame_5.png | Bin .../nsfw}/lvl_22/frame_50.png | Bin .../nsfw}/lvl_22/frame_51.png | Bin .../nsfw}/lvl_22/frame_52.png | Bin .../nsfw}/lvl_22/frame_53.png | Bin .../nsfw}/lvl_22/frame_54.png | Bin .../nsfw}/lvl_22/frame_55.png | Bin .../nsfw}/lvl_22/frame_56.png | Bin .../nsfw}/lvl_22/frame_57.png | Bin .../nsfw}/lvl_22/frame_58.png | Bin .../nsfw}/lvl_22/frame_59.png | Bin .../nsfw}/lvl_22/frame_6.png | Bin .../nsfw}/lvl_22/frame_7.png | Bin .../nsfw}/lvl_22/frame_8.png | Bin .../nsfw}/lvl_22/frame_9.png | Bin .../Anims => external/nsfw}/lvl_22/meta.txt | 0 .../nsfw}/lvl_23/frame_0.png | Bin .../nsfw}/lvl_23/frame_1.png | Bin .../nsfw}/lvl_23/frame_10.png | Bin .../nsfw}/lvl_23/frame_11.png | Bin .../nsfw}/lvl_23/frame_12.png | Bin .../nsfw}/lvl_23/frame_13.png | Bin .../nsfw}/lvl_23/frame_14.png | Bin .../nsfw}/lvl_23/frame_15.png | Bin .../nsfw}/lvl_23/frame_16.png | Bin .../nsfw}/lvl_23/frame_2.png | Bin .../nsfw}/lvl_23/frame_3.png | Bin .../nsfw}/lvl_23/frame_4.png | Bin .../nsfw}/lvl_23/frame_5.png | Bin .../nsfw}/lvl_23/frame_6.png | Bin .../nsfw}/lvl_23/frame_7.png | Bin .../nsfw}/lvl_23/frame_8.png | Bin .../nsfw}/lvl_23/frame_9.png | Bin .../Anims => external/nsfw}/lvl_23/meta.txt | 0 .../nsfw}/lvl_24/frame_0.png | Bin .../nsfw}/lvl_24/frame_1.png | Bin .../nsfw}/lvl_24/frame_10.png | Bin .../nsfw}/lvl_24/frame_11.png | Bin .../nsfw}/lvl_24/frame_12.png | Bin .../nsfw}/lvl_24/frame_13.png | Bin .../nsfw}/lvl_24/frame_14.png | Bin .../nsfw}/lvl_24/frame_15.png | Bin .../nsfw}/lvl_24/frame_16.png | Bin .../nsfw}/lvl_24/frame_17.png | Bin .../nsfw}/lvl_24/frame_18.png | Bin .../nsfw}/lvl_24/frame_19.png | Bin .../nsfw}/lvl_24/frame_2.png | Bin .../nsfw}/lvl_24/frame_20.png | Bin .../nsfw}/lvl_24/frame_21.png | Bin .../nsfw}/lvl_24/frame_22.png | Bin .../nsfw}/lvl_24/frame_23.png | Bin .../nsfw}/lvl_24/frame_24.png | Bin .../nsfw}/lvl_24/frame_25.png | Bin .../nsfw}/lvl_24/frame_26.png | Bin .../nsfw}/lvl_24/frame_27.png | Bin .../nsfw}/lvl_24/frame_28.png | Bin .../nsfw}/lvl_24/frame_29.png | Bin .../nsfw}/lvl_24/frame_3.png | Bin .../nsfw}/lvl_24/frame_4.png | Bin .../nsfw}/lvl_24/frame_5.png | Bin .../nsfw}/lvl_24/frame_6.png | Bin .../nsfw}/lvl_24/frame_7.png | Bin .../nsfw}/lvl_24/frame_8.png | Bin .../nsfw}/lvl_24/frame_9.png | Bin .../Anims => external/nsfw}/lvl_24/meta.txt | 0 .../nsfw}/lvl_25/frame_0.png | Bin .../nsfw}/lvl_25/frame_1.png | Bin .../nsfw}/lvl_25/frame_10.png | Bin .../nsfw}/lvl_25/frame_11.png | Bin .../nsfw}/lvl_25/frame_12.png | Bin .../nsfw}/lvl_25/frame_13.png | Bin .../nsfw}/lvl_25/frame_14.png | Bin .../nsfw}/lvl_25/frame_15.png | Bin .../nsfw}/lvl_25/frame_16.png | Bin .../nsfw}/lvl_25/frame_17.png | Bin .../nsfw}/lvl_25/frame_18.png | Bin .../nsfw}/lvl_25/frame_19.png | Bin .../nsfw}/lvl_25/frame_2.png | Bin .../nsfw}/lvl_25/frame_20.png | Bin .../nsfw}/lvl_25/frame_21.png | Bin .../nsfw}/lvl_25/frame_22.png | Bin .../nsfw}/lvl_25/frame_23.png | Bin .../nsfw}/lvl_25/frame_24.png | Bin .../nsfw}/lvl_25/frame_25.png | Bin .../nsfw}/lvl_25/frame_26.png | Bin .../nsfw}/lvl_25/frame_27.png | Bin .../nsfw}/lvl_25/frame_28.png | Bin .../nsfw}/lvl_25/frame_29.png | Bin .../nsfw}/lvl_25/frame_3.png | Bin .../nsfw}/lvl_25/frame_30.png | Bin .../nsfw}/lvl_25/frame_31.png | Bin .../nsfw}/lvl_25/frame_32.png | Bin .../nsfw}/lvl_25/frame_33.png | Bin .../nsfw}/lvl_25/frame_34.png | Bin .../nsfw}/lvl_25/frame_35.png | Bin .../nsfw}/lvl_25/frame_4.png | Bin .../nsfw}/lvl_25/frame_5.png | Bin .../nsfw}/lvl_25/frame_6.png | Bin .../nsfw}/lvl_25/frame_7.png | Bin .../nsfw}/lvl_25/frame_8.png | Bin .../nsfw}/lvl_25/frame_9.png | Bin .../Anims => external/nsfw}/lvl_25/meta.txt | 0 .../nsfw}/lvl_26/frame_0.png | Bin .../nsfw}/lvl_26/frame_1.png | Bin .../nsfw}/lvl_26/frame_10.png | Bin .../nsfw}/lvl_26/frame_11.png | Bin .../nsfw}/lvl_26/frame_2.png | Bin .../nsfw}/lvl_26/frame_3.png | Bin .../nsfw}/lvl_26/frame_4.png | Bin .../nsfw}/lvl_26/frame_5.png | Bin .../nsfw}/lvl_26/frame_6.png | Bin .../nsfw}/lvl_26/frame_7.png | Bin .../nsfw}/lvl_26/frame_8.png | Bin .../nsfw}/lvl_26/frame_9.png | Bin .../Anims => external/nsfw}/lvl_26/meta.txt | 0 .../nsfw}/lvl_27/frame_0.png | Bin .../nsfw}/lvl_27/frame_1.png | Bin .../nsfw}/lvl_27/frame_10.png | Bin .../nsfw}/lvl_27/frame_11.png | Bin .../nsfw}/lvl_27/frame_12.png | Bin .../nsfw}/lvl_27/frame_13.png | Bin .../nsfw}/lvl_27/frame_14.png | Bin .../nsfw}/lvl_27/frame_15.png | Bin .../nsfw}/lvl_27/frame_16.png | Bin .../nsfw}/lvl_27/frame_17.png | Bin .../nsfw}/lvl_27/frame_18.png | Bin .../nsfw}/lvl_27/frame_19.png | Bin .../nsfw}/lvl_27/frame_2.png | Bin .../nsfw}/lvl_27/frame_20.png | Bin .../nsfw}/lvl_27/frame_21.png | Bin .../nsfw}/lvl_27/frame_3.png | Bin .../nsfw}/lvl_27/frame_4.png | Bin .../nsfw}/lvl_27/frame_5.png | Bin .../nsfw}/lvl_27/frame_6.png | Bin .../nsfw}/lvl_27/frame_7.png | Bin .../nsfw}/lvl_27/frame_8.png | Bin .../nsfw}/lvl_27/frame_9.png | Bin .../Anims => external/nsfw}/lvl_27/meta.txt | 0 .../nsfw}/lvl_28/frame_0.png | Bin .../nsfw}/lvl_28/frame_1.png | Bin .../nsfw}/lvl_28/frame_2.png | Bin .../nsfw}/lvl_28/frame_3.png | Bin .../nsfw}/lvl_28/frame_4.png | Bin .../nsfw}/lvl_28/frame_5.png | Bin .../Anims => external/nsfw}/lvl_28/meta.txt | 0 .../nsfw}/lvl_29/frame_0.png | Bin .../nsfw}/lvl_29/frame_1.png | Bin .../nsfw}/lvl_29/frame_10.png | Bin .../nsfw}/lvl_29/frame_11.png | Bin .../nsfw}/lvl_29/frame_12.png | Bin .../nsfw}/lvl_29/frame_13.png | Bin .../nsfw}/lvl_29/frame_14.png | Bin .../nsfw}/lvl_29/frame_15.png | Bin .../nsfw}/lvl_29/frame_16.png | Bin .../nsfw}/lvl_29/frame_17.png | Bin .../nsfw}/lvl_29/frame_18.png | Bin .../nsfw}/lvl_29/frame_19.png | Bin .../nsfw}/lvl_29/frame_2.png | Bin .../nsfw}/lvl_29/frame_20.png | Bin .../nsfw}/lvl_29/frame_21.png | Bin .../nsfw}/lvl_29/frame_22.png | Bin .../nsfw}/lvl_29/frame_23.png | Bin .../nsfw}/lvl_29/frame_24.png | Bin .../nsfw}/lvl_29/frame_25.png | Bin .../nsfw}/lvl_29/frame_26.png | Bin .../nsfw}/lvl_29/frame_27.png | Bin .../nsfw}/lvl_29/frame_28.png | Bin .../nsfw}/lvl_29/frame_29.png | Bin .../nsfw}/lvl_29/frame_3.png | Bin .../nsfw}/lvl_29/frame_30.png | Bin .../nsfw}/lvl_29/frame_31.png | Bin .../nsfw}/lvl_29/frame_32.png | Bin .../nsfw}/lvl_29/frame_33.png | Bin .../nsfw}/lvl_29/frame_34.png | Bin .../nsfw}/lvl_29/frame_35.png | Bin .../nsfw}/lvl_29/frame_36.png | Bin .../nsfw}/lvl_29/frame_37.png | Bin .../nsfw}/lvl_29/frame_38.png | Bin .../nsfw}/lvl_29/frame_39.png | Bin .../nsfw}/lvl_29/frame_4.png | Bin .../nsfw}/lvl_29/frame_40.png | Bin .../nsfw}/lvl_29/frame_41.png | Bin .../nsfw}/lvl_29/frame_42.png | Bin .../nsfw}/lvl_29/frame_43.png | Bin .../nsfw}/lvl_29/frame_44.png | Bin .../nsfw}/lvl_29/frame_45.png | Bin .../nsfw}/lvl_29/frame_46.png | Bin .../nsfw}/lvl_29/frame_47.png | Bin .../nsfw}/lvl_29/frame_48.png | Bin .../nsfw}/lvl_29/frame_49.png | Bin .../nsfw}/lvl_29/frame_5.png | Bin .../nsfw}/lvl_29/frame_50.png | Bin .../nsfw}/lvl_29/frame_51.png | Bin .../nsfw}/lvl_29/frame_6.png | Bin .../nsfw}/lvl_29/frame_7.png | Bin .../nsfw}/lvl_29/frame_8.png | Bin .../nsfw}/lvl_29/frame_9.png | Bin .../Anims => external/nsfw}/lvl_29/meta.txt | 0 .../Anims => external/nsfw}/lvl_3/frame_0.png | Bin .../Anims => external/nsfw}/lvl_3/frame_1.png | Bin .../nsfw}/lvl_3/frame_10.png | Bin .../nsfw}/lvl_3/frame_11.png | Bin .../nsfw}/lvl_3/frame_12.png | Bin .../nsfw}/lvl_3/frame_13.png | Bin .../nsfw}/lvl_3/frame_14.png | Bin .../Anims => external/nsfw}/lvl_3/frame_2.png | Bin .../Anims => external/nsfw}/lvl_3/frame_3.png | Bin .../Anims => external/nsfw}/lvl_3/frame_4.png | Bin .../Anims => external/nsfw}/lvl_3/frame_5.png | Bin .../Anims => external/nsfw}/lvl_3/frame_6.png | Bin .../Anims => external/nsfw}/lvl_3/frame_7.png | Bin .../Anims => external/nsfw}/lvl_3/frame_8.png | Bin .../Anims => external/nsfw}/lvl_3/frame_9.png | Bin .../Anims => external/nsfw}/lvl_3/meta.txt | 0 .../nsfw}/lvl_30/frame_0.png | Bin .../nsfw}/lvl_30/frame_1.png | Bin .../nsfw}/lvl_30/frame_10.png | Bin .../nsfw}/lvl_30/frame_11.png | Bin .../nsfw}/lvl_30/frame_12.png | Bin .../nsfw}/lvl_30/frame_13.png | Bin .../nsfw}/lvl_30/frame_14.png | Bin .../nsfw}/lvl_30/frame_15.png | Bin .../nsfw}/lvl_30/frame_16.png | Bin .../nsfw}/lvl_30/frame_17.png | Bin .../nsfw}/lvl_30/frame_18.png | Bin .../nsfw}/lvl_30/frame_19.png | Bin .../nsfw}/lvl_30/frame_2.png | Bin .../nsfw}/lvl_30/frame_20.png | Bin .../nsfw}/lvl_30/frame_21.png | Bin .../nsfw}/lvl_30/frame_22.png | Bin .../nsfw}/lvl_30/frame_23.png | Bin .../nsfw}/lvl_30/frame_24.png | Bin .../nsfw}/lvl_30/frame_25.png | Bin .../nsfw}/lvl_30/frame_26.png | Bin .../nsfw}/lvl_30/frame_27.png | Bin .../nsfw}/lvl_30/frame_28.png | Bin .../nsfw}/lvl_30/frame_29.png | Bin .../nsfw}/lvl_30/frame_3.png | Bin .../nsfw}/lvl_30/frame_30.png | Bin .../nsfw}/lvl_30/frame_31.png | Bin .../nsfw}/lvl_30/frame_32.png | Bin .../nsfw}/lvl_30/frame_33.png | Bin .../nsfw}/lvl_30/frame_34.png | Bin .../nsfw}/lvl_30/frame_35.png | Bin .../nsfw}/lvl_30/frame_36.png | Bin .../nsfw}/lvl_30/frame_37.png | Bin .../nsfw}/lvl_30/frame_38.png | Bin .../nsfw}/lvl_30/frame_39.png | Bin .../nsfw}/lvl_30/frame_4.png | Bin .../nsfw}/lvl_30/frame_40.png | Bin .../nsfw}/lvl_30/frame_41.png | Bin .../nsfw}/lvl_30/frame_42.png | Bin .../nsfw}/lvl_30/frame_43.png | Bin .../nsfw}/lvl_30/frame_44.png | Bin .../nsfw}/lvl_30/frame_45.png | Bin .../nsfw}/lvl_30/frame_46.png | Bin .../nsfw}/lvl_30/frame_47.png | Bin .../nsfw}/lvl_30/frame_48.png | Bin .../nsfw}/lvl_30/frame_49.png | Bin .../nsfw}/lvl_30/frame_5.png | Bin .../nsfw}/lvl_30/frame_6.png | Bin .../nsfw}/lvl_30/frame_7.png | Bin .../nsfw}/lvl_30/frame_8.png | Bin .../nsfw}/lvl_30/frame_9.png | Bin .../Anims => external/nsfw}/lvl_30/meta.txt | 0 .../Anims => external/nsfw}/lvl_4/frame_0.png | Bin .../Anims => external/nsfw}/lvl_4/frame_1.png | Bin .../nsfw}/lvl_4/frame_10.png | Bin .../nsfw}/lvl_4/frame_11.png | Bin .../nsfw}/lvl_4/frame_12.png | Bin .../nsfw}/lvl_4/frame_13.png | Bin .../nsfw}/lvl_4/frame_14.png | Bin .../nsfw}/lvl_4/frame_15.png | Bin .../nsfw}/lvl_4/frame_16.png | Bin .../nsfw}/lvl_4/frame_17.png | Bin .../nsfw}/lvl_4/frame_18.png | Bin .../nsfw}/lvl_4/frame_19.png | Bin .../Anims => external/nsfw}/lvl_4/frame_2.png | Bin .../Anims => external/nsfw}/lvl_4/frame_3.png | Bin .../Anims => external/nsfw}/lvl_4/frame_4.png | Bin .../Anims => external/nsfw}/lvl_4/frame_5.png | Bin .../Anims => external/nsfw}/lvl_4/frame_6.png | Bin .../Anims => external/nsfw}/lvl_4/frame_7.png | Bin .../Anims => external/nsfw}/lvl_4/frame_8.png | Bin .../Anims => external/nsfw}/lvl_4/frame_9.png | Bin .../Anims => external/nsfw}/lvl_4/meta.txt | 0 .../Anims => external/nsfw}/lvl_5/frame_0.png | Bin .../Anims => external/nsfw}/lvl_5/frame_1.png | Bin .../nsfw}/lvl_5/frame_10.png | Bin .../nsfw}/lvl_5/frame_11.png | Bin .../nsfw}/lvl_5/frame_12.png | Bin .../nsfw}/lvl_5/frame_13.png | Bin .../nsfw}/lvl_5/frame_14.png | Bin .../nsfw}/lvl_5/frame_15.png | Bin .../nsfw}/lvl_5/frame_16.png | Bin .../nsfw}/lvl_5/frame_17.png | Bin .../nsfw}/lvl_5/frame_18.png | Bin .../nsfw}/lvl_5/frame_19.png | Bin .../Anims => external/nsfw}/lvl_5/frame_2.png | Bin .../nsfw}/lvl_5/frame_20.png | Bin .../nsfw}/lvl_5/frame_21.png | Bin .../nsfw}/lvl_5/frame_22.png | Bin .../nsfw}/lvl_5/frame_23.png | Bin .../nsfw}/lvl_5/frame_24.png | Bin .../nsfw}/lvl_5/frame_25.png | Bin .../nsfw}/lvl_5/frame_26.png | Bin .../nsfw}/lvl_5/frame_27.png | Bin .../Anims => external/nsfw}/lvl_5/frame_3.png | Bin .../Anims => external/nsfw}/lvl_5/frame_4.png | Bin .../Anims => external/nsfw}/lvl_5/frame_5.png | Bin .../Anims => external/nsfw}/lvl_5/frame_6.png | Bin .../Anims => external/nsfw}/lvl_5/frame_7.png | Bin .../Anims => external/nsfw}/lvl_5/frame_8.png | Bin .../Anims => external/nsfw}/lvl_5/frame_9.png | Bin .../Anims => external/nsfw}/lvl_5/meta.txt | 0 .../Anims => external/nsfw}/lvl_6/frame_0.png | Bin .../Anims => external/nsfw}/lvl_6/frame_1.png | Bin .../Anims => external/nsfw}/lvl_6/frame_2.png | Bin .../Anims => external/nsfw}/lvl_6/frame_3.png | Bin .../Anims => external/nsfw}/lvl_6/frame_4.png | Bin .../Anims => external/nsfw}/lvl_6/frame_5.png | Bin .../Anims => external/nsfw}/lvl_6/frame_6.png | Bin .../Anims => external/nsfw}/lvl_6/meta.txt | 0 .../Anims => external/nsfw}/lvl_7/frame_0.png | Bin .../Anims => external/nsfw}/lvl_7/frame_1.png | Bin .../nsfw}/lvl_7/frame_10.png | Bin .../nsfw}/lvl_7/frame_11.png | Bin .../nsfw}/lvl_7/frame_12.png | Bin .../nsfw}/lvl_7/frame_13.png | Bin .../Anims => external/nsfw}/lvl_7/frame_2.png | Bin .../Anims => external/nsfw}/lvl_7/frame_3.png | Bin .../Anims => external/nsfw}/lvl_7/frame_4.png | Bin .../Anims => external/nsfw}/lvl_7/frame_5.png | Bin .../Anims => external/nsfw}/lvl_7/frame_6.png | Bin .../Anims => external/nsfw}/lvl_7/frame_7.png | Bin .../Anims => external/nsfw}/lvl_7/frame_8.png | Bin .../Anims => external/nsfw}/lvl_7/frame_9.png | Bin .../Anims => external/nsfw}/lvl_7/meta.txt | 0 .../Anims => external/nsfw}/lvl_8/frame_0.png | Bin .../Anims => external/nsfw}/lvl_8/frame_1.png | Bin .../Anims => external/nsfw}/lvl_8/frame_2.png | Bin .../Anims => external/nsfw}/lvl_8/frame_3.png | Bin .../Anims => external/nsfw}/lvl_8/frame_4.png | Bin .../Anims => external/nsfw}/lvl_8/frame_5.png | Bin .../Anims => external/nsfw}/lvl_8/meta.txt | 0 .../Anims => external/nsfw}/lvl_9/frame_0.png | Bin .../Anims => external/nsfw}/lvl_9/frame_1.png | Bin .../Anims => external/nsfw}/lvl_9/frame_2.png | Bin .../Anims => external/nsfw}/lvl_9/frame_3.png | Bin .../Anims => external/nsfw}/lvl_9/frame_4.png | Bin .../Anims => external/nsfw}/lvl_9/frame_5.png | Bin .../Anims => external/nsfw}/lvl_9/frame_6.png | Bin .../Anims => external/nsfw}/lvl_9/frame_7.png | Bin .../Anims => external/nsfw}/lvl_9/meta.txt | 0 .../NSFW/Anims => external/nsfw}/manifest.txt | 0 .../{ => sfw}/L1_Boxing_128x64/frame_0.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_1.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_2.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_3.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_4.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_5.png | Bin .../{ => sfw}/L1_Boxing_128x64/frame_6.png | Bin .../{ => sfw}/L1_Boxing_128x64/meta.txt | 0 .../{ => sfw}/L1_Cry_128x64/frame_0.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_1.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_2.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_3.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_4.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_5.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_6.png | Bin .../{ => sfw}/L1_Cry_128x64/frame_7.png | Bin .../external/{ => sfw}/L1_Cry_128x64/meta.txt | 0 .../{ => sfw}/L1_Furippa1_128x64/frame_0.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_1.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_10.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_11.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_12.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_13.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_14.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_15.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_16.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_17.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_18.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_2.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_3.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_4.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_5.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_6.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_7.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_8.png | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_9.png | Bin .../{ => sfw}/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{ => sfw}/L1_Laptop_128x51/frame_0.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_1.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_2.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_3.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_4.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_5.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_6.png | Bin .../{ => sfw}/L1_Laptop_128x51/frame_7.png | Bin .../{ => sfw}/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{ => sfw}/L1_Leaving_sad_128x64/meta.txt | 0 .../{ => sfw}/L1_Mad_fist_128x64/frame_0.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_1.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_10.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_11.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_12.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_13.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_2.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_3.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_4.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_5.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_6.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_7.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_8.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_9.png | Bin .../{ => sfw}/L1_Mad_fist_128x64/meta.txt | 0 .../{ => sfw}/L1_Mods_128x64/frame_0.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_1.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_10.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_11.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_12.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_13.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_14.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_15.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_16.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_17.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_18.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_19.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_2.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_20.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_21.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_22.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_23.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_24.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_25.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_26.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_27.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_28.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_29.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_3.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_30.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_31.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_32.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_33.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_34.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_35.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_36.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_37.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_38.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_39.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_4.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_40.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_5.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_6.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_7.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_8.png | Bin .../{ => sfw}/L1_Mods_128x64/frame_9.png | Bin .../{ => sfw}/L1_Mods_128x64/meta.txt | 0 .../{ => sfw}/L1_Painting_128x64/frame_0.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_1.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_10.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_11.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_2.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_3.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_4.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_5.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_6.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_7.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_8.png | Bin .../{ => sfw}/L1_Painting_128x64/frame_9.png | Bin .../{ => sfw}/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{ => sfw}/L1_Read_books_128x64/meta.txt | 0 .../{ => sfw}/L1_Recording_128x51/frame_0.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_2.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_3.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_4.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_5.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_6.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_7.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_8.png | Bin .../{ => sfw}/L1_Recording_128x51/frame_9.png | Bin .../{ => sfw}/L1_Recording_128x51/meta.txt | 0 .../{ => sfw}/L1_Sleep_128x64/frame_0.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_1.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_2.png | Bin .../{ => sfw}/L1_Sleep_128x64/frame_3.png | Bin .../{ => sfw}/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{ => sfw}/L1_Sleigh_ride_128x64/meta.txt | 0 .../{ => sfw}/L1_Waves_128x50/frame_0.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_1.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_2.png | Bin .../{ => sfw}/L1_Waves_128x50/frame_3.png | Bin .../{ => sfw}/L1_Waves_128x50/meta.txt | 0 .../{ => sfw}/L2_Furippa2_128x64/frame_0.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_1.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_10.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_11.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_12.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_13.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_14.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_15.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_16.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_17.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_18.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_2.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_3.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_4.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_5.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_6.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_7.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_8.png | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_9.png | Bin .../{ => sfw}/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{ => sfw}/L2_Hacking_pc_128x64/meta.txt | 0 .../{ => sfw}/L2_Soldering_128x64/frame_0.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_2.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_3.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_4.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_5.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_6.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_7.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_8.png | Bin .../{ => sfw}/L2_Soldering_128x64/frame_9.png | Bin .../{ => sfw}/L2_Soldering_128x64/meta.txt | 0 .../{ => sfw}/L2_Wake_up_128x64/frame_0.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_1.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_10.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_11.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_12.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_13.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_14.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_15.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_16.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_17.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_18.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_19.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_2.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_20.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_3.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_4.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_5.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_6.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_7.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_8.png | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_9.png | Bin .../{ => sfw}/L2_Wake_up_128x64/meta.txt | 0 .../{ => sfw}/L3_Furippa3_128x64/frame_0.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_1.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_10.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_11.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_12.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_13.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_14.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_15.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_16.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_17.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_18.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_2.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_3.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_4.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_5.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_6.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_7.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_8.png | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_9.png | Bin .../{ => sfw}/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{ => sfw}/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{ => sfw}/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{ => sfw}/manifest.txt | 0 .../Animations/Levelup1_128x64}/frame_00.png | Bin .../Animations/Levelup1_128x64}/frame_01.png | Bin .../Animations/Levelup1_128x64}/frame_02.png | Bin .../Animations/Levelup1_128x64}/frame_03.png | Bin .../Animations/Levelup1_128x64}/frame_04.png | Bin .../Animations/Levelup1_128x64}/frame_05.png | Bin .../Animations/Levelup1_128x64}/frame_06.png | Bin .../Animations/Levelup1_128x64}/frame_07.png | Bin .../Animations/Levelup1_128x64}/frame_08.png | Bin .../Animations/Levelup1_128x64}/frame_09.png | Bin .../Animations/Levelup1_128x64}/frame_10.png | Bin .../Animations/Levelup1_128x64}/frame_11.png | Bin .../Animations/Levelup1_128x64}/frame_12.png | Bin .../Animations/Levelup1_128x64}/frame_13.png | Bin .../Animations/Levelup1_128x64}/frame_14.png | Bin .../Animations/Levelup1_128x64}/frame_15.png | Bin .../Animations/Levelup1_128x64}/frame_16.png | Bin .../Animations/Levelup1_128x64}/frame_17.png | Bin .../Animations/Levelup1_128x64}/frame_18.png | Bin .../Animations/Levelup1_128x64}/frame_19.png | Bin .../Animations/Levelup1_128x64}/frame_20.png | Bin .../Animations/Levelup1_128x64}/frame_21.png | Bin .../Animations/Levelup1_128x64}/frame_22.png | Bin .../Animations/Levelup1_128x64}/frame_23.png | Bin .../Animations/Levelup1_128x64}/frame_24.png | Bin .../Animations/Levelup1_128x64}/frame_25.png | Bin .../Animations/Levelup1_128x64}/frame_26.png | Bin .../Animations/Levelup1_128x64}/frame_27.png | Bin .../Animations/Levelup1_128x64}/frame_28.png | Bin .../Animations/Levelup1_128x64}/frame_29.png | Bin .../Animations/Levelup1_128x64}/frame_30.png | Bin .../Animations/Levelup1_128x64}/frame_31.png | Bin .../Animations/Levelup1_128x64}/frame_rate | 0 .../Levelup1_128x64_sfw/frame_00.png | Bin 0 -> 1326 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 0 -> 1597 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 0 -> 1754 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 0 -> 1828 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 0 -> 1686 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 0 -> 1672 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 0 -> 1659 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 0 -> 1540 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 0 -> 1557 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 0 -> 1551 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 0 -> 1604 bytes .../frame_rate | 0 .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../Animations/Levelup2_128x64_sfw/frame_rate | 1 + assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2307 -> 2610 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 0 -> 2307 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 1416 -> 3376 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 0 -> 1416 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 1177 -> 5390 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 0 -> 1177 bytes assets/icons/Interface/SmallArrowDown_4x7.png | Bin 0 -> 8340 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 0 -> 8552 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 0 -> 3692 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 0 -> 981 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 0 -> 3740 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 1541 -> 4224 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 0 -> 1541 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 0 -> 3748 bytes .../Passport/flipper.png} | Bin assets/icons/Passport/passport_DB.png | Bin 750 -> 852 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 0 -> 750 bytes .../Passport/passport_bad1_46x49_sfw.png | Bin 0 -> 1237 bytes ..._46x49.png => passport_bad2_46x49_sfw.png} | Bin .../Passport/passport_bad3_46x49_sfw.png | Bin 0 -> 1304 bytes .../Passport/passport_happy1_46x49_sfw.png | Bin 0 -> 1296 bytes ...6x49.png => passport_happy2_46x49_sfw.png} | Bin .../Passport/passport_happy3_46x49_sfw.png | Bin 0 -> 1348 bytes .../Passport/passport_okay1_46x49_sfw.png | Bin 0 -> 1244 bytes ...46x49.png => passport_okay2_46x49_sfw.png} | Bin .../Passport/passport_okay3_46x49_sfw.png | Bin 0 -> 1304 bytes .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 1421 -> 4862 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 0 -> 1421 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 1418 -> 4882 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 0 -> 1418 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 2681 -> 4466 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 0 -> 2681 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3898 -> 3798 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 0 -> 3898 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 0 -> 3611 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 0 -> 1690 bytes ...g_9x10.png => Charging-lightning_9x10.png} | Bin ...0.png => Charging-lightning_mask_9x10.png} | Bin assets/icons/StatusBar/GameMode_11x8.png | Bin 0 -> 3610 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 1690 -> 4092 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 0 -> 1690 bytes assets/icons/U2F/Auth_62x31.png | Bin 3761 -> 1864 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 0 -> 3761 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 3767 -> 1895 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 0 -> 3767 bytes assets/icons/U2F/Connected_62x31.png | Bin 3765 -> 1874 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 0 -> 3765 bytes assets/icons/U2F/Error_62x31.png | Bin 3751 -> 1863 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 0 -> 3751 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 2504 -> 6876 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 0 -> 2504 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 2459 -> 5422 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 0 -> 2459 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 2023 -> 5122 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 0 -> 2023 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 2157 -> 4719 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 0 -> 2157 bytes .../nsfw}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_10.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_11.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_12.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_13.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_14.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_15.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_16.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_17.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_18.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_19.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_20.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_21.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../nsfw}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../Anims => dolphin/nsfw}/lvl_1/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_1/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_10/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_10/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_11/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_11/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_12/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_12/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_13/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_13/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_14/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_14/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_15/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_15/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_16/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_16/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_17/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_17/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_18/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_18/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_19/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_19/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_2/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_2/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_20/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_20/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_21/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_21/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_22/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_50.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_51.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_52.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_53.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_54.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_55.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_56.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_57.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_58.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_59.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_22/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_23/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_23/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_24/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_24/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_25/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_25/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_26/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_26/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_27/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_27/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_28/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_28/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_29/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_50.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_51.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_29/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_3/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_3/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_30/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_28.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_29.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_30.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_31.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_32.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_33.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_34.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_35.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_36.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_37.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_38.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_39.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_40.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_41.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_42.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_43.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_44.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_45.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_46.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_47.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_48.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_49.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_30/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_4/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_4/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_5/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_14.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_15.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_16.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_17.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_18.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_19.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_20.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_21.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_22.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_23.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_24.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_25.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_26.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_27.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_5/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_6/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_6/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_7/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_10.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_11.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_12.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_13.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_8.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/frame_9.bm | Bin .../Anims => dolphin/nsfw}/lvl_7/meta.txt | 2 +- .../Anims => dolphin/nsfw}/lvl_8/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_8/meta.txt | 0 .../Anims => dolphin/nsfw}/lvl_9/frame_0.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_1.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_2.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_3.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_4.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_5.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_6.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/frame_7.bm | Bin .../Anims => dolphin/nsfw}/lvl_9/meta.txt | 0 .../NSFW/Anims => dolphin/nsfw}/manifest.txt | 0 .../{ => sfw}/L1_Boxing_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Boxing_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Boxing_128x64/meta.txt | 0 .../{ => sfw}/L1_Cry_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{ => sfw}/L1_Cry_128x64/meta.txt | 0 .../{ => sfw}/L1_Furippa1_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_14.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_15.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_16.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_17.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_18.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{ => sfw}/L1_Laptop_128x51/frame_0.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_1.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_2.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_3.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_4.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_5.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_6.bm | Bin .../{ => sfw}/L1_Laptop_128x51/frame_7.bm | Bin .../{ => sfw}/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Leaving_sad_128x64/meta.txt | 0 .../{ => sfw}/L1_Mad_fist_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Mad_fist_128x64/meta.txt | 0 .../{ => sfw}/L1_Mods_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_12.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_13.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_14.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_15.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_16.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_17.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_18.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_19.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_20.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_21.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_22.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_23.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_24.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_25.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_26.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_27.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_28.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_29.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_30.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_31.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_32.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_33.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_34.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_35.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_36.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_37.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_38.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_39.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_40.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{ => sfw}/L1_Mods_128x64/meta.txt | 0 .../{ => sfw}/L1_Painting_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_10.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_11.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Painting_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Painting_128x64/meta.txt | 0 .../{ => sfw}/L1_Read_books_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_4.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_5.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_6.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_7.bm | Bin .../{ => sfw}/L1_Read_books_128x64/frame_8.bm | Bin .../{ => sfw}/L1_Read_books_128x64/meta.txt | 0 .../{ => sfw}/L1_Recording_128x51/frame_0.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_1.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_10.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_11.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_2.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_3.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_4.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_5.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_6.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_7.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_8.bm | Bin .../{ => sfw}/L1_Recording_128x51/frame_9.bm | Bin .../{ => sfw}/L1_Recording_128x51/meta.txt | 0 .../{ => sfw}/L1_Sleep_128x64/frame_0.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_1.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_2.bm | Bin .../{ => sfw}/L1_Sleep_128x64/frame_3.bm | Bin .../{ => sfw}/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{ => sfw}/L1_Sleigh_ride_128x64/meta.txt | 0 .../{ => sfw}/L1_Waves_128x50/frame_0.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_1.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_2.bm | Bin .../{ => sfw}/L1_Waves_128x50/frame_3.bm | Bin .../{ => sfw}/L1_Waves_128x50/meta.txt | 0 .../{ => sfw}/L2_Furippa2_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_11.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_12.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_13.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_14.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_15.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_16.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_17.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_18.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Furippa2_128x64/meta.txt | 0 .../{ => sfw}/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Hacking_pc_128x64/meta.txt | 0 .../{ => sfw}/L2_Soldering_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Soldering_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Soldering_128x64/meta.txt | 0 .../{ => sfw}/L2_Wake_up_128x64/frame_0.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_1.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_10.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_11.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_12.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_13.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_14.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_15.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_16.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_17.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_18.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_19.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_2.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_20.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_3.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_4.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_5.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_6.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_7.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_8.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/frame_9.bm | Bin .../{ => sfw}/L2_Wake_up_128x64/meta.txt | 0 .../{ => sfw}/L3_Furippa3_128x64/frame_0.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_1.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_10.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_11.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_12.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_13.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_14.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_15.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_16.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_17.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_18.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_2.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_3.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_4.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_5.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_6.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_7.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_8.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/frame_9.bm | Bin .../{ => sfw}/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{ => sfw}/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{ => sfw}/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{ => sfw}/manifest.txt | 0 .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 472 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 462 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 487 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 483 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 483 -> 0 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 488 -> 0 bytes .../Animations/Levelup_128x64/frame_00.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_01.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_02.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_03.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_04.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_05.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_06.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_07.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_08.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_09.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_10.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_11.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_12.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_13.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_14.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_15.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_16.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_17.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_18.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_19.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_20.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_21.bm | Bin 217 -> 0 bytes .../Animations/Levelup_128x64/frame_22.bm | Bin 219 -> 0 bytes .../Animations/Levelup_128x64/frame_23.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_24.bm | Bin 221 -> 0 bytes .../Animations/Levelup_128x64/frame_25.bm | Bin 223 -> 0 bytes .../Animations/Levelup_128x64/frame_26.bm | Bin 225 -> 0 bytes .../Animations/Levelup_128x64/frame_27.bm | Bin 227 -> 0 bytes .../Animations/Levelup_128x64/frame_28.bm | Bin 228 -> 0 bytes .../Animations/Levelup_128x64/frame_29.bm | Bin 222 -> 0 bytes .../Animations/Levelup_128x64/frame_30.bm | Bin 224 -> 0 bytes .../Animations/Levelup_128x64/frame_31.bm | Bin 518 -> 0 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 16 -> 0 bytes .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 480 -> 0 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 325 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 513 -> 0 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 375 -> 0 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 286 -> 0 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 297 -> 0 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 297 -> 0 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 297 -> 0 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 525 -> 0 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 525 -> 0 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 502 -> 0 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 352 -> 0 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 458 -> 0 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 257 -> 0 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 257 -> 0 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 684 -> 0 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 609 -> 0 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 481 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 482 -> 0 bytes scripts/asset_packer.py | 70 ++--- scripts/assets.py | 35 ++- 2348 files changed, 298 insertions(+), 216 deletions(-) delete mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png delete mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_1/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_10/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_11/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_12/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_13/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_14/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_15/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_16/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_17/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_18/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_19/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_2/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_20/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_21/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_50.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_51.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_52.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_53.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_54.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_55.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_56.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_57.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_58.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_59.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_22/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_23/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_24/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_25/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_26/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_27/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_28/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_50.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_51.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_29/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_3/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_28.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_29.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_30.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_31.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_32.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_33.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_34.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_35.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_36.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_37.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_38.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_39.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_40.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_41.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_42.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_43.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_44.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_45.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_46.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_47.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_48.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_49.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_30/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_4/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_14.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_15.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_16.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_17.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_18.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_19.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_20.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_21.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_22.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_23.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_24.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_25.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_26.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_27.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_5/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_6/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_10.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_11.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_12.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_13.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_8.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/frame_9.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_7/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_8/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_0.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_1.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_2.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_3.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_4.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_5.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_6.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/frame_7.png (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/lvl_9/meta.txt (100%) rename assets/dolphin/{custom/NSFW/Anims => external/nsfw}/manifest.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{ => sfw}/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{ => sfw}/manifest.txt (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_00.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_01.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_02.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_03.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_04.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_05.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_06.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_07.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_08.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_09.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_10.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_11.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_12.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_13.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_14.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_15.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_16.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_17.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_18.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_19.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_20.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_21.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_22.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_23.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_24.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_25.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_26.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_27.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_28.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_29.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_30.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_31.png (100%) rename assets/{dolphin/custom/NSFW/Icons/Animations/Levelup_128x64 => icons/Animations/Levelup1_128x64}/frame_rate (100%) create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png create mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png rename assets/icons/Animations/{Levelup_128x64 => Levelup1_128x64_sfw}/frame_rate (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_00.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_01.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_02.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_03.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_04.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_05.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_06.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_07.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_08.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_09.png (100%) rename assets/icons/Animations/{Levelup_128x64 => Levelup2_128x64_sfw}/frame_10.png (100%) create mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate create mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png create mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png create mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png create mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png create mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png create mode 100644 assets/icons/NFC/ArrowC_1_36x36.png create mode 100644 assets/icons/NFC/Detailed_chip_17x13.png create mode 100644 assets/icons/NFC/Medium-chip-22x21.png create mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png create mode 100644 assets/icons/NFC/Tap_reader_36x38.png rename assets/{dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png => icons/Passport/flipper.png} (100%) create mode 100644 assets/icons/Passport/passport_DB_sfw.png create mode 100644 assets/icons/Passport/passport_bad1_46x49_sfw.png rename assets/icons/Passport/{passport_bad_46x49.png => passport_bad2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png create mode 100644 assets/icons/Passport/passport_happy1_46x49_sfw.png rename assets/icons/Passport/{passport_happy_46x49.png => passport_happy2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png create mode 100644 assets/icons/Passport/passport_okay1_46x49_sfw.png rename assets/icons/Passport/{passport_okay_46x49.png => passport_okay2_46x49_sfw.png} (100%) create mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png create mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png create mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png create mode 100644 assets/icons/StatusBar/Alert_9x8.png create mode 100644 assets/icons/StatusBar/Attention_5x8.png rename assets/icons/StatusBar/{Charging_lightning_9x10.png => Charging-lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging_lightning_mask_9x10.png => Charging-lightning_mask_9x10.png} (100%) create mode 100644 assets/icons/StatusBar/GameMode_11x8.png create mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png create mode 100644 assets/icons/U2F/Auth_62x31_sfw.png create mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png create mode 100644 assets/icons/U2F/Connected_62x31_sfw.png create mode 100644 assets/icons/U2F/Error_62x31_sfw.png create mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png create mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png create mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png create mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin_custom/NSFW/Anims => dolphin/nsfw}/manifest.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{ => sfw}/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{ => sfw}/manifest.txt (100%) delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx delete mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index 025246faa..bf7addba9 100644 --- a/.gitignore +++ b/.gitignore @@ -74,9 +74,7 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* -!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* -!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9cb66a5e5..3163b8cff 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,14 +457,10 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - for(int i = 0; i < 2; i++) { - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; - } - if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; - break; + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 43a9a651a..5b13e98da 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,7 +17,6 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), - [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -45,7 +44,6 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, - [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 26ed17d75..dce753fde 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,7 +19,6 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", - [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index 6e6582405..a525a7db6 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,7 +31,6 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, - ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index d32eee382..2c707bbf1 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 51bed3835..ad889cd1c 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define MAX_NAME_LEN 64 @@ -28,6 +28,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -48,7 +49,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -67,7 +68,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -77,7 +78,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(XTREME_ASSETS()->is_nsfw) { + if(xtreme_settings->nsfw_mode) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 35a6ce1d9..162faf2f1 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fc1c5c4fa..eecd32702 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index cea02bd00..caec3cbf6 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,3 +10,4 @@ App( fap_category="Misc", order=81, ) + diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 750373342..27e73a0ce 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) +) \ No newline at end of file diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 4d48d7bb5..8d87a4a62 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 0808a3618..5239d72d5 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define TAG "AnimationManager" @@ -569,6 +569,9 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); + Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); + DolphinStats stats = dolphin_stats(dolphin); + furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -577,8 +580,19 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - one_shot_view_start_animation( - animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); + if(XTREME_SETTINGS()->nsfw_mode) { + one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); + } else { + if(stats.level <= 20) { + one_shot_view_start_animation( + animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); + } else if(stats.level >= 21) { + one_shot_view_start_animation( + animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); + } else { + furi_assert(0); + } + } } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 4263dc0a4..a80a958a3 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom manifest selected"); + FURI_LOG_I(TAG, "Custom Manifest selected"); } else { use_asset_pack = false; } @@ -48,8 +48,14 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); + if(xtreme_settings->nsfw_mode) { + furi_string_cat_str(anim_dir, "/nsfw"); + FURI_LOG_I(TAG, "NSFW Manifest selected"); + } else { + furi_string_cat_str(anim_dir, "/sfw"); + FURI_LOG_I(TAG, "SFW Manifest selected"); + } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); - FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b5b73668b..7840cd00a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,8 +17,6 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" -#include "../../settings/xtreme_settings/xtreme_assets.h" - static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -307,9 +305,6 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); - // TODO: find a (working) way to run this at startup without hooking desktop - XTREME_ASSETS_LOAD(); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index c2149253c..e4f5e788f 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "../../../settings/xtreme_settings/xtreme_settings.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 450c5af23..f0430de5d 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index 6fd26138b..ecab8c333 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_ASSETS()->is_nsfw) { + if(XTREME_SETTINGS()->nsfw_mode) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index ba5e65ca0..3323cf1df 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,6 +3,15 @@ #include #include +static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { + XtremeSettingsApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); + XTREME_SETTINGS()->nsfw_mode = value; + app->settings_changed = true; + app->assets_changed = true; +} + static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -163,6 +172,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); + item = variable_item_list_add( + var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); + variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); + item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 13014b8d1..0f6ab998d 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,50 +2,175 @@ #include "assets_icons.h" #include -#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" - XtremeAssets* xtreme_assets = NULL; -void anim(const Icon** replace, const char* name, FuriString* path, File* file) { - do { - furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) - break; - int32_t width, height, frame_rate, frame_count; - storage_file_read(file, &width, 4); - storage_file_read(file, &height, 4); - storage_file_read(file, &frame_rate, 4); - storage_file_read(file, &frame_count, 4); - storage_file_close(file); - - Icon* icon = malloc(sizeof(Icon)); - FURI_CONST_ASSIGN(icon->width, width); - FURI_CONST_ASSIGN(icon->height, height); - FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); - FURI_CONST_ASSIGN(icon->frame_count, frame_count); - icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); - const char* pack = XTREME_SETTINGS()->asset_pack; - - bool ok = true; - for(int i = 0; ok && i < icon->frame_count; ++i) { - ok = false; - furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) - break; - - uint64_t size = storage_file_size(file); - FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); - if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; - storage_file_close(file); - } - if(!ok) break; - - *replace = icon; - } while(false); +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); + } + return xtreme_assets; } -void icon(const Icon** replace, const char* name, FuriString* path, File* file) { - furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); +void XTREME_ASSETS_LOAD() { + if(xtreme_assets != NULL) return; + + xtreme_assets = malloc(sizeof(XtremeAssets)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + + if(xtreme_settings->nsfw_mode) { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_flipper; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_flipper; + xtreme_assets->I_passport_okay_46x49 = &I_flipper; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; + } else { + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = + &I_iButtonDolphinVerySuccess_108x52_sfw; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; + xtreme_assets->I_passport_DB = &I_passport_DB_sfw; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; + xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; + } + + if(xtreme_settings->asset_pack[0] == '\0') return; + FileInfo info; + FuriString* path = furi_string_alloc(); + const char* pack = xtreme_settings->asset_pack; + furi_string_printf(path, PACKS_DIR "/%s", pack); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { + File* file = storage_file_alloc(storage); + + swap_bmx_icon( + &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinCommon_56x48, + pack, + "Dolphin/DolphinCommon_56x48.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinMafia_115x62, + pack, + "iButton/DolphinMafia_115x62.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, + pack, + "iButton/iButtonDolphinVerySuccess_108x52.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_DolphinReadingSuccess_59x63, + pack, + "Infrared/DolphinReadingSuccess_59x63.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_NFC_dolphin_emulation_47x61, + pack, + "NFC/NFC_dolphin_emulation_47x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_bad_46x49, + pack, + "Passport/passport_bad_46x49.bmx", + path, + file); + swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_passport_happy_46x49, + pack, + "Passport/passport_happy_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_passport_okay_46x49, + pack, + "Passport/passport_okay_46x49.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinReceive_97x61, + pack, + "RFID/RFIDDolphinReceive_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSend_97x61, + pack, + "RFID/RFIDDolphinSend_97x61.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_RFIDDolphinSuccess_108x57, + pack, + "RFID/RFIDDolphinSuccess_108x57.bmx", + path, + file); + swap_bmx_icon( + &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); + swap_bmx_icon( + &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); + swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); + + storage_file_free(file); + } + furi_record_close(RECORD_STORAGE); + furi_string_free(path); +} + +void swap_bmx_icon( + const Icon** replace, + const char* pack, + const char* name, + FuriString* path, + File* file) { + furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -65,80 +190,3 @@ void icon(const Icon** replace, const char* name, FuriString* path, File* file) storage_file_close(file); } } - -void swap(XtremeAssets* x, FuriString* p, File* f) { - anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); - icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); - icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); - icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); - icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); - icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); - icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); - icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); - icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); - icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); - icon(&x->I_passport_DB, "Passport/passport_DB", p, f); - icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); - icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); - icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); - icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); - icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); - icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); - icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); - icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); - icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); - icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); - icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); -} - -void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - - xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - - if(xtreme_settings->asset_pack[0] == '\0') return; - xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; - FileInfo info; - FuriString* path = furi_string_alloc(); - furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && - info.flags & FSF_DIRECTORY) { - File* file = storage_file_alloc(storage); - swap(xtreme_assets, path, file); - storage_file_free(file); - } - furi_record_close(RECORD_STORAGE); - furi_string_free(path); -} - -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; -} diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index 038372a43..c49f5b590 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,8 +7,6 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { - bool is_nsfw; - const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; @@ -32,6 +30,15 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; +XtremeAssets* XTREME_ASSETS(); + void XTREME_ASSETS_LOAD(); -XtremeAssets* XTREME_ASSETS(); +void swap_bmx_icon( + const Icon** replace, + const char* base, + const char* name, + FuriString* path, + File* file); + +void free_bmx_icon(Icon* icon); diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index 13bb574ad..ee0ec5583 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,6 +18,7 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; + bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png deleted file mode 100644 index f60598005d41ff05a9f763f42f1a6b7900150e33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png deleted file mode 100644 index e80fea5bd7f694549da1b45e9f3db056342a95cc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3376 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 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png deleted file mode 100644 index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4224 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png deleted file mode 100644 index 98d7e15f9c105cc36c1f6d3473fd915a9687968b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png deleted file mode 100644 index 2528ebc95d79335975511a384c70c010d476a8c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4862 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 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png deleted file mode 100644 index fef503263fd962534e7e5543954612bf6c1faefd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4882 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 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png deleted file mode 100644 index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4466 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 Ef`SCamH+?% diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png deleted file mode 100644 index d1164c59483a2242769327b5b2c9ac01acb83186..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3798 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 M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png deleted file mode 100644 index a48c5330e85c2135866fe99bcb6f3534d06c6d53..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4092 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 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png deleted file mode 100644 index bc1010ca9da500055615c00359ea7536df4caa01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png deleted file mode 100644 index 4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5122 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 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png deleted file mode 100644 index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4719 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 diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png b/assets/dolphin/external/nsfw/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png rename to assets/dolphin/external/nsfw/lvl_1/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png b/assets/dolphin/external/nsfw/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png rename to assets/dolphin/external/nsfw/lvl_1/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png b/assets/dolphin/external/nsfw/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png rename to assets/dolphin/external/nsfw/lvl_1/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png b/assets/dolphin/external/nsfw/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png rename to assets/dolphin/external/nsfw/lvl_1/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png b/assets/dolphin/external/nsfw/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png rename to assets/dolphin/external/nsfw/lvl_1/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png b/assets/dolphin/external/nsfw/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png rename to assets/dolphin/external/nsfw/lvl_1/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png b/assets/dolphin/external/nsfw/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png rename to assets/dolphin/external/nsfw/lvl_1/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png b/assets/dolphin/external/nsfw/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png rename to assets/dolphin/external/nsfw/lvl_1/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png b/assets/dolphin/external/nsfw/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png rename to assets/dolphin/external/nsfw/lvl_1/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png b/assets/dolphin/external/nsfw/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png rename to assets/dolphin/external/nsfw/lvl_1/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png b/assets/dolphin/external/nsfw/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png rename to assets/dolphin/external/nsfw/lvl_1/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png b/assets/dolphin/external/nsfw/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png rename to assets/dolphin/external/nsfw/lvl_1/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png b/assets/dolphin/external/nsfw/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png rename to assets/dolphin/external/nsfw/lvl_1/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png b/assets/dolphin/external/nsfw/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png rename to assets/dolphin/external/nsfw/lvl_1/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png b/assets/dolphin/external/nsfw/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png rename to assets/dolphin/external/nsfw/lvl_1/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png b/assets/dolphin/external/nsfw/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png rename to assets/dolphin/external/nsfw/lvl_1/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png b/assets/dolphin/external/nsfw/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png rename to assets/dolphin/external/nsfw/lvl_1/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png b/assets/dolphin/external/nsfw/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png rename to assets/dolphin/external/nsfw/lvl_1/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png b/assets/dolphin/external/nsfw/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png rename to assets/dolphin/external/nsfw/lvl_1/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png b/assets/dolphin/external/nsfw/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png rename to assets/dolphin/external/nsfw/lvl_1/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png b/assets/dolphin/external/nsfw/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png rename to assets/dolphin/external/nsfw/lvl_1/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png b/assets/dolphin/external/nsfw/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png rename to assets/dolphin/external/nsfw/lvl_1/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png b/assets/dolphin/external/nsfw/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png rename to assets/dolphin/external/nsfw/lvl_1/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png b/assets/dolphin/external/nsfw/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png rename to assets/dolphin/external/nsfw/lvl_1/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png b/assets/dolphin/external/nsfw/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png rename to assets/dolphin/external/nsfw/lvl_1/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png b/assets/dolphin/external/nsfw/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png rename to assets/dolphin/external/nsfw/lvl_1/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png b/assets/dolphin/external/nsfw/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png rename to assets/dolphin/external/nsfw/lvl_1/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png b/assets/dolphin/external/nsfw/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png rename to assets/dolphin/external/nsfw/lvl_1/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png b/assets/dolphin/external/nsfw/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png rename to assets/dolphin/external/nsfw/lvl_1/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png b/assets/dolphin/external/nsfw/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png rename to assets/dolphin/external/nsfw/lvl_1/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png b/assets/dolphin/external/nsfw/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png rename to assets/dolphin/external/nsfw/lvl_1/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt b/assets/dolphin/external/nsfw/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt rename to assets/dolphin/external/nsfw/lvl_1/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png b/assets/dolphin/external/nsfw/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png rename to assets/dolphin/external/nsfw/lvl_10/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png b/assets/dolphin/external/nsfw/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png rename to assets/dolphin/external/nsfw/lvl_10/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png b/assets/dolphin/external/nsfw/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png rename to assets/dolphin/external/nsfw/lvl_10/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png b/assets/dolphin/external/nsfw/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png rename to assets/dolphin/external/nsfw/lvl_10/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png b/assets/dolphin/external/nsfw/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png rename to assets/dolphin/external/nsfw/lvl_10/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png b/assets/dolphin/external/nsfw/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png rename to assets/dolphin/external/nsfw/lvl_10/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png b/assets/dolphin/external/nsfw/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png rename to assets/dolphin/external/nsfw/lvl_10/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png b/assets/dolphin/external/nsfw/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png rename to assets/dolphin/external/nsfw/lvl_10/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png b/assets/dolphin/external/nsfw/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png rename to assets/dolphin/external/nsfw/lvl_10/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png b/assets/dolphin/external/nsfw/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png rename to assets/dolphin/external/nsfw/lvl_10/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png b/assets/dolphin/external/nsfw/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png rename to assets/dolphin/external/nsfw/lvl_10/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png b/assets/dolphin/external/nsfw/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png rename to assets/dolphin/external/nsfw/lvl_10/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png b/assets/dolphin/external/nsfw/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png rename to assets/dolphin/external/nsfw/lvl_10/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png b/assets/dolphin/external/nsfw/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png rename to assets/dolphin/external/nsfw/lvl_10/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png b/assets/dolphin/external/nsfw/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png rename to assets/dolphin/external/nsfw/lvl_10/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png b/assets/dolphin/external/nsfw/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png rename to assets/dolphin/external/nsfw/lvl_10/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png b/assets/dolphin/external/nsfw/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png rename to assets/dolphin/external/nsfw/lvl_10/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png b/assets/dolphin/external/nsfw/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png rename to assets/dolphin/external/nsfw/lvl_10/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png b/assets/dolphin/external/nsfw/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png rename to assets/dolphin/external/nsfw/lvl_10/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png b/assets/dolphin/external/nsfw/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png rename to assets/dolphin/external/nsfw/lvl_10/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png b/assets/dolphin/external/nsfw/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png rename to assets/dolphin/external/nsfw/lvl_10/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png b/assets/dolphin/external/nsfw/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png rename to assets/dolphin/external/nsfw/lvl_10/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png b/assets/dolphin/external/nsfw/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png rename to assets/dolphin/external/nsfw/lvl_10/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png b/assets/dolphin/external/nsfw/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png rename to assets/dolphin/external/nsfw/lvl_10/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png b/assets/dolphin/external/nsfw/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png rename to assets/dolphin/external/nsfw/lvl_10/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png b/assets/dolphin/external/nsfw/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png rename to assets/dolphin/external/nsfw/lvl_10/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png b/assets/dolphin/external/nsfw/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png rename to assets/dolphin/external/nsfw/lvl_10/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png b/assets/dolphin/external/nsfw/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png rename to assets/dolphin/external/nsfw/lvl_10/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt b/assets/dolphin/external/nsfw/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt rename to assets/dolphin/external/nsfw/lvl_10/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png b/assets/dolphin/external/nsfw/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png rename to assets/dolphin/external/nsfw/lvl_11/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png b/assets/dolphin/external/nsfw/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png rename to assets/dolphin/external/nsfw/lvl_11/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png b/assets/dolphin/external/nsfw/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png rename to assets/dolphin/external/nsfw/lvl_11/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png b/assets/dolphin/external/nsfw/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png rename to assets/dolphin/external/nsfw/lvl_11/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png b/assets/dolphin/external/nsfw/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png rename to assets/dolphin/external/nsfw/lvl_11/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png b/assets/dolphin/external/nsfw/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png rename to assets/dolphin/external/nsfw/lvl_11/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png b/assets/dolphin/external/nsfw/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png rename to assets/dolphin/external/nsfw/lvl_11/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png b/assets/dolphin/external/nsfw/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png rename to assets/dolphin/external/nsfw/lvl_11/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png b/assets/dolphin/external/nsfw/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png rename to assets/dolphin/external/nsfw/lvl_11/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png b/assets/dolphin/external/nsfw/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png rename to assets/dolphin/external/nsfw/lvl_11/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png b/assets/dolphin/external/nsfw/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png rename to assets/dolphin/external/nsfw/lvl_11/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png b/assets/dolphin/external/nsfw/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png rename to assets/dolphin/external/nsfw/lvl_11/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png b/assets/dolphin/external/nsfw/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png rename to assets/dolphin/external/nsfw/lvl_11/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png b/assets/dolphin/external/nsfw/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png rename to assets/dolphin/external/nsfw/lvl_11/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png b/assets/dolphin/external/nsfw/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png rename to assets/dolphin/external/nsfw/lvl_11/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png b/assets/dolphin/external/nsfw/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png rename to assets/dolphin/external/nsfw/lvl_11/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png b/assets/dolphin/external/nsfw/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png rename to assets/dolphin/external/nsfw/lvl_11/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png b/assets/dolphin/external/nsfw/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png rename to assets/dolphin/external/nsfw/lvl_11/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png b/assets/dolphin/external/nsfw/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png rename to assets/dolphin/external/nsfw/lvl_11/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png b/assets/dolphin/external/nsfw/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png rename to assets/dolphin/external/nsfw/lvl_11/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png b/assets/dolphin/external/nsfw/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png rename to assets/dolphin/external/nsfw/lvl_11/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png b/assets/dolphin/external/nsfw/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png rename to assets/dolphin/external/nsfw/lvl_11/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png b/assets/dolphin/external/nsfw/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png rename to assets/dolphin/external/nsfw/lvl_11/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png b/assets/dolphin/external/nsfw/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png rename to assets/dolphin/external/nsfw/lvl_11/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png b/assets/dolphin/external/nsfw/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png rename to assets/dolphin/external/nsfw/lvl_11/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png b/assets/dolphin/external/nsfw/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png rename to assets/dolphin/external/nsfw/lvl_11/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png b/assets/dolphin/external/nsfw/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png rename to assets/dolphin/external/nsfw/lvl_11/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png b/assets/dolphin/external/nsfw/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png rename to assets/dolphin/external/nsfw/lvl_11/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png b/assets/dolphin/external/nsfw/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png rename to assets/dolphin/external/nsfw/lvl_11/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png b/assets/dolphin/external/nsfw/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png rename to assets/dolphin/external/nsfw/lvl_11/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png b/assets/dolphin/external/nsfw/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png rename to assets/dolphin/external/nsfw/lvl_11/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png b/assets/dolphin/external/nsfw/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png rename to assets/dolphin/external/nsfw/lvl_11/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png b/assets/dolphin/external/nsfw/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png rename to assets/dolphin/external/nsfw/lvl_11/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png b/assets/dolphin/external/nsfw/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png rename to assets/dolphin/external/nsfw/lvl_11/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png b/assets/dolphin/external/nsfw/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png rename to assets/dolphin/external/nsfw/lvl_11/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png b/assets/dolphin/external/nsfw/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png rename to assets/dolphin/external/nsfw/lvl_11/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png b/assets/dolphin/external/nsfw/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png rename to assets/dolphin/external/nsfw/lvl_11/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png b/assets/dolphin/external/nsfw/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png rename to assets/dolphin/external/nsfw/lvl_11/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png b/assets/dolphin/external/nsfw/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png rename to assets/dolphin/external/nsfw/lvl_11/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png b/assets/dolphin/external/nsfw/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png rename to assets/dolphin/external/nsfw/lvl_11/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png b/assets/dolphin/external/nsfw/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png rename to assets/dolphin/external/nsfw/lvl_11/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png b/assets/dolphin/external/nsfw/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png rename to assets/dolphin/external/nsfw/lvl_11/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png b/assets/dolphin/external/nsfw/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png rename to assets/dolphin/external/nsfw/lvl_11/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png b/assets/dolphin/external/nsfw/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png rename to assets/dolphin/external/nsfw/lvl_11/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png b/assets/dolphin/external/nsfw/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png rename to assets/dolphin/external/nsfw/lvl_11/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png b/assets/dolphin/external/nsfw/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png rename to assets/dolphin/external/nsfw/lvl_11/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png b/assets/dolphin/external/nsfw/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png rename to assets/dolphin/external/nsfw/lvl_11/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png b/assets/dolphin/external/nsfw/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png rename to assets/dolphin/external/nsfw/lvl_11/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png b/assets/dolphin/external/nsfw/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png rename to assets/dolphin/external/nsfw/lvl_11/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png b/assets/dolphin/external/nsfw/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png rename to assets/dolphin/external/nsfw/lvl_11/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt b/assets/dolphin/external/nsfw/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt rename to assets/dolphin/external/nsfw/lvl_11/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png b/assets/dolphin/external/nsfw/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png rename to assets/dolphin/external/nsfw/lvl_12/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png b/assets/dolphin/external/nsfw/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png rename to assets/dolphin/external/nsfw/lvl_12/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png b/assets/dolphin/external/nsfw/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png rename to assets/dolphin/external/nsfw/lvl_12/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png b/assets/dolphin/external/nsfw/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png rename to assets/dolphin/external/nsfw/lvl_12/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png b/assets/dolphin/external/nsfw/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png rename to assets/dolphin/external/nsfw/lvl_12/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png b/assets/dolphin/external/nsfw/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png rename to assets/dolphin/external/nsfw/lvl_12/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png b/assets/dolphin/external/nsfw/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png rename to assets/dolphin/external/nsfw/lvl_12/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png b/assets/dolphin/external/nsfw/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png rename to assets/dolphin/external/nsfw/lvl_12/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png b/assets/dolphin/external/nsfw/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png rename to assets/dolphin/external/nsfw/lvl_12/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png b/assets/dolphin/external/nsfw/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png rename to assets/dolphin/external/nsfw/lvl_12/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png b/assets/dolphin/external/nsfw/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png rename to assets/dolphin/external/nsfw/lvl_12/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png b/assets/dolphin/external/nsfw/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png rename to assets/dolphin/external/nsfw/lvl_12/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png b/assets/dolphin/external/nsfw/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png rename to assets/dolphin/external/nsfw/lvl_12/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png b/assets/dolphin/external/nsfw/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png rename to assets/dolphin/external/nsfw/lvl_12/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png b/assets/dolphin/external/nsfw/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png rename to assets/dolphin/external/nsfw/lvl_12/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png b/assets/dolphin/external/nsfw/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png rename to assets/dolphin/external/nsfw/lvl_12/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt b/assets/dolphin/external/nsfw/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt rename to assets/dolphin/external/nsfw/lvl_12/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png b/assets/dolphin/external/nsfw/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png rename to assets/dolphin/external/nsfw/lvl_13/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png b/assets/dolphin/external/nsfw/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png rename to assets/dolphin/external/nsfw/lvl_13/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png b/assets/dolphin/external/nsfw/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png rename to assets/dolphin/external/nsfw/lvl_13/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png b/assets/dolphin/external/nsfw/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png rename to assets/dolphin/external/nsfw/lvl_13/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png b/assets/dolphin/external/nsfw/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png rename to assets/dolphin/external/nsfw/lvl_13/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png b/assets/dolphin/external/nsfw/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png rename to assets/dolphin/external/nsfw/lvl_13/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png b/assets/dolphin/external/nsfw/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png rename to assets/dolphin/external/nsfw/lvl_13/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png b/assets/dolphin/external/nsfw/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png rename to assets/dolphin/external/nsfw/lvl_13/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png b/assets/dolphin/external/nsfw/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png rename to assets/dolphin/external/nsfw/lvl_13/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png b/assets/dolphin/external/nsfw/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png rename to assets/dolphin/external/nsfw/lvl_13/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png b/assets/dolphin/external/nsfw/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png rename to assets/dolphin/external/nsfw/lvl_13/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt b/assets/dolphin/external/nsfw/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt rename to assets/dolphin/external/nsfw/lvl_13/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png b/assets/dolphin/external/nsfw/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png rename to assets/dolphin/external/nsfw/lvl_14/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png b/assets/dolphin/external/nsfw/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png rename to assets/dolphin/external/nsfw/lvl_14/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png b/assets/dolphin/external/nsfw/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png rename to assets/dolphin/external/nsfw/lvl_14/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png b/assets/dolphin/external/nsfw/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png rename to assets/dolphin/external/nsfw/lvl_14/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png b/assets/dolphin/external/nsfw/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png rename to assets/dolphin/external/nsfw/lvl_14/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png b/assets/dolphin/external/nsfw/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png rename to assets/dolphin/external/nsfw/lvl_14/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png b/assets/dolphin/external/nsfw/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png rename to assets/dolphin/external/nsfw/lvl_14/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png b/assets/dolphin/external/nsfw/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png rename to assets/dolphin/external/nsfw/lvl_14/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt b/assets/dolphin/external/nsfw/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt rename to assets/dolphin/external/nsfw/lvl_14/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png b/assets/dolphin/external/nsfw/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png rename to assets/dolphin/external/nsfw/lvl_15/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png b/assets/dolphin/external/nsfw/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png rename to assets/dolphin/external/nsfw/lvl_15/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png b/assets/dolphin/external/nsfw/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png rename to assets/dolphin/external/nsfw/lvl_15/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png b/assets/dolphin/external/nsfw/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png rename to assets/dolphin/external/nsfw/lvl_15/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png b/assets/dolphin/external/nsfw/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png rename to assets/dolphin/external/nsfw/lvl_15/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png b/assets/dolphin/external/nsfw/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png rename to assets/dolphin/external/nsfw/lvl_15/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png b/assets/dolphin/external/nsfw/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png rename to assets/dolphin/external/nsfw/lvl_15/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png b/assets/dolphin/external/nsfw/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png rename to assets/dolphin/external/nsfw/lvl_15/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png b/assets/dolphin/external/nsfw/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png rename to assets/dolphin/external/nsfw/lvl_15/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png b/assets/dolphin/external/nsfw/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png rename to assets/dolphin/external/nsfw/lvl_15/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png b/assets/dolphin/external/nsfw/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png rename to assets/dolphin/external/nsfw/lvl_15/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png b/assets/dolphin/external/nsfw/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png rename to assets/dolphin/external/nsfw/lvl_15/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png b/assets/dolphin/external/nsfw/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png rename to assets/dolphin/external/nsfw/lvl_15/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png b/assets/dolphin/external/nsfw/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png rename to assets/dolphin/external/nsfw/lvl_15/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png b/assets/dolphin/external/nsfw/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png rename to assets/dolphin/external/nsfw/lvl_15/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png b/assets/dolphin/external/nsfw/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png rename to assets/dolphin/external/nsfw/lvl_15/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png b/assets/dolphin/external/nsfw/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png rename to assets/dolphin/external/nsfw/lvl_15/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png b/assets/dolphin/external/nsfw/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png rename to assets/dolphin/external/nsfw/lvl_15/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png b/assets/dolphin/external/nsfw/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png rename to assets/dolphin/external/nsfw/lvl_15/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png b/assets/dolphin/external/nsfw/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png rename to assets/dolphin/external/nsfw/lvl_15/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png b/assets/dolphin/external/nsfw/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png rename to assets/dolphin/external/nsfw/lvl_15/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png b/assets/dolphin/external/nsfw/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png rename to assets/dolphin/external/nsfw/lvl_15/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt b/assets/dolphin/external/nsfw/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt rename to assets/dolphin/external/nsfw/lvl_15/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png b/assets/dolphin/external/nsfw/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png rename to assets/dolphin/external/nsfw/lvl_16/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png b/assets/dolphin/external/nsfw/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png rename to assets/dolphin/external/nsfw/lvl_16/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png b/assets/dolphin/external/nsfw/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png rename to assets/dolphin/external/nsfw/lvl_16/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png b/assets/dolphin/external/nsfw/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png rename to assets/dolphin/external/nsfw/lvl_16/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png b/assets/dolphin/external/nsfw/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png rename to assets/dolphin/external/nsfw/lvl_16/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png b/assets/dolphin/external/nsfw/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png rename to assets/dolphin/external/nsfw/lvl_16/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png b/assets/dolphin/external/nsfw/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png rename to assets/dolphin/external/nsfw/lvl_16/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png b/assets/dolphin/external/nsfw/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png rename to assets/dolphin/external/nsfw/lvl_16/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png b/assets/dolphin/external/nsfw/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png rename to assets/dolphin/external/nsfw/lvl_16/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png b/assets/dolphin/external/nsfw/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png rename to assets/dolphin/external/nsfw/lvl_16/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png b/assets/dolphin/external/nsfw/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png rename to assets/dolphin/external/nsfw/lvl_16/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png b/assets/dolphin/external/nsfw/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png rename to assets/dolphin/external/nsfw/lvl_16/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png b/assets/dolphin/external/nsfw/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png rename to assets/dolphin/external/nsfw/lvl_16/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png b/assets/dolphin/external/nsfw/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png rename to assets/dolphin/external/nsfw/lvl_16/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png b/assets/dolphin/external/nsfw/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png rename to assets/dolphin/external/nsfw/lvl_16/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png b/assets/dolphin/external/nsfw/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png rename to assets/dolphin/external/nsfw/lvl_16/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png b/assets/dolphin/external/nsfw/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png rename to assets/dolphin/external/nsfw/lvl_16/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png b/assets/dolphin/external/nsfw/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png rename to assets/dolphin/external/nsfw/lvl_16/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png b/assets/dolphin/external/nsfw/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png rename to assets/dolphin/external/nsfw/lvl_16/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png b/assets/dolphin/external/nsfw/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png rename to assets/dolphin/external/nsfw/lvl_16/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png b/assets/dolphin/external/nsfw/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png rename to assets/dolphin/external/nsfw/lvl_16/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt b/assets/dolphin/external/nsfw/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt rename to assets/dolphin/external/nsfw/lvl_16/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png b/assets/dolphin/external/nsfw/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png rename to assets/dolphin/external/nsfw/lvl_17/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png b/assets/dolphin/external/nsfw/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png rename to assets/dolphin/external/nsfw/lvl_17/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png b/assets/dolphin/external/nsfw/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png rename to assets/dolphin/external/nsfw/lvl_17/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png b/assets/dolphin/external/nsfw/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png rename to assets/dolphin/external/nsfw/lvl_17/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png b/assets/dolphin/external/nsfw/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png rename to assets/dolphin/external/nsfw/lvl_17/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png b/assets/dolphin/external/nsfw/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png rename to assets/dolphin/external/nsfw/lvl_17/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png b/assets/dolphin/external/nsfw/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png rename to assets/dolphin/external/nsfw/lvl_17/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png b/assets/dolphin/external/nsfw/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png rename to assets/dolphin/external/nsfw/lvl_17/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png b/assets/dolphin/external/nsfw/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png rename to assets/dolphin/external/nsfw/lvl_17/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png b/assets/dolphin/external/nsfw/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png rename to assets/dolphin/external/nsfw/lvl_17/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png b/assets/dolphin/external/nsfw/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png rename to assets/dolphin/external/nsfw/lvl_17/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png b/assets/dolphin/external/nsfw/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png rename to assets/dolphin/external/nsfw/lvl_17/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png b/assets/dolphin/external/nsfw/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png rename to assets/dolphin/external/nsfw/lvl_17/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png b/assets/dolphin/external/nsfw/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png rename to assets/dolphin/external/nsfw/lvl_17/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png b/assets/dolphin/external/nsfw/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png rename to assets/dolphin/external/nsfw/lvl_17/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png b/assets/dolphin/external/nsfw/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png rename to assets/dolphin/external/nsfw/lvl_17/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png b/assets/dolphin/external/nsfw/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png rename to assets/dolphin/external/nsfw/lvl_17/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png b/assets/dolphin/external/nsfw/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png rename to assets/dolphin/external/nsfw/lvl_17/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png b/assets/dolphin/external/nsfw/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png rename to assets/dolphin/external/nsfw/lvl_17/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png b/assets/dolphin/external/nsfw/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png rename to assets/dolphin/external/nsfw/lvl_17/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png b/assets/dolphin/external/nsfw/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png rename to assets/dolphin/external/nsfw/lvl_17/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png b/assets/dolphin/external/nsfw/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png rename to assets/dolphin/external/nsfw/lvl_17/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png b/assets/dolphin/external/nsfw/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png rename to assets/dolphin/external/nsfw/lvl_17/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png b/assets/dolphin/external/nsfw/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png rename to assets/dolphin/external/nsfw/lvl_17/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png b/assets/dolphin/external/nsfw/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png rename to assets/dolphin/external/nsfw/lvl_17/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png b/assets/dolphin/external/nsfw/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png rename to assets/dolphin/external/nsfw/lvl_17/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png b/assets/dolphin/external/nsfw/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png rename to assets/dolphin/external/nsfw/lvl_17/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png b/assets/dolphin/external/nsfw/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png rename to assets/dolphin/external/nsfw/lvl_17/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png b/assets/dolphin/external/nsfw/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png rename to assets/dolphin/external/nsfw/lvl_17/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png b/assets/dolphin/external/nsfw/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png rename to assets/dolphin/external/nsfw/lvl_17/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png b/assets/dolphin/external/nsfw/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png rename to assets/dolphin/external/nsfw/lvl_17/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png b/assets/dolphin/external/nsfw/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png rename to assets/dolphin/external/nsfw/lvl_17/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt b/assets/dolphin/external/nsfw/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt rename to assets/dolphin/external/nsfw/lvl_17/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png b/assets/dolphin/external/nsfw/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png rename to assets/dolphin/external/nsfw/lvl_18/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png b/assets/dolphin/external/nsfw/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png rename to assets/dolphin/external/nsfw/lvl_18/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png b/assets/dolphin/external/nsfw/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png rename to assets/dolphin/external/nsfw/lvl_18/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png b/assets/dolphin/external/nsfw/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png rename to assets/dolphin/external/nsfw/lvl_18/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png b/assets/dolphin/external/nsfw/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png rename to assets/dolphin/external/nsfw/lvl_18/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png b/assets/dolphin/external/nsfw/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png rename to assets/dolphin/external/nsfw/lvl_18/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png b/assets/dolphin/external/nsfw/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png rename to assets/dolphin/external/nsfw/lvl_18/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png b/assets/dolphin/external/nsfw/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png rename to assets/dolphin/external/nsfw/lvl_18/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png b/assets/dolphin/external/nsfw/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png rename to assets/dolphin/external/nsfw/lvl_18/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png b/assets/dolphin/external/nsfw/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png rename to assets/dolphin/external/nsfw/lvl_18/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png b/assets/dolphin/external/nsfw/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png rename to assets/dolphin/external/nsfw/lvl_18/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png b/assets/dolphin/external/nsfw/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png rename to assets/dolphin/external/nsfw/lvl_18/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png b/assets/dolphin/external/nsfw/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png rename to assets/dolphin/external/nsfw/lvl_18/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png b/assets/dolphin/external/nsfw/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png rename to assets/dolphin/external/nsfw/lvl_18/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png b/assets/dolphin/external/nsfw/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png rename to assets/dolphin/external/nsfw/lvl_18/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png b/assets/dolphin/external/nsfw/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png rename to assets/dolphin/external/nsfw/lvl_18/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png b/assets/dolphin/external/nsfw/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png rename to assets/dolphin/external/nsfw/lvl_18/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png b/assets/dolphin/external/nsfw/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png rename to assets/dolphin/external/nsfw/lvl_18/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png b/assets/dolphin/external/nsfw/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png rename to assets/dolphin/external/nsfw/lvl_18/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png b/assets/dolphin/external/nsfw/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png rename to assets/dolphin/external/nsfw/lvl_18/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png b/assets/dolphin/external/nsfw/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png rename to assets/dolphin/external/nsfw/lvl_18/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png b/assets/dolphin/external/nsfw/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png rename to assets/dolphin/external/nsfw/lvl_18/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png b/assets/dolphin/external/nsfw/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png rename to assets/dolphin/external/nsfw/lvl_18/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt b/assets/dolphin/external/nsfw/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt rename to assets/dolphin/external/nsfw/lvl_18/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png b/assets/dolphin/external/nsfw/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png rename to assets/dolphin/external/nsfw/lvl_19/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png b/assets/dolphin/external/nsfw/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png rename to assets/dolphin/external/nsfw/lvl_19/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png b/assets/dolphin/external/nsfw/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png rename to assets/dolphin/external/nsfw/lvl_19/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png b/assets/dolphin/external/nsfw/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png rename to assets/dolphin/external/nsfw/lvl_19/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png b/assets/dolphin/external/nsfw/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png rename to assets/dolphin/external/nsfw/lvl_19/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png b/assets/dolphin/external/nsfw/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png rename to assets/dolphin/external/nsfw/lvl_19/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png b/assets/dolphin/external/nsfw/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png rename to assets/dolphin/external/nsfw/lvl_19/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png b/assets/dolphin/external/nsfw/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png rename to assets/dolphin/external/nsfw/lvl_19/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png b/assets/dolphin/external/nsfw/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png rename to assets/dolphin/external/nsfw/lvl_19/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png b/assets/dolphin/external/nsfw/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png rename to assets/dolphin/external/nsfw/lvl_19/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png b/assets/dolphin/external/nsfw/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png rename to assets/dolphin/external/nsfw/lvl_19/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png b/assets/dolphin/external/nsfw/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png rename to assets/dolphin/external/nsfw/lvl_19/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png b/assets/dolphin/external/nsfw/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png rename to assets/dolphin/external/nsfw/lvl_19/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png b/assets/dolphin/external/nsfw/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png rename to assets/dolphin/external/nsfw/lvl_19/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png b/assets/dolphin/external/nsfw/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png rename to assets/dolphin/external/nsfw/lvl_19/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png b/assets/dolphin/external/nsfw/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png rename to assets/dolphin/external/nsfw/lvl_19/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png b/assets/dolphin/external/nsfw/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png rename to assets/dolphin/external/nsfw/lvl_19/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png b/assets/dolphin/external/nsfw/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png rename to assets/dolphin/external/nsfw/lvl_19/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png b/assets/dolphin/external/nsfw/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png rename to assets/dolphin/external/nsfw/lvl_19/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png b/assets/dolphin/external/nsfw/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png rename to assets/dolphin/external/nsfw/lvl_19/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png b/assets/dolphin/external/nsfw/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png rename to assets/dolphin/external/nsfw/lvl_19/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png b/assets/dolphin/external/nsfw/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png rename to assets/dolphin/external/nsfw/lvl_19/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt b/assets/dolphin/external/nsfw/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt rename to assets/dolphin/external/nsfw/lvl_19/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png b/assets/dolphin/external/nsfw/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png rename to assets/dolphin/external/nsfw/lvl_2/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png b/assets/dolphin/external/nsfw/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png rename to assets/dolphin/external/nsfw/lvl_2/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png b/assets/dolphin/external/nsfw/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png rename to assets/dolphin/external/nsfw/lvl_2/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png b/assets/dolphin/external/nsfw/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png rename to assets/dolphin/external/nsfw/lvl_2/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png b/assets/dolphin/external/nsfw/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png rename to assets/dolphin/external/nsfw/lvl_2/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png b/assets/dolphin/external/nsfw/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png rename to assets/dolphin/external/nsfw/lvl_2/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png b/assets/dolphin/external/nsfw/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png rename to assets/dolphin/external/nsfw/lvl_2/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png b/assets/dolphin/external/nsfw/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png rename to assets/dolphin/external/nsfw/lvl_2/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png b/assets/dolphin/external/nsfw/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png rename to assets/dolphin/external/nsfw/lvl_2/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png b/assets/dolphin/external/nsfw/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png rename to assets/dolphin/external/nsfw/lvl_2/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png b/assets/dolphin/external/nsfw/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png rename to assets/dolphin/external/nsfw/lvl_2/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png b/assets/dolphin/external/nsfw/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png rename to assets/dolphin/external/nsfw/lvl_2/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png b/assets/dolphin/external/nsfw/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png rename to assets/dolphin/external/nsfw/lvl_2/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png b/assets/dolphin/external/nsfw/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png rename to assets/dolphin/external/nsfw/lvl_2/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png b/assets/dolphin/external/nsfw/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png rename to assets/dolphin/external/nsfw/lvl_2/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt b/assets/dolphin/external/nsfw/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt rename to assets/dolphin/external/nsfw/lvl_2/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png b/assets/dolphin/external/nsfw/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png rename to assets/dolphin/external/nsfw/lvl_20/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png b/assets/dolphin/external/nsfw/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png rename to assets/dolphin/external/nsfw/lvl_20/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png b/assets/dolphin/external/nsfw/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png rename to assets/dolphin/external/nsfw/lvl_20/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png b/assets/dolphin/external/nsfw/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png rename to assets/dolphin/external/nsfw/lvl_20/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png b/assets/dolphin/external/nsfw/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png rename to assets/dolphin/external/nsfw/lvl_20/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png b/assets/dolphin/external/nsfw/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png rename to assets/dolphin/external/nsfw/lvl_20/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png b/assets/dolphin/external/nsfw/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png rename to assets/dolphin/external/nsfw/lvl_20/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png b/assets/dolphin/external/nsfw/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png rename to assets/dolphin/external/nsfw/lvl_20/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png b/assets/dolphin/external/nsfw/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png rename to assets/dolphin/external/nsfw/lvl_20/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png b/assets/dolphin/external/nsfw/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png rename to assets/dolphin/external/nsfw/lvl_20/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png b/assets/dolphin/external/nsfw/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png rename to assets/dolphin/external/nsfw/lvl_20/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png b/assets/dolphin/external/nsfw/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png rename to assets/dolphin/external/nsfw/lvl_20/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png b/assets/dolphin/external/nsfw/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png rename to assets/dolphin/external/nsfw/lvl_20/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png b/assets/dolphin/external/nsfw/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png rename to assets/dolphin/external/nsfw/lvl_20/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt b/assets/dolphin/external/nsfw/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt rename to assets/dolphin/external/nsfw/lvl_20/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png b/assets/dolphin/external/nsfw/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png rename to assets/dolphin/external/nsfw/lvl_21/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png b/assets/dolphin/external/nsfw/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png rename to assets/dolphin/external/nsfw/lvl_21/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png b/assets/dolphin/external/nsfw/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png rename to assets/dolphin/external/nsfw/lvl_21/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png b/assets/dolphin/external/nsfw/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png rename to assets/dolphin/external/nsfw/lvl_21/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png b/assets/dolphin/external/nsfw/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png rename to assets/dolphin/external/nsfw/lvl_21/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png b/assets/dolphin/external/nsfw/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png rename to assets/dolphin/external/nsfw/lvl_21/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt b/assets/dolphin/external/nsfw/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt rename to assets/dolphin/external/nsfw/lvl_21/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png b/assets/dolphin/external/nsfw/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png rename to assets/dolphin/external/nsfw/lvl_22/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png b/assets/dolphin/external/nsfw/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png rename to assets/dolphin/external/nsfw/lvl_22/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png b/assets/dolphin/external/nsfw/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png rename to assets/dolphin/external/nsfw/lvl_22/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png b/assets/dolphin/external/nsfw/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png rename to assets/dolphin/external/nsfw/lvl_22/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png b/assets/dolphin/external/nsfw/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png rename to assets/dolphin/external/nsfw/lvl_22/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png b/assets/dolphin/external/nsfw/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png rename to assets/dolphin/external/nsfw/lvl_22/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png b/assets/dolphin/external/nsfw/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png rename to assets/dolphin/external/nsfw/lvl_22/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png b/assets/dolphin/external/nsfw/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png rename to assets/dolphin/external/nsfw/lvl_22/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png b/assets/dolphin/external/nsfw/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png rename to assets/dolphin/external/nsfw/lvl_22/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png b/assets/dolphin/external/nsfw/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png rename to assets/dolphin/external/nsfw/lvl_22/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png b/assets/dolphin/external/nsfw/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png rename to assets/dolphin/external/nsfw/lvl_22/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png b/assets/dolphin/external/nsfw/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png rename to assets/dolphin/external/nsfw/lvl_22/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png b/assets/dolphin/external/nsfw/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png rename to assets/dolphin/external/nsfw/lvl_22/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png b/assets/dolphin/external/nsfw/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png rename to assets/dolphin/external/nsfw/lvl_22/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png b/assets/dolphin/external/nsfw/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png rename to assets/dolphin/external/nsfw/lvl_22/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png b/assets/dolphin/external/nsfw/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png rename to assets/dolphin/external/nsfw/lvl_22/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png b/assets/dolphin/external/nsfw/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png rename to assets/dolphin/external/nsfw/lvl_22/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png b/assets/dolphin/external/nsfw/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png rename to assets/dolphin/external/nsfw/lvl_22/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png b/assets/dolphin/external/nsfw/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png rename to assets/dolphin/external/nsfw/lvl_22/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png b/assets/dolphin/external/nsfw/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png rename to assets/dolphin/external/nsfw/lvl_22/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png b/assets/dolphin/external/nsfw/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png rename to assets/dolphin/external/nsfw/lvl_22/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png b/assets/dolphin/external/nsfw/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png rename to assets/dolphin/external/nsfw/lvl_22/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png b/assets/dolphin/external/nsfw/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png rename to assets/dolphin/external/nsfw/lvl_22/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png b/assets/dolphin/external/nsfw/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png rename to assets/dolphin/external/nsfw/lvl_22/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png b/assets/dolphin/external/nsfw/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png rename to assets/dolphin/external/nsfw/lvl_22/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png b/assets/dolphin/external/nsfw/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png rename to assets/dolphin/external/nsfw/lvl_22/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png b/assets/dolphin/external/nsfw/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png rename to assets/dolphin/external/nsfw/lvl_22/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png b/assets/dolphin/external/nsfw/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png rename to assets/dolphin/external/nsfw/lvl_22/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png b/assets/dolphin/external/nsfw/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png rename to assets/dolphin/external/nsfw/lvl_22/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png b/assets/dolphin/external/nsfw/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png rename to assets/dolphin/external/nsfw/lvl_22/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png b/assets/dolphin/external/nsfw/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png rename to assets/dolphin/external/nsfw/lvl_22/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png b/assets/dolphin/external/nsfw/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png rename to assets/dolphin/external/nsfw/lvl_22/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png b/assets/dolphin/external/nsfw/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png rename to assets/dolphin/external/nsfw/lvl_22/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png b/assets/dolphin/external/nsfw/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png rename to assets/dolphin/external/nsfw/lvl_22/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png b/assets/dolphin/external/nsfw/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png rename to assets/dolphin/external/nsfw/lvl_22/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png b/assets/dolphin/external/nsfw/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png rename to assets/dolphin/external/nsfw/lvl_22/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png b/assets/dolphin/external/nsfw/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png rename to assets/dolphin/external/nsfw/lvl_22/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png b/assets/dolphin/external/nsfw/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png rename to assets/dolphin/external/nsfw/lvl_22/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png b/assets/dolphin/external/nsfw/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png rename to assets/dolphin/external/nsfw/lvl_22/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png b/assets/dolphin/external/nsfw/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png rename to assets/dolphin/external/nsfw/lvl_22/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png b/assets/dolphin/external/nsfw/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png rename to assets/dolphin/external/nsfw/lvl_22/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png b/assets/dolphin/external/nsfw/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png rename to assets/dolphin/external/nsfw/lvl_22/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png b/assets/dolphin/external/nsfw/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png rename to assets/dolphin/external/nsfw/lvl_22/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png b/assets/dolphin/external/nsfw/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png rename to assets/dolphin/external/nsfw/lvl_22/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png b/assets/dolphin/external/nsfw/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png rename to assets/dolphin/external/nsfw/lvl_22/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png b/assets/dolphin/external/nsfw/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png rename to assets/dolphin/external/nsfw/lvl_22/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png b/assets/dolphin/external/nsfw/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png rename to assets/dolphin/external/nsfw/lvl_22/frame_50.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png b/assets/dolphin/external/nsfw/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png rename to assets/dolphin/external/nsfw/lvl_22/frame_51.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png b/assets/dolphin/external/nsfw/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png rename to assets/dolphin/external/nsfw/lvl_22/frame_52.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png b/assets/dolphin/external/nsfw/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png rename to assets/dolphin/external/nsfw/lvl_22/frame_53.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png b/assets/dolphin/external/nsfw/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png rename to assets/dolphin/external/nsfw/lvl_22/frame_54.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png b/assets/dolphin/external/nsfw/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png rename to assets/dolphin/external/nsfw/lvl_22/frame_55.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png b/assets/dolphin/external/nsfw/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png rename to assets/dolphin/external/nsfw/lvl_22/frame_56.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png b/assets/dolphin/external/nsfw/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png rename to assets/dolphin/external/nsfw/lvl_22/frame_57.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png b/assets/dolphin/external/nsfw/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png rename to assets/dolphin/external/nsfw/lvl_22/frame_58.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png b/assets/dolphin/external/nsfw/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png rename to assets/dolphin/external/nsfw/lvl_22/frame_59.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png b/assets/dolphin/external/nsfw/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png rename to assets/dolphin/external/nsfw/lvl_22/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png b/assets/dolphin/external/nsfw/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png rename to assets/dolphin/external/nsfw/lvl_22/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png b/assets/dolphin/external/nsfw/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png rename to assets/dolphin/external/nsfw/lvl_22/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png b/assets/dolphin/external/nsfw/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png rename to assets/dolphin/external/nsfw/lvl_22/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt b/assets/dolphin/external/nsfw/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt rename to assets/dolphin/external/nsfw/lvl_22/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png b/assets/dolphin/external/nsfw/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png rename to assets/dolphin/external/nsfw/lvl_23/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png b/assets/dolphin/external/nsfw/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png rename to assets/dolphin/external/nsfw/lvl_23/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png b/assets/dolphin/external/nsfw/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png rename to assets/dolphin/external/nsfw/lvl_23/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png b/assets/dolphin/external/nsfw/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png rename to assets/dolphin/external/nsfw/lvl_23/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png b/assets/dolphin/external/nsfw/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png rename to assets/dolphin/external/nsfw/lvl_23/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png b/assets/dolphin/external/nsfw/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png rename to assets/dolphin/external/nsfw/lvl_23/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png b/assets/dolphin/external/nsfw/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png rename to assets/dolphin/external/nsfw/lvl_23/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png b/assets/dolphin/external/nsfw/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png rename to assets/dolphin/external/nsfw/lvl_23/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png b/assets/dolphin/external/nsfw/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png rename to assets/dolphin/external/nsfw/lvl_23/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png b/assets/dolphin/external/nsfw/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png rename to assets/dolphin/external/nsfw/lvl_23/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png b/assets/dolphin/external/nsfw/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png rename to assets/dolphin/external/nsfw/lvl_23/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png b/assets/dolphin/external/nsfw/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png rename to assets/dolphin/external/nsfw/lvl_23/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png b/assets/dolphin/external/nsfw/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png rename to assets/dolphin/external/nsfw/lvl_23/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png b/assets/dolphin/external/nsfw/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png rename to assets/dolphin/external/nsfw/lvl_23/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png b/assets/dolphin/external/nsfw/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png rename to assets/dolphin/external/nsfw/lvl_23/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png b/assets/dolphin/external/nsfw/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png rename to assets/dolphin/external/nsfw/lvl_23/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png b/assets/dolphin/external/nsfw/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png rename to assets/dolphin/external/nsfw/lvl_23/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt b/assets/dolphin/external/nsfw/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt rename to assets/dolphin/external/nsfw/lvl_23/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png b/assets/dolphin/external/nsfw/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png rename to assets/dolphin/external/nsfw/lvl_24/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png b/assets/dolphin/external/nsfw/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png rename to assets/dolphin/external/nsfw/lvl_24/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png b/assets/dolphin/external/nsfw/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png rename to assets/dolphin/external/nsfw/lvl_24/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png b/assets/dolphin/external/nsfw/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png rename to assets/dolphin/external/nsfw/lvl_24/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png b/assets/dolphin/external/nsfw/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png rename to assets/dolphin/external/nsfw/lvl_24/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png b/assets/dolphin/external/nsfw/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png rename to assets/dolphin/external/nsfw/lvl_24/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png b/assets/dolphin/external/nsfw/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png rename to assets/dolphin/external/nsfw/lvl_24/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png b/assets/dolphin/external/nsfw/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png rename to assets/dolphin/external/nsfw/lvl_24/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png b/assets/dolphin/external/nsfw/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png rename to assets/dolphin/external/nsfw/lvl_24/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png b/assets/dolphin/external/nsfw/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png rename to assets/dolphin/external/nsfw/lvl_24/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png b/assets/dolphin/external/nsfw/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png rename to assets/dolphin/external/nsfw/lvl_24/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png b/assets/dolphin/external/nsfw/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png rename to assets/dolphin/external/nsfw/lvl_24/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png b/assets/dolphin/external/nsfw/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png rename to assets/dolphin/external/nsfw/lvl_24/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png b/assets/dolphin/external/nsfw/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png rename to assets/dolphin/external/nsfw/lvl_24/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png b/assets/dolphin/external/nsfw/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png rename to assets/dolphin/external/nsfw/lvl_24/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png b/assets/dolphin/external/nsfw/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png rename to assets/dolphin/external/nsfw/lvl_24/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png b/assets/dolphin/external/nsfw/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png rename to assets/dolphin/external/nsfw/lvl_24/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png b/assets/dolphin/external/nsfw/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png rename to assets/dolphin/external/nsfw/lvl_24/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png b/assets/dolphin/external/nsfw/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png rename to assets/dolphin/external/nsfw/lvl_24/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png b/assets/dolphin/external/nsfw/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png rename to assets/dolphin/external/nsfw/lvl_24/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png b/assets/dolphin/external/nsfw/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png rename to assets/dolphin/external/nsfw/lvl_24/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png b/assets/dolphin/external/nsfw/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png rename to assets/dolphin/external/nsfw/lvl_24/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png b/assets/dolphin/external/nsfw/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png rename to assets/dolphin/external/nsfw/lvl_24/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png b/assets/dolphin/external/nsfw/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png rename to assets/dolphin/external/nsfw/lvl_24/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png b/assets/dolphin/external/nsfw/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png rename to assets/dolphin/external/nsfw/lvl_24/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png b/assets/dolphin/external/nsfw/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png rename to assets/dolphin/external/nsfw/lvl_24/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png b/assets/dolphin/external/nsfw/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png rename to assets/dolphin/external/nsfw/lvl_24/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png b/assets/dolphin/external/nsfw/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png rename to assets/dolphin/external/nsfw/lvl_24/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png b/assets/dolphin/external/nsfw/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png rename to assets/dolphin/external/nsfw/lvl_24/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png b/assets/dolphin/external/nsfw/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png rename to assets/dolphin/external/nsfw/lvl_24/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt b/assets/dolphin/external/nsfw/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt rename to assets/dolphin/external/nsfw/lvl_24/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png b/assets/dolphin/external/nsfw/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png rename to assets/dolphin/external/nsfw/lvl_25/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png b/assets/dolphin/external/nsfw/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png rename to assets/dolphin/external/nsfw/lvl_25/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png b/assets/dolphin/external/nsfw/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png rename to assets/dolphin/external/nsfw/lvl_25/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png b/assets/dolphin/external/nsfw/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png rename to assets/dolphin/external/nsfw/lvl_25/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png b/assets/dolphin/external/nsfw/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png rename to assets/dolphin/external/nsfw/lvl_25/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png b/assets/dolphin/external/nsfw/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png rename to assets/dolphin/external/nsfw/lvl_25/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png b/assets/dolphin/external/nsfw/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png rename to assets/dolphin/external/nsfw/lvl_25/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png b/assets/dolphin/external/nsfw/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png rename to assets/dolphin/external/nsfw/lvl_25/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png b/assets/dolphin/external/nsfw/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png rename to assets/dolphin/external/nsfw/lvl_25/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png b/assets/dolphin/external/nsfw/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png rename to assets/dolphin/external/nsfw/lvl_25/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png b/assets/dolphin/external/nsfw/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png rename to assets/dolphin/external/nsfw/lvl_25/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png b/assets/dolphin/external/nsfw/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png rename to assets/dolphin/external/nsfw/lvl_25/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png b/assets/dolphin/external/nsfw/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png rename to assets/dolphin/external/nsfw/lvl_25/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png b/assets/dolphin/external/nsfw/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png rename to assets/dolphin/external/nsfw/lvl_25/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png b/assets/dolphin/external/nsfw/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png rename to assets/dolphin/external/nsfw/lvl_25/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png b/assets/dolphin/external/nsfw/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png rename to assets/dolphin/external/nsfw/lvl_25/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png b/assets/dolphin/external/nsfw/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png rename to assets/dolphin/external/nsfw/lvl_25/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png b/assets/dolphin/external/nsfw/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png rename to assets/dolphin/external/nsfw/lvl_25/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png b/assets/dolphin/external/nsfw/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png rename to assets/dolphin/external/nsfw/lvl_25/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png b/assets/dolphin/external/nsfw/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png rename to assets/dolphin/external/nsfw/lvl_25/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png b/assets/dolphin/external/nsfw/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png rename to assets/dolphin/external/nsfw/lvl_25/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png b/assets/dolphin/external/nsfw/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png rename to assets/dolphin/external/nsfw/lvl_25/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png b/assets/dolphin/external/nsfw/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png rename to assets/dolphin/external/nsfw/lvl_25/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png b/assets/dolphin/external/nsfw/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png rename to assets/dolphin/external/nsfw/lvl_25/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png b/assets/dolphin/external/nsfw/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png rename to assets/dolphin/external/nsfw/lvl_25/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png b/assets/dolphin/external/nsfw/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png rename to assets/dolphin/external/nsfw/lvl_25/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png b/assets/dolphin/external/nsfw/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png rename to assets/dolphin/external/nsfw/lvl_25/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png b/assets/dolphin/external/nsfw/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png rename to assets/dolphin/external/nsfw/lvl_25/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png b/assets/dolphin/external/nsfw/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png rename to assets/dolphin/external/nsfw/lvl_25/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png b/assets/dolphin/external/nsfw/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png rename to assets/dolphin/external/nsfw/lvl_25/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png b/assets/dolphin/external/nsfw/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png rename to assets/dolphin/external/nsfw/lvl_25/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png b/assets/dolphin/external/nsfw/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png rename to assets/dolphin/external/nsfw/lvl_25/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png b/assets/dolphin/external/nsfw/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png rename to assets/dolphin/external/nsfw/lvl_25/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png b/assets/dolphin/external/nsfw/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png rename to assets/dolphin/external/nsfw/lvl_25/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png b/assets/dolphin/external/nsfw/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png rename to assets/dolphin/external/nsfw/lvl_25/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png b/assets/dolphin/external/nsfw/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png rename to assets/dolphin/external/nsfw/lvl_25/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt b/assets/dolphin/external/nsfw/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt rename to assets/dolphin/external/nsfw/lvl_25/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png b/assets/dolphin/external/nsfw/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png rename to assets/dolphin/external/nsfw/lvl_26/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png b/assets/dolphin/external/nsfw/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png rename to assets/dolphin/external/nsfw/lvl_26/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png b/assets/dolphin/external/nsfw/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png rename to assets/dolphin/external/nsfw/lvl_26/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png b/assets/dolphin/external/nsfw/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png rename to assets/dolphin/external/nsfw/lvl_26/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png b/assets/dolphin/external/nsfw/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png rename to assets/dolphin/external/nsfw/lvl_26/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png b/assets/dolphin/external/nsfw/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png rename to assets/dolphin/external/nsfw/lvl_26/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png b/assets/dolphin/external/nsfw/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png rename to assets/dolphin/external/nsfw/lvl_26/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png b/assets/dolphin/external/nsfw/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png rename to assets/dolphin/external/nsfw/lvl_26/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png b/assets/dolphin/external/nsfw/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png rename to assets/dolphin/external/nsfw/lvl_26/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png b/assets/dolphin/external/nsfw/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png rename to assets/dolphin/external/nsfw/lvl_26/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png b/assets/dolphin/external/nsfw/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png rename to assets/dolphin/external/nsfw/lvl_26/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png b/assets/dolphin/external/nsfw/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png rename to assets/dolphin/external/nsfw/lvl_26/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt b/assets/dolphin/external/nsfw/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt rename to assets/dolphin/external/nsfw/lvl_26/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png b/assets/dolphin/external/nsfw/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png rename to assets/dolphin/external/nsfw/lvl_27/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png b/assets/dolphin/external/nsfw/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png rename to assets/dolphin/external/nsfw/lvl_27/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png b/assets/dolphin/external/nsfw/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png rename to assets/dolphin/external/nsfw/lvl_27/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png b/assets/dolphin/external/nsfw/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png rename to assets/dolphin/external/nsfw/lvl_27/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png b/assets/dolphin/external/nsfw/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png rename to assets/dolphin/external/nsfw/lvl_27/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png b/assets/dolphin/external/nsfw/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png rename to assets/dolphin/external/nsfw/lvl_27/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png b/assets/dolphin/external/nsfw/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png rename to assets/dolphin/external/nsfw/lvl_27/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png b/assets/dolphin/external/nsfw/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png rename to assets/dolphin/external/nsfw/lvl_27/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png b/assets/dolphin/external/nsfw/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png rename to assets/dolphin/external/nsfw/lvl_27/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png b/assets/dolphin/external/nsfw/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png rename to assets/dolphin/external/nsfw/lvl_27/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png b/assets/dolphin/external/nsfw/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png rename to assets/dolphin/external/nsfw/lvl_27/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png b/assets/dolphin/external/nsfw/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png rename to assets/dolphin/external/nsfw/lvl_27/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png b/assets/dolphin/external/nsfw/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png rename to assets/dolphin/external/nsfw/lvl_27/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png b/assets/dolphin/external/nsfw/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png rename to assets/dolphin/external/nsfw/lvl_27/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png b/assets/dolphin/external/nsfw/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png rename to assets/dolphin/external/nsfw/lvl_27/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png b/assets/dolphin/external/nsfw/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png rename to assets/dolphin/external/nsfw/lvl_27/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png b/assets/dolphin/external/nsfw/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png rename to assets/dolphin/external/nsfw/lvl_27/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png b/assets/dolphin/external/nsfw/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png rename to assets/dolphin/external/nsfw/lvl_27/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png b/assets/dolphin/external/nsfw/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png rename to assets/dolphin/external/nsfw/lvl_27/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png b/assets/dolphin/external/nsfw/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png rename to assets/dolphin/external/nsfw/lvl_27/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png b/assets/dolphin/external/nsfw/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png rename to assets/dolphin/external/nsfw/lvl_27/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png b/assets/dolphin/external/nsfw/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png rename to assets/dolphin/external/nsfw/lvl_27/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt b/assets/dolphin/external/nsfw/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt rename to assets/dolphin/external/nsfw/lvl_27/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png b/assets/dolphin/external/nsfw/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png rename to assets/dolphin/external/nsfw/lvl_28/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png b/assets/dolphin/external/nsfw/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png rename to assets/dolphin/external/nsfw/lvl_28/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png b/assets/dolphin/external/nsfw/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png rename to assets/dolphin/external/nsfw/lvl_28/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png b/assets/dolphin/external/nsfw/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png rename to assets/dolphin/external/nsfw/lvl_28/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png b/assets/dolphin/external/nsfw/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png rename to assets/dolphin/external/nsfw/lvl_28/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png b/assets/dolphin/external/nsfw/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png rename to assets/dolphin/external/nsfw/lvl_28/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt b/assets/dolphin/external/nsfw/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt rename to assets/dolphin/external/nsfw/lvl_28/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png b/assets/dolphin/external/nsfw/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png rename to assets/dolphin/external/nsfw/lvl_29/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png b/assets/dolphin/external/nsfw/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png rename to assets/dolphin/external/nsfw/lvl_29/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png b/assets/dolphin/external/nsfw/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png rename to assets/dolphin/external/nsfw/lvl_29/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png b/assets/dolphin/external/nsfw/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png rename to assets/dolphin/external/nsfw/lvl_29/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png b/assets/dolphin/external/nsfw/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png rename to assets/dolphin/external/nsfw/lvl_29/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png b/assets/dolphin/external/nsfw/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png rename to assets/dolphin/external/nsfw/lvl_29/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png b/assets/dolphin/external/nsfw/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png rename to assets/dolphin/external/nsfw/lvl_29/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png b/assets/dolphin/external/nsfw/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png rename to assets/dolphin/external/nsfw/lvl_29/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png b/assets/dolphin/external/nsfw/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png rename to assets/dolphin/external/nsfw/lvl_29/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png b/assets/dolphin/external/nsfw/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png rename to assets/dolphin/external/nsfw/lvl_29/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png b/assets/dolphin/external/nsfw/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png rename to assets/dolphin/external/nsfw/lvl_29/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png b/assets/dolphin/external/nsfw/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png rename to assets/dolphin/external/nsfw/lvl_29/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png b/assets/dolphin/external/nsfw/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png rename to assets/dolphin/external/nsfw/lvl_29/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png b/assets/dolphin/external/nsfw/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png rename to assets/dolphin/external/nsfw/lvl_29/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png b/assets/dolphin/external/nsfw/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png rename to assets/dolphin/external/nsfw/lvl_29/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png b/assets/dolphin/external/nsfw/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png rename to assets/dolphin/external/nsfw/lvl_29/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png b/assets/dolphin/external/nsfw/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png rename to assets/dolphin/external/nsfw/lvl_29/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png b/assets/dolphin/external/nsfw/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png rename to assets/dolphin/external/nsfw/lvl_29/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png b/assets/dolphin/external/nsfw/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png rename to assets/dolphin/external/nsfw/lvl_29/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png b/assets/dolphin/external/nsfw/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png rename to assets/dolphin/external/nsfw/lvl_29/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png b/assets/dolphin/external/nsfw/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png rename to assets/dolphin/external/nsfw/lvl_29/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png b/assets/dolphin/external/nsfw/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png rename to assets/dolphin/external/nsfw/lvl_29/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png b/assets/dolphin/external/nsfw/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png rename to assets/dolphin/external/nsfw/lvl_29/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png b/assets/dolphin/external/nsfw/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png rename to assets/dolphin/external/nsfw/lvl_29/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png b/assets/dolphin/external/nsfw/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png rename to assets/dolphin/external/nsfw/lvl_29/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png b/assets/dolphin/external/nsfw/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png rename to assets/dolphin/external/nsfw/lvl_29/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png b/assets/dolphin/external/nsfw/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png rename to assets/dolphin/external/nsfw/lvl_29/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png b/assets/dolphin/external/nsfw/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png rename to assets/dolphin/external/nsfw/lvl_29/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png b/assets/dolphin/external/nsfw/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png rename to assets/dolphin/external/nsfw/lvl_29/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png b/assets/dolphin/external/nsfw/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png rename to assets/dolphin/external/nsfw/lvl_29/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png b/assets/dolphin/external/nsfw/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png rename to assets/dolphin/external/nsfw/lvl_29/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png b/assets/dolphin/external/nsfw/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png rename to assets/dolphin/external/nsfw/lvl_29/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png b/assets/dolphin/external/nsfw/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png rename to assets/dolphin/external/nsfw/lvl_29/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png b/assets/dolphin/external/nsfw/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png rename to assets/dolphin/external/nsfw/lvl_29/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png b/assets/dolphin/external/nsfw/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png rename to assets/dolphin/external/nsfw/lvl_29/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png b/assets/dolphin/external/nsfw/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png rename to assets/dolphin/external/nsfw/lvl_29/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png b/assets/dolphin/external/nsfw/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png rename to assets/dolphin/external/nsfw/lvl_29/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png b/assets/dolphin/external/nsfw/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png rename to assets/dolphin/external/nsfw/lvl_29/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png b/assets/dolphin/external/nsfw/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png rename to assets/dolphin/external/nsfw/lvl_29/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png b/assets/dolphin/external/nsfw/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png rename to assets/dolphin/external/nsfw/lvl_29/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png b/assets/dolphin/external/nsfw/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png rename to assets/dolphin/external/nsfw/lvl_29/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png b/assets/dolphin/external/nsfw/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png rename to assets/dolphin/external/nsfw/lvl_29/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png b/assets/dolphin/external/nsfw/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png rename to assets/dolphin/external/nsfw/lvl_29/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png b/assets/dolphin/external/nsfw/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png rename to assets/dolphin/external/nsfw/lvl_29/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png b/assets/dolphin/external/nsfw/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png rename to assets/dolphin/external/nsfw/lvl_29/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png b/assets/dolphin/external/nsfw/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png rename to assets/dolphin/external/nsfw/lvl_29/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png b/assets/dolphin/external/nsfw/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png rename to assets/dolphin/external/nsfw/lvl_29/frame_50.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png b/assets/dolphin/external/nsfw/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png rename to assets/dolphin/external/nsfw/lvl_29/frame_51.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png b/assets/dolphin/external/nsfw/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png rename to assets/dolphin/external/nsfw/lvl_29/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png b/assets/dolphin/external/nsfw/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png rename to assets/dolphin/external/nsfw/lvl_29/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png b/assets/dolphin/external/nsfw/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png rename to assets/dolphin/external/nsfw/lvl_29/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png b/assets/dolphin/external/nsfw/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png rename to assets/dolphin/external/nsfw/lvl_29/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt b/assets/dolphin/external/nsfw/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt rename to assets/dolphin/external/nsfw/lvl_29/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png b/assets/dolphin/external/nsfw/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png rename to assets/dolphin/external/nsfw/lvl_3/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png b/assets/dolphin/external/nsfw/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png rename to assets/dolphin/external/nsfw/lvl_3/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png b/assets/dolphin/external/nsfw/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png rename to assets/dolphin/external/nsfw/lvl_3/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png b/assets/dolphin/external/nsfw/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png rename to assets/dolphin/external/nsfw/lvl_3/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png b/assets/dolphin/external/nsfw/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png rename to assets/dolphin/external/nsfw/lvl_3/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png b/assets/dolphin/external/nsfw/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png rename to assets/dolphin/external/nsfw/lvl_3/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png b/assets/dolphin/external/nsfw/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png rename to assets/dolphin/external/nsfw/lvl_3/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png b/assets/dolphin/external/nsfw/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png rename to assets/dolphin/external/nsfw/lvl_3/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png b/assets/dolphin/external/nsfw/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png rename to assets/dolphin/external/nsfw/lvl_3/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png b/assets/dolphin/external/nsfw/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png rename to assets/dolphin/external/nsfw/lvl_3/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png b/assets/dolphin/external/nsfw/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png rename to assets/dolphin/external/nsfw/lvl_3/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png b/assets/dolphin/external/nsfw/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png rename to assets/dolphin/external/nsfw/lvl_3/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png b/assets/dolphin/external/nsfw/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png rename to assets/dolphin/external/nsfw/lvl_3/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png b/assets/dolphin/external/nsfw/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png rename to assets/dolphin/external/nsfw/lvl_3/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png b/assets/dolphin/external/nsfw/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png rename to assets/dolphin/external/nsfw/lvl_3/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt b/assets/dolphin/external/nsfw/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt rename to assets/dolphin/external/nsfw/lvl_3/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png b/assets/dolphin/external/nsfw/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png rename to assets/dolphin/external/nsfw/lvl_30/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png b/assets/dolphin/external/nsfw/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png rename to assets/dolphin/external/nsfw/lvl_30/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png b/assets/dolphin/external/nsfw/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png rename to assets/dolphin/external/nsfw/lvl_30/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png b/assets/dolphin/external/nsfw/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png rename to assets/dolphin/external/nsfw/lvl_30/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png b/assets/dolphin/external/nsfw/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png rename to assets/dolphin/external/nsfw/lvl_30/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png b/assets/dolphin/external/nsfw/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png rename to assets/dolphin/external/nsfw/lvl_30/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png b/assets/dolphin/external/nsfw/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png rename to assets/dolphin/external/nsfw/lvl_30/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png b/assets/dolphin/external/nsfw/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png rename to assets/dolphin/external/nsfw/lvl_30/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png b/assets/dolphin/external/nsfw/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png rename to assets/dolphin/external/nsfw/lvl_30/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png b/assets/dolphin/external/nsfw/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png rename to assets/dolphin/external/nsfw/lvl_30/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png b/assets/dolphin/external/nsfw/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png rename to assets/dolphin/external/nsfw/lvl_30/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png b/assets/dolphin/external/nsfw/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png rename to assets/dolphin/external/nsfw/lvl_30/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png b/assets/dolphin/external/nsfw/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png rename to assets/dolphin/external/nsfw/lvl_30/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png b/assets/dolphin/external/nsfw/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png rename to assets/dolphin/external/nsfw/lvl_30/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png b/assets/dolphin/external/nsfw/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png rename to assets/dolphin/external/nsfw/lvl_30/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png b/assets/dolphin/external/nsfw/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png rename to assets/dolphin/external/nsfw/lvl_30/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png b/assets/dolphin/external/nsfw/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png rename to assets/dolphin/external/nsfw/lvl_30/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png b/assets/dolphin/external/nsfw/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png rename to assets/dolphin/external/nsfw/lvl_30/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png b/assets/dolphin/external/nsfw/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png rename to assets/dolphin/external/nsfw/lvl_30/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png b/assets/dolphin/external/nsfw/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png rename to assets/dolphin/external/nsfw/lvl_30/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png b/assets/dolphin/external/nsfw/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png rename to assets/dolphin/external/nsfw/lvl_30/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png b/assets/dolphin/external/nsfw/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png rename to assets/dolphin/external/nsfw/lvl_30/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png b/assets/dolphin/external/nsfw/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png rename to assets/dolphin/external/nsfw/lvl_30/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png b/assets/dolphin/external/nsfw/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png rename to assets/dolphin/external/nsfw/lvl_30/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png b/assets/dolphin/external/nsfw/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png rename to assets/dolphin/external/nsfw/lvl_30/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png b/assets/dolphin/external/nsfw/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png rename to assets/dolphin/external/nsfw/lvl_30/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png b/assets/dolphin/external/nsfw/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png rename to assets/dolphin/external/nsfw/lvl_30/frame_32.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png b/assets/dolphin/external/nsfw/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png rename to assets/dolphin/external/nsfw/lvl_30/frame_33.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png b/assets/dolphin/external/nsfw/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png rename to assets/dolphin/external/nsfw/lvl_30/frame_34.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png b/assets/dolphin/external/nsfw/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png rename to assets/dolphin/external/nsfw/lvl_30/frame_35.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png b/assets/dolphin/external/nsfw/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png rename to assets/dolphin/external/nsfw/lvl_30/frame_36.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png b/assets/dolphin/external/nsfw/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png rename to assets/dolphin/external/nsfw/lvl_30/frame_37.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png b/assets/dolphin/external/nsfw/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png rename to assets/dolphin/external/nsfw/lvl_30/frame_38.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png b/assets/dolphin/external/nsfw/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png rename to assets/dolphin/external/nsfw/lvl_30/frame_39.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png b/assets/dolphin/external/nsfw/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png rename to assets/dolphin/external/nsfw/lvl_30/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png b/assets/dolphin/external/nsfw/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png rename to assets/dolphin/external/nsfw/lvl_30/frame_40.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png b/assets/dolphin/external/nsfw/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png rename to assets/dolphin/external/nsfw/lvl_30/frame_41.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png b/assets/dolphin/external/nsfw/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png rename to assets/dolphin/external/nsfw/lvl_30/frame_42.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png b/assets/dolphin/external/nsfw/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png rename to assets/dolphin/external/nsfw/lvl_30/frame_43.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png b/assets/dolphin/external/nsfw/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png rename to assets/dolphin/external/nsfw/lvl_30/frame_44.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png b/assets/dolphin/external/nsfw/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png rename to assets/dolphin/external/nsfw/lvl_30/frame_45.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png b/assets/dolphin/external/nsfw/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png rename to assets/dolphin/external/nsfw/lvl_30/frame_46.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png b/assets/dolphin/external/nsfw/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png rename to assets/dolphin/external/nsfw/lvl_30/frame_47.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png b/assets/dolphin/external/nsfw/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png rename to assets/dolphin/external/nsfw/lvl_30/frame_48.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png b/assets/dolphin/external/nsfw/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png rename to assets/dolphin/external/nsfw/lvl_30/frame_49.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png b/assets/dolphin/external/nsfw/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png rename to assets/dolphin/external/nsfw/lvl_30/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png b/assets/dolphin/external/nsfw/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png rename to assets/dolphin/external/nsfw/lvl_30/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png b/assets/dolphin/external/nsfw/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png rename to assets/dolphin/external/nsfw/lvl_30/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png b/assets/dolphin/external/nsfw/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png rename to assets/dolphin/external/nsfw/lvl_30/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png b/assets/dolphin/external/nsfw/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png rename to assets/dolphin/external/nsfw/lvl_30/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt b/assets/dolphin/external/nsfw/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt rename to assets/dolphin/external/nsfw/lvl_30/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png b/assets/dolphin/external/nsfw/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png rename to assets/dolphin/external/nsfw/lvl_4/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png b/assets/dolphin/external/nsfw/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png rename to assets/dolphin/external/nsfw/lvl_4/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png b/assets/dolphin/external/nsfw/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png rename to assets/dolphin/external/nsfw/lvl_4/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png b/assets/dolphin/external/nsfw/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png rename to assets/dolphin/external/nsfw/lvl_4/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png b/assets/dolphin/external/nsfw/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png rename to assets/dolphin/external/nsfw/lvl_4/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png b/assets/dolphin/external/nsfw/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png rename to assets/dolphin/external/nsfw/lvl_4/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png b/assets/dolphin/external/nsfw/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png rename to assets/dolphin/external/nsfw/lvl_4/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png b/assets/dolphin/external/nsfw/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png rename to assets/dolphin/external/nsfw/lvl_4/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png b/assets/dolphin/external/nsfw/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png rename to assets/dolphin/external/nsfw/lvl_4/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png b/assets/dolphin/external/nsfw/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png rename to assets/dolphin/external/nsfw/lvl_4/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png b/assets/dolphin/external/nsfw/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png rename to assets/dolphin/external/nsfw/lvl_4/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png b/assets/dolphin/external/nsfw/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png rename to assets/dolphin/external/nsfw/lvl_4/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png b/assets/dolphin/external/nsfw/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png rename to assets/dolphin/external/nsfw/lvl_4/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png b/assets/dolphin/external/nsfw/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png rename to assets/dolphin/external/nsfw/lvl_4/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png b/assets/dolphin/external/nsfw/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png rename to assets/dolphin/external/nsfw/lvl_4/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png b/assets/dolphin/external/nsfw/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png rename to assets/dolphin/external/nsfw/lvl_4/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png b/assets/dolphin/external/nsfw/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png rename to assets/dolphin/external/nsfw/lvl_4/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png b/assets/dolphin/external/nsfw/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png rename to assets/dolphin/external/nsfw/lvl_4/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png b/assets/dolphin/external/nsfw/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png rename to assets/dolphin/external/nsfw/lvl_4/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png b/assets/dolphin/external/nsfw/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png rename to assets/dolphin/external/nsfw/lvl_4/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt b/assets/dolphin/external/nsfw/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt rename to assets/dolphin/external/nsfw/lvl_4/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png b/assets/dolphin/external/nsfw/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png rename to assets/dolphin/external/nsfw/lvl_5/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png b/assets/dolphin/external/nsfw/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png rename to assets/dolphin/external/nsfw/lvl_5/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png b/assets/dolphin/external/nsfw/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png rename to assets/dolphin/external/nsfw/lvl_5/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png b/assets/dolphin/external/nsfw/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png rename to assets/dolphin/external/nsfw/lvl_5/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png b/assets/dolphin/external/nsfw/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png rename to assets/dolphin/external/nsfw/lvl_5/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png b/assets/dolphin/external/nsfw/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png rename to assets/dolphin/external/nsfw/lvl_5/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png b/assets/dolphin/external/nsfw/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png rename to assets/dolphin/external/nsfw/lvl_5/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png b/assets/dolphin/external/nsfw/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png rename to assets/dolphin/external/nsfw/lvl_5/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png b/assets/dolphin/external/nsfw/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png rename to assets/dolphin/external/nsfw/lvl_5/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png b/assets/dolphin/external/nsfw/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png rename to assets/dolphin/external/nsfw/lvl_5/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png b/assets/dolphin/external/nsfw/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png rename to assets/dolphin/external/nsfw/lvl_5/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png b/assets/dolphin/external/nsfw/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png rename to assets/dolphin/external/nsfw/lvl_5/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png b/assets/dolphin/external/nsfw/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png rename to assets/dolphin/external/nsfw/lvl_5/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png b/assets/dolphin/external/nsfw/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png rename to assets/dolphin/external/nsfw/lvl_5/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png b/assets/dolphin/external/nsfw/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png rename to assets/dolphin/external/nsfw/lvl_5/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png b/assets/dolphin/external/nsfw/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png rename to assets/dolphin/external/nsfw/lvl_5/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png b/assets/dolphin/external/nsfw/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png rename to assets/dolphin/external/nsfw/lvl_5/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png b/assets/dolphin/external/nsfw/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png rename to assets/dolphin/external/nsfw/lvl_5/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png b/assets/dolphin/external/nsfw/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png rename to assets/dolphin/external/nsfw/lvl_5/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png b/assets/dolphin/external/nsfw/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png rename to assets/dolphin/external/nsfw/lvl_5/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png b/assets/dolphin/external/nsfw/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png rename to assets/dolphin/external/nsfw/lvl_5/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png b/assets/dolphin/external/nsfw/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png rename to assets/dolphin/external/nsfw/lvl_5/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png b/assets/dolphin/external/nsfw/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png rename to assets/dolphin/external/nsfw/lvl_5/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png b/assets/dolphin/external/nsfw/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png rename to assets/dolphin/external/nsfw/lvl_5/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png b/assets/dolphin/external/nsfw/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png rename to assets/dolphin/external/nsfw/lvl_5/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png b/assets/dolphin/external/nsfw/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png rename to assets/dolphin/external/nsfw/lvl_5/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png b/assets/dolphin/external/nsfw/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png rename to assets/dolphin/external/nsfw/lvl_5/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png b/assets/dolphin/external/nsfw/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png rename to assets/dolphin/external/nsfw/lvl_5/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt b/assets/dolphin/external/nsfw/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt rename to assets/dolphin/external/nsfw/lvl_5/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png b/assets/dolphin/external/nsfw/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png rename to assets/dolphin/external/nsfw/lvl_6/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png b/assets/dolphin/external/nsfw/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png rename to assets/dolphin/external/nsfw/lvl_6/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png b/assets/dolphin/external/nsfw/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png rename to assets/dolphin/external/nsfw/lvl_6/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png b/assets/dolphin/external/nsfw/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png rename to assets/dolphin/external/nsfw/lvl_6/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png b/assets/dolphin/external/nsfw/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png rename to assets/dolphin/external/nsfw/lvl_6/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png b/assets/dolphin/external/nsfw/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png rename to assets/dolphin/external/nsfw/lvl_6/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png b/assets/dolphin/external/nsfw/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png rename to assets/dolphin/external/nsfw/lvl_6/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt b/assets/dolphin/external/nsfw/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt rename to assets/dolphin/external/nsfw/lvl_6/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png b/assets/dolphin/external/nsfw/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png rename to assets/dolphin/external/nsfw/lvl_7/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png b/assets/dolphin/external/nsfw/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png rename to assets/dolphin/external/nsfw/lvl_7/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png b/assets/dolphin/external/nsfw/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png rename to assets/dolphin/external/nsfw/lvl_7/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png b/assets/dolphin/external/nsfw/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png rename to assets/dolphin/external/nsfw/lvl_7/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png b/assets/dolphin/external/nsfw/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png rename to assets/dolphin/external/nsfw/lvl_7/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png b/assets/dolphin/external/nsfw/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png rename to assets/dolphin/external/nsfw/lvl_7/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png b/assets/dolphin/external/nsfw/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png rename to assets/dolphin/external/nsfw/lvl_7/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png b/assets/dolphin/external/nsfw/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png rename to assets/dolphin/external/nsfw/lvl_7/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png b/assets/dolphin/external/nsfw/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png rename to assets/dolphin/external/nsfw/lvl_7/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png b/assets/dolphin/external/nsfw/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png rename to assets/dolphin/external/nsfw/lvl_7/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png b/assets/dolphin/external/nsfw/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png rename to assets/dolphin/external/nsfw/lvl_7/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png b/assets/dolphin/external/nsfw/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png rename to assets/dolphin/external/nsfw/lvl_7/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png b/assets/dolphin/external/nsfw/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png rename to assets/dolphin/external/nsfw/lvl_7/frame_8.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png b/assets/dolphin/external/nsfw/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png rename to assets/dolphin/external/nsfw/lvl_7/frame_9.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt b/assets/dolphin/external/nsfw/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt rename to assets/dolphin/external/nsfw/lvl_7/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png b/assets/dolphin/external/nsfw/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png rename to assets/dolphin/external/nsfw/lvl_8/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png b/assets/dolphin/external/nsfw/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png rename to assets/dolphin/external/nsfw/lvl_8/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png b/assets/dolphin/external/nsfw/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png rename to assets/dolphin/external/nsfw/lvl_8/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png b/assets/dolphin/external/nsfw/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png rename to assets/dolphin/external/nsfw/lvl_8/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png b/assets/dolphin/external/nsfw/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png rename to assets/dolphin/external/nsfw/lvl_8/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png b/assets/dolphin/external/nsfw/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png rename to assets/dolphin/external/nsfw/lvl_8/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt b/assets/dolphin/external/nsfw/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt rename to assets/dolphin/external/nsfw/lvl_8/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png b/assets/dolphin/external/nsfw/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png rename to assets/dolphin/external/nsfw/lvl_9/frame_0.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png b/assets/dolphin/external/nsfw/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png rename to assets/dolphin/external/nsfw/lvl_9/frame_1.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png b/assets/dolphin/external/nsfw/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png rename to assets/dolphin/external/nsfw/lvl_9/frame_2.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png b/assets/dolphin/external/nsfw/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png rename to assets/dolphin/external/nsfw/lvl_9/frame_3.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png b/assets/dolphin/external/nsfw/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png rename to assets/dolphin/external/nsfw/lvl_9/frame_4.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png b/assets/dolphin/external/nsfw/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png rename to assets/dolphin/external/nsfw/lvl_9/frame_5.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png b/assets/dolphin/external/nsfw/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png rename to assets/dolphin/external/nsfw/lvl_9/frame_6.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png b/assets/dolphin/external/nsfw/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png rename to assets/dolphin/external/nsfw/lvl_9/frame_7.png diff --git a/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt b/assets/dolphin/external/nsfw/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt rename to assets/dolphin/external/nsfw/lvl_9/meta.txt diff --git a/assets/dolphin/custom/NSFW/Anims/manifest.txt b/assets/dolphin/external/nsfw/manifest.txt similarity index 100% rename from assets/dolphin/custom/NSFW/Anims/manifest.txt rename to assets/dolphin/external/nsfw/manifest.txt diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Cry_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Mods_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Painting_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/L1_Recording_128x51/meta.txt b/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/L1_Waves_128x50/meta.txt b/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/manifest.txt b/assets/dolphin/external/sfw/manifest.txt similarity index 100% rename from assets/dolphin/external/manifest.txt rename to assets/dolphin/external/sfw/manifest.txt diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png b/assets/icons/Animations/Levelup1_128x64/frame_00.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png rename to assets/icons/Animations/Levelup1_128x64/frame_00.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png b/assets/icons/Animations/Levelup1_128x64/frame_01.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png rename to assets/icons/Animations/Levelup1_128x64/frame_01.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png b/assets/icons/Animations/Levelup1_128x64/frame_02.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png rename to assets/icons/Animations/Levelup1_128x64/frame_02.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png b/assets/icons/Animations/Levelup1_128x64/frame_03.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png rename to assets/icons/Animations/Levelup1_128x64/frame_03.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png b/assets/icons/Animations/Levelup1_128x64/frame_04.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png rename to assets/icons/Animations/Levelup1_128x64/frame_04.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png b/assets/icons/Animations/Levelup1_128x64/frame_05.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png rename to assets/icons/Animations/Levelup1_128x64/frame_05.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png b/assets/icons/Animations/Levelup1_128x64/frame_06.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png rename to assets/icons/Animations/Levelup1_128x64/frame_06.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png b/assets/icons/Animations/Levelup1_128x64/frame_07.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png rename to assets/icons/Animations/Levelup1_128x64/frame_07.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png b/assets/icons/Animations/Levelup1_128x64/frame_08.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png rename to assets/icons/Animations/Levelup1_128x64/frame_08.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png b/assets/icons/Animations/Levelup1_128x64/frame_09.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png rename to assets/icons/Animations/Levelup1_128x64/frame_09.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png b/assets/icons/Animations/Levelup1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png rename to assets/icons/Animations/Levelup1_128x64/frame_10.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png b/assets/icons/Animations/Levelup1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png rename to assets/icons/Animations/Levelup1_128x64/frame_11.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png b/assets/icons/Animations/Levelup1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png rename to assets/icons/Animations/Levelup1_128x64/frame_12.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png b/assets/icons/Animations/Levelup1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png rename to assets/icons/Animations/Levelup1_128x64/frame_13.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png b/assets/icons/Animations/Levelup1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png rename to assets/icons/Animations/Levelup1_128x64/frame_14.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png b/assets/icons/Animations/Levelup1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png rename to assets/icons/Animations/Levelup1_128x64/frame_15.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png b/assets/icons/Animations/Levelup1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png rename to assets/icons/Animations/Levelup1_128x64/frame_16.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png b/assets/icons/Animations/Levelup1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png rename to assets/icons/Animations/Levelup1_128x64/frame_17.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png b/assets/icons/Animations/Levelup1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png rename to assets/icons/Animations/Levelup1_128x64/frame_18.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png b/assets/icons/Animations/Levelup1_128x64/frame_19.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png rename to assets/icons/Animations/Levelup1_128x64/frame_19.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png b/assets/icons/Animations/Levelup1_128x64/frame_20.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png rename to assets/icons/Animations/Levelup1_128x64/frame_20.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png b/assets/icons/Animations/Levelup1_128x64/frame_21.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png rename to assets/icons/Animations/Levelup1_128x64/frame_21.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png b/assets/icons/Animations/Levelup1_128x64/frame_22.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png rename to assets/icons/Animations/Levelup1_128x64/frame_22.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png b/assets/icons/Animations/Levelup1_128x64/frame_23.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png rename to assets/icons/Animations/Levelup1_128x64/frame_23.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png b/assets/icons/Animations/Levelup1_128x64/frame_24.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png rename to assets/icons/Animations/Levelup1_128x64/frame_24.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png b/assets/icons/Animations/Levelup1_128x64/frame_25.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png rename to assets/icons/Animations/Levelup1_128x64/frame_25.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png b/assets/icons/Animations/Levelup1_128x64/frame_26.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png rename to assets/icons/Animations/Levelup1_128x64/frame_26.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png b/assets/icons/Animations/Levelup1_128x64/frame_27.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png rename to assets/icons/Animations/Levelup1_128x64/frame_27.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png b/assets/icons/Animations/Levelup1_128x64/frame_28.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png rename to assets/icons/Animations/Levelup1_128x64/frame_28.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png b/assets/icons/Animations/Levelup1_128x64/frame_29.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png rename to assets/icons/Animations/Levelup1_128x64/frame_29.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png b/assets/icons/Animations/Levelup1_128x64/frame_30.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png rename to assets/icons/Animations/Levelup1_128x64/frame_30.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png b/assets/icons/Animations/Levelup1_128x64/frame_31.png similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png rename to assets/icons/Animations/Levelup1_128x64/frame_31.png diff --git a/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64/frame_rate similarity index 100% rename from assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate rename to assets/icons/Animations/Levelup1_128x64/frame_rate diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png new file mode 100644 index 0000000000000000000000000000000000000000..bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444 GIT binary patch literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png new file mode 100644 index 0000000000000000000000000000000000000000..39c910d3a41314c985e4ce79337cbbf30d1ae79e GIT binary patch literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png new file mode 100644 index 0000000000000000000000000000000000000000..4975adf8627b62a22d002bfc1d7be423dcc5e83b GIT binary patch literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png new file mode 100644 index 0000000000000000000000000000000000000000..e6c88df92b73b89f1ad31810be1a653c3fea5d6f GIT binary patch literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png new file mode 100644 index 0000000000000000000000000000000000000000..e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338 GIT binary patch literal 1672 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 literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png new file mode 100644 index 0000000000000000000000000000000000000000..32e864e9827420142930083d480d331024d033f9 GIT binary patch literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png new file mode 100644 index 0000000000000000000000000000000000000000..c692f48952c106373b56ecafd4bce7ba1295f5da GIT binary patch literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png new file mode 100644 index 0000000000000000000000000000000000000000..fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52 GIT binary patch literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) literal 0 HcmV?d00001 diff --git a/assets/icons/Animations/Levelup_128x64/frame_rate b/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_rate rename to assets/icons/Animations/Levelup1_128x64_sfw/frame_rate diff --git a/assets/icons/Animations/Levelup_128x64/frame_00.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_00.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_01.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_01.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_02.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_02.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_03.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_03.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_04.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_04.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_05.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_05.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_06.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_06.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_07.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_07.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_08.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_08.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_09.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_09.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png diff --git a/assets/icons/Animations/Levelup_128x64/frame_10.png b/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup_128x64/frame_10.png rename to assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate @@ -0,0 +1 @@ +2 diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index 34068c300386e0c88e45b40a8995b0a4360ed79f..f60598005d41ff05a9f763f42f1a6b7900150e33 100644 GIT binary patch delta 2594 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 Ef}^?D!2kdN literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..34068c300386e0c88e45b40a8995b0a4360ed79f GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG literal 0 HcmV?d00001 diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index 089aaed83507431993a76ca25d32fdd9664c1c84..e80fea5bd7f694549da1b45e9f3db056342a95cc 100644 GIT binary patch delta 3373 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 D!U>8P literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..089aaed83507431993a76ca25d32fdd9664c1c84 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj literal 0 HcmV?d00001 diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 46f559f65f11194c94c15bb7f195a1d72ef2a295..93a7ad79cd95cdfe7789366edd8fadb3355e467c 100644 GIT binary patch literal 5390 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 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..46f559f65f11194c94c15bb7f195a1d72ef2a295 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 0 HcmV?d00001 diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png new file mode 100644 index 0000000000000000000000000000000000000000..5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4 GIT binary patch literal 8340 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146 GIT binary patch literal 3692 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png new file mode 100644 index 0000000000000000000000000000000000000000..9aaa1c5552a2d115b7042a4bee7a0c636ed00f01 GIT binary patch literal 981 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 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index 1783531285bed514517fc0821501e718359dd765..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f 100644 GIT binary patch literal 4224 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 E4{AO|uK)l5 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..1783531285bed514517fc0821501e718359dd765 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 0 HcmV?d00001 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png new file mode 100644 index 0000000000000000000000000000000000000000..4e0ba8f05921ccbfbaa4390ad07e98fd454df00a GIT binary patch literal 3748 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 delta 706 zcmV;z0zLiI2JQurIDZ15Nkl;5(?#D zv~htv4nV-d9K@^)0%me<5#Ize&B1}Y*n?0E8-aj*5C9jnJbwWQ!k@UwfQ>*v=8?-P zj+ngT2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${ z1wa7+6aWPPPyiGFY+3Yxqo6!F~AV8 ziy!4bb0@qb0)L%rnJVx9FxfTB%C85YsXYQ1GA_C0Ux~>NW z;gaYrjejUTz>+kl(V56PIstBbs(?Vete;J!O8L2_L}#3UxB|w#F_ik}$qCS#ChG^) z*q|Q2*W0kY+^i-e5l>`}jgG^zpv2kGLf+wzflj@H9aiY0Tpc`_otNG&=#FeMs_Rvk z+9&36QnT#*Fm)eL7-zH&v@fcMpn5_28XQPmE<2ao?RLN4hdxF3GRGTwggg&ZvO}6` oC%mhx)7!$^)2FsIeteJM4=9genX?&?VE_OC07*qoM6N<$f_@u2jQ{`u diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d GIT binary patch literal 750 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 literal 0 HcmV?d00001 diff --git a/assets/icons/Passport/passport_happy1_46x49_sfw.png b/assets/icons/Passport/passport_happy1_46x49_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..56ea000cd03bf445ff64ac2a9c01318a4ed280e3 GIT binary patch literal 1296 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 MT&7n)()IlKe@XVQIsgCw literal 0 HcmV?d00001 diff --git a/assets/icons/Passport/passport_okay_46x49.png b/assets/icons/Passport/passport_okay2_46x49_sfw.png similarity index 100% rename from assets/icons/Passport/passport_okay_46x49.png rename to assets/icons/Passport/passport_okay2_46x49_sfw.png diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..e65da5b0e586ab706b40263b637f0fd3647ac5d6 GIT binary patch literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..2528ebc95d79335975511a384c70c010d476a8c0 100644 GIT binary patch literal 4862 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 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index 380a970d9004cba5520560fd9aa24aa42924e2a1..fef503263fd962534e7e5543954612bf6c1faefd 100644 GIT binary patch literal 4882 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 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..380a970d9004cba5520560fd9aa24aa42924e2a1 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 0 HcmV?d00001 diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 34199910945376f054daa0c1738d7e64dc410421..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 100644 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 2681 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 M07*qoM6N<$g8AuxBLDyZ literal 3898 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< literal 0 HcmV?d00001 diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png new file mode 100644 index 0000000000000000000000000000000000000000..137d4c4d054227f27ad6a7e3e374b100d975d9af GIT binary patch literal 1690 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 literal 0 HcmV?d00001 diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png index ec785948d035b717944bc41811f73d190e6fc3bd..a48c5330e85c2135866fe99bcb6f3534d06c6d53 100644 GIT binary patch literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/SubGhz/Scanning_123x52_sfw.png b/assets/icons/SubGhz/Scanning_123x52_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..ec785948d035b717944bc41811f73d190e6fc3bd GIT binary patch literal 1690 zcmaJ?X;2eq7>)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo literal 0 HcmV?d00001 diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index 40f094ac9ba758ea06838c2bb8376cf6f28f8050..dd220bb65104666cb19752a96fe716cc44dffb68 100644 GIT binary patch delta 1860 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 diff --git a/assets/icons/U2F/Auth_62x31_sfw.png b/assets/icons/U2F/Auth_62x31_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..40f094ac9ba758ea06838c2bb8376cf6f28f8050 GIT binary patch literal 3761 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 literal 0 HcmV?d00001 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 68c48c0e68142548919d6a4b02e40b48a243b04e..495e8ab55c1e2dabb547151c2319f321f4af13bb 100644 GIT binary patch delta 1891 zcmV-p2b}n~9p?^^8Gix*001yk>3{$L010qNS#tmY4#NNd4#NS*Z>VGd00$XKL_t(| z+O?QlOjSn|hG8oRh>GGZ;swMCiVCP$2m(U5h>bOddI{B*1Oy&HNv)*;Bx*FKeZkhI zB-%dM1e>&xrj7K$)`!xj)v9R=_C!b_pdvRBLEK z*T2@F<>ch7UAs0lHPyw%W%1(0vu4fWapugKPEJnFFPgqDO&e_bx7Dv2om?gSDrp=gE^NdIBg8sLrZYs~}Qxax$H$sHm{8u)Msypr9a30==5a zBGSKg>sDb`!T3UNZ*QM6WyTNU-=G)7 zYFilOUQ<&uzR)2$tR2p2fv%WSS69dWba!{Vy1F(sHO-wnH}-0rkB?7qa4qP>Z+C&CQU9LAEP4HkJ-sfu~_V z#xQ|tn194ZjmD`;NvwJutRN6S306k%AA?JmE}b)Hj-En*yuA3Bc%7INGuVyUvu8hk{8$kQU-Am( z?A*E2_gg=;M0|WaQV3$W#INShpO3-F6#Y?pU4PvzLq~%F#K&+OlmP3qXU}jjzI^!b zA$^&LNz6p`=|o0Gp7kkvzjc3J)4r_#GQ-2enNI-Rym|B5wQC_EAp|r(ApnMEhL_if z=x!A{+yE273SaRP(sAn4DUvI=MK`erNnvN0jbX=+A4ea3eSO!hTNfG{x^?T;O`A3y zK7V`|BZc~%ot>gEBxXHs!d_jr(F4~kaauG$x8h zK>$3BB_uB%OG`^3dQnl)>C>m*ZA;r&o$Oz}hCH|~m=%8D%x3@Q9 zpPQQt{n)^a&OM4J` z;6QeEHXMujbHnhp3SEo72>3GouLSlGo6QwtC}J}Ty$W=pIQU=-noS;AxpHMfLIO6d z|0623>1_xD6DU`hom90k;qSx~KA+fL6xN~fX1L9`J_cS#1c6SZQuHAa7cN{VDJcPc zMMZ_ofH~|5l2?`VkAsU6B)gh-TYnW0=P}|s&?V!b*^*Pr%F2!%I|jLB1}uWRk-x8( znRZ*{>%ZWIQQ~%}hJg%x34G)~qysTQ4ats3d#UH=KL%AGc38#KfFA-K`Re)e=T#yz zqqu{(Rq3Mc7IUgb{Pp(s!U{M8ttp}KG4_}W?!rRu;MPNN1iG9;%oJvP5=&i3>3!?xNjc8YH;|2Z^PqxcysetUaZ|03GL9}00m)2BnQ5|^li z6l6#^jwh8`gsZNBk4%#(X<}kxEO5X$;u=O6-_c_oI#~I0_wHSMOqimNkej+dk>p%> zIRQXX@R|Eb!yEtVe+*o8@qZZhjj4!Vwrp8jTbnIF4l~|P@K<-eW^aB&TOHKn)K_}uPy*Tkbhhb?3!P=Xj2!XFf!3Ut%seSt+u^c>?GJv9N2wf(2uK d6tdy_{{j6OA(WAZ17rXI002ovPDHLkV1l_ti)jD= literal 3767 zcmaJ@c|26@-#)fNSrR5BW5iQgW`^N*QF&1S0K}{@ z<~ZJH#Cw#41$ggBZpK#Rpm%ERm8u{5b*UVy<_v)2fkBAW_%^;c9MGWU&>#&o z>;fL!KlKm=5&^)Ebepnj`0o?@&{UD$_XLo@x5X}dq?z7sYz;sNp2Fq#WtcmM%+rCm%F7GdQd~{MxVBi~!m%=_xV-$w*08*@+n!uxZ^6 z0P@T2uQv3IU{{kT8g=4|-R&>%+2vjZCyH~0ks$)j!DUboTFE&+Ny-{XL4 z-+~iBuM^-su>Cx=2Eh7{I<_S9A3F)@K~Qny~MD{88fgjzGX9Y+mz* z{I+;N(n|-yS^|KC>g8Aybv~daCGQIWRD9ia@_r)7rcM|D%(E{Xcx<|L`zvX7jd;c@ z#flmbKVgfx$@aZ9hLUDmM#6WC-r1FGD(6sx>=u0XSU^5|N7|5N$t$>?*!ZB-$qSQo z(3aD?vR?11xFRBG+914>pp1;%;U3csX%z;g#t1?P7@eEpdEkji>0^+$4U9bg0yCd@ zSB%P>CPP|3`lQK9TweniaeeK2g%v#U!joeHANT`IUffrbzh0N^-_F#La1Bq+@okqc z*@Fsyo|E5x;`BAVyjs01aTKrX6st>A#2*TjQK-ox zaE15sM}_a45%Slg_w7nlL!_2#gWZwE zad=C%+IQtW%2}$X{u1BmmVntS-gtB5sHn1P9Kw2=wJ)X*qnT%)XVf7{&ahFNjH5Ju z9ua;L&IYH%m&QrN+1*@id^1uijeHw9Y$KAh7nhePby$~VJSuqiHo`15`@yl6f~*h1 z4|!|>CzsHly@l|zF)Ua zj89zp5wud&EcV=#KTigq4z8upJKlhh=9XLKwtjJiDE8z^gk|;>Os)g5v`#KsS+)*An#G%SF`NV@HGFT`x}SKmJY^Fo zw5kvYJ!@l+_J82X_Kf$;96Qs(Ctg=PIHTW_aky2vRjl=_rhulgW^tEKp=V)O;i2A; z-l|^rX~wkd^kgqLfSzfRsgb#Wq#&09M9L4AA1jY2?+a-AX7rvo$(j&ZXj%}T_|$H- zK>3lo0Q$!5;k(&~Lm`)jx9+r;X-?4wep4tnaTALD`N}Jj;t-nZER9&PjwkbC6 zmQw68U>clbFaw$c{pb~wc|$sH3Tg}9OgMWfc-`^OoYw{2I}*o{N(oE>*;Deb=TmQo zaFRylzn6DBd%uvI{xZG4u!r*DQ#0yq=)+Z8t#pM(*EcAx>5rKu{P3CPFPZu2`LE~W z#-XPIPr000t=wbxG(#<`3r1<^t35GT$vJx(X@nxJv`z>Z)fw^M4!M1`>RuIO09B;G%^{gdEE?_4c>x-m zXys%D8M^4Y#GTWNxFwbH#CY@)*bEEZWjC5nwN>0XcR4-6&Y~(OI_Fq%Y1v`Jn`4-dF4_1M0Lz>~QL=c)6P|ROW4+%)36b+j`FQXzC|sBxdB|#K_4d>K|dH z`%3LR)zuTkMp$E%CWVBnO-myx%l`9yGJX*@?YSkmBCNQq4fMWx@QGa~g3B>ef6<6*NOrGfo<#=Q6yu=fy86${2j+ zCQem6QxA6bt+WVTpmuz}_$gN2U7@^sEbLR6Rgm4=tk|=6&u(3`;7IlaPQpNFW9elTHo})X&Fd8U%DVXiD9XC zAgcq|nz@v0)SXK1*BiN+il-E44j zM8DhgyN1LDKU%BmUH`UqMdI7}l8s_g4Q24>>6+7RZ(Jm};v!eGHWGimzdF=m)oj(N zVyO}xo2jIvASPcq=h2!ll>a1uXIoR4(=XPf|HJWt_2JbcZTPkBnQhBaZPi7rf_Vn_ zVZkG0y-K}OMJo$6dvC3{^U z4DluEdq9khz=pv{o&bf!B!Gh{epG*CFbeXwE|NFjG(#cazadOt6y!fa;cc;CG>t(5 zAJT$r5@9eH_>jI9$%BA!*Y+e3HNXfM0uF`q?n9dJLr8=+60Q&a=YsGYGCVwyICIN? z-0@Z@h&Pi-M?#@NK|xwU+FCS*7Zk3quMdSGpa_H}525MLqB04=npA(4-wNg=er+JL*ZJm&8&U{ZEgR5D24Jb+MkId z{WsqKQ`p~uMJGXVB!5}}gUH)EPnAtqbR?QVA~0zT2O7=qcOkLfG$zg8n??sCw6);i zeYON5nYwA&|2M_f7HLiOXA-DHlC?Ps!V}RVlRc0y3sbbEzApM8OdAe|TcAx3>FVlR zAP(wUSenCN+WNn_<}_jeg+yik=6d{>YyL;>Wy=U z(?9C{!}a)cEM|Y?LV3YJH~02`?e%XHuYoqF|28jg@o)Q+sJxzM@S0mWr*npPFt%WA z94vla-#jDb3*|80P-|^&>JZ$^PC6k~3lp=Yhi@z z`51nv&OpWmcVI2xti+M&Q!C)GhLRd3BUbIfdT38#i4Z#bTk!`2$?ZoTJ;U-8%n|y4g+cwH(>YR7vd$GeGzYI#^JouGlr|_w0cCP2=dVlPi aN(~_Yif=!tN`^N*QF&1S0K}{@ z<~ZJH#Cw#41$ggBZpK#Rpm%ERm8u{5b*UVy<_v)2fkBAW_%^;c9MGWU&>#&o z>;fL!KlKm=5&^)Ebepnj`0o?@&{UD$_XLo@x5X}dq?z7sYz;sNp2Fq#WtcmM%+rCm%F7GdQd~{MxVBi~!m%=_xV-$w*08*@+n!uxZ^6 z0P@T2uQv3IU{{kT8g=4|-R&>%+2vjZCyH~0ks$)j!DUboTFE&+Ny-{XL4 z-+~iBuM^-su>Cx=2Eh7{I<_S9A3F)@K~Qny~MD{88fgjzGX9Y+mz* z{I+;N(n|-yS^|KC>g8Aybv~daCGQIWRD9ia@_r)7rcM|D%(E{Xcx<|L`zvX7jd;c@ z#flmbKVgfx$@aZ9hLUDmM#6WC-r1FGD(6sx>=u0XSU^5|N7|5N$t$>?*!ZB-$qSQo z(3aD?vR?11xFRBG+914>pp1;%;U3csX%z;g#t1?P7@eEpdEkji>0^+$4U9bg0yCd@ zSB%P>CPP|3`lQK9TweniaeeK2g%v#U!joeHANT`IUffrbzh0N^-_F#La1Bq+@okqc z*@Fsyo|E5x;`BAVyjs01aTKrX6st>A#2*TjQK-ox zaE15sM}_a45%Slg_w7nlL!_2#gWZwE zad=C%+IQtW%2}$X{u1BmmVntS-gtB5sHn1P9Kw2=wJ)X*qnT%)XVf7{&ahFNjH5Ju z9ua;L&IYH%m&QrN+1*@id^1uijeHw9Y$KAh7nhePby$~VJSuqiHo`15`@yl6f~*h1 z4|!|>CzsHly@l|zF)Ua zj89zp5wud&EcV=#KTigq4z8upJKlhh=9XLKwtjJiDE8z^gk|;>Os)g5v`#KsS+)*An#G%SF`NV@HGFT`x}SKmJY^Fo zw5kvYJ!@l+_J82X_Kf$;96Qs(Ctg=PIHTW_aky2vRjl=_rhulgW^tEKp=V)O;i2A; z-l|^rX~wkd^kgqLfSzfRsgb#Wq#&09M9L4AA1jY2?+a-AX7rvo$(j&ZXj%}T_|$H- zK>3lo0Q$!5;k(&~Lm`)jx9+r;X-?4wep4tnaTALD`N}Jj;t-nZER9&PjwkbC6 zmQw68U>clbFaw$c{pb~wc|$sH3Tg}9OgMWfc-`^OoYw{2I}*o{N(oE>*;Deb=TmQo zaFRylzn6DBd%uvI{xZG4u!r*DQ#0yq=)+Z8t#pM(*EcAx>5rKu{P3CPFPZu2`LE~W z#-XPIPr000t=wbxG(#<`3r1<^t35GT$vJx(X@nxJv`z>Z)fw^M4!M1`>RuIO09B;G%^{gdEE?_4c>x-m zXys%D8M^4Y#GTWNxFwbH#CY@)*bEEZWjC5nwN>0XcR4-6&Y~(OI_Fq%Y1v`Jn`4-dF4_1M0Lz>~QL=c)6P|ROW4+%)36b+j`FQXzC|sBxdB|#K_4d>K|dH z`%3LR)zuTkMp$E%CWVBnO-myx%l`9yGJX*@?YSkmBCNQq4fMWx@QGa~g3B>ef6<6*NOrGfo<#=Q6yu=fy86${2j+ zCQem6QxA6bt+WVTpmuz}_$gN2U7@^sEbLR6Rgm4=tk|=6&u(3`;7IlaPQpNFW9elTHo})X&Fd8U%DVXiD9XC zAgcq|nz@v0)SXK1*BiN+il-E44j zM8DhgyN1LDKU%BmUH`UqMdI7}l8s_g4Q24>>6+7RZ(Jm};v!eGHWGimzdF=m)oj(N zVyO}xo2jIvASPcq=h2!ll>a1uXIoR4(=XPf|HJWt_2JbcZTPkBnQhBaZPi7rf_Vn_ zVZkG0y-K}OMJo$6dvC3{^U z4DluEdq9khz=pv{o&bf!B!Gh{epG*CFbeXwE|NFjG(#cazadOt6y!fa;cc;CG>t(5 zAJT$r5@9eH_>jI9$%BA!*Y+e3HNXfM0uF`q?n9dJLr8=+60Q&a=YsGYGCVwyICIN? z-0@Z@h&Pi-M?#@NK|xwU+FCS*7Zk3quMdSGpa_H}525MLqB04=npA(4-wNg=er+JL*ZJm&8&U{ZEgR5D24Jb+MkId z{WsqKQ`p~uMJGXVB!5}}gUH)EPnAtqbR?QVA~0zT2O7=qcOkLfG$zg8n??sCw6);i zeYON5nYwA&|2M_f7HLiOXA-DHlC?Ps!V}RVlRc0y3sbbEzApM8OdAe|TcAx3>FVlR zAP(wUSenCN+WNn_<}_jeg+yik=6d{>YyL;>Wy=U z(?9C{!}a)cEM|Y?LV3YJH~02`?e%XHuYoqF|28jg@o)Q+sJxzM@S0mWr*npPFt%WA z94vla-#jDb3*|80P-|^&>JZ$^PC6k~3lp=Yhi@z z`51nv&OpWmcVI2xti+M&Q!C)GhLRd3BUbIfdT38#i4Z#bTk!`2$?ZoTJ;U-8%n|y4g+cwH(>YR7vd$GeGzYI#^JouGlr|_w0cCP2=dVlPi aN(~_Yif=!tN3{$L010qNS#tmY4#NNd4#NS*Z>VGd00#s~L_t(| z+P#=tOjK7ChM7@rii+Ybf(l{|ilJ%7MltmDqf%s%_<+2>zt z|Lb3Ck1;PVZ{50e>FMdt&dy7gESWQB4!5&s&vtThGCXVAzBJ=#(>~?X!^30pYhA#a_iQuo}QlO=H`3%?g<*zMn*=o{o&EZ9Qq1< ze}BK2G&D4H{eSv(CkHoUM@NUrWNK(=;AU`e5CiVsy*o16w4hyGT?Ht8TswXG^qDhf z(r2%eZ_uQgU)Kov5g&u&}U#f`Xu+AWQ;oNP?%%(^c=6(ZfPVmgfB&gdr%stN1p_v3-VDD% zFNoE)Fvw?peZ6C$Lv&a>meT@VF{iPyk^SlI?R9Z+X>DztKYxDgl{jy2@8IBIf|6Sa z06wvV5pld@p~G5{1%W7@W5nIv-Te3&0YJkD)Z%GdTN~tIknM_%jitj@;Az;8F-%|@ zCb3bDg?|oK5Qv`yDn>FLSK#OuVIn89w$ojdo@qeqHJ_>xC3CoL___iI13M0|WaQV3$W z#IJmOd@vZ9qVHJfXfS|y8E%6TV14@ZDGtV$4}Ts!pfB?;`t|EWLqoT2-I|h;a_G<@j1=m3cXx}zkeIc&343*oi{4mh z;D53MIxJAC*sl2TY$h>@@h7equvXyQbtyyK%E{&2*0^yZ+%mlB=44+pP29mIn* zYu3O4grcvnkDUQM$b=G7qWqBfqjsSKZhxg-US5cOettgmV*|6gcPpM)$5Chix7`XO zu}BAw!VtVw<$spIC3nJOf{*dIxVWOCqQJmFTnu=tCmTpFdw-UJm-|>S~z*bJ!Ck zuQur~8@G}m+1a*hqNfJi;t(6uHGjU>fi4*b&6b=}Sy_4H$PvgbGhh+ijr?_k%(U8S zUjGR%yZ~AxF}K2M->1`{b91pJl>Zmr`qLY6$MVg9Vf>bY4nXYk$S-QlLT=5c3IQ zQAw&1NFtNu1V{r}X=y2y!i5VL_Uze1u@zDoyy4qO5*hNUqVr7XmlNgD7fAjSRc1zs zMX*9{ssIigRAaYq-)H#IexH*|QIgdhbvdCfw)70|-koJW9k!5_KnY@82!Bv?D%c%Ywj-3fpP5W1ImO9oQ2j$@K~=1l0Cv~wI9MnF zdO#qK9NvIJhop!{NvP^@E$x_dIZ6+sa5<0oq5SFB)z$TXB1h8hT#I+5$Gf3Qb8|B~ zgTf$ioQbG)0fZ&HYIi;_uxe^Vb~sM}ogfx=EnK+J=@qtr0rka475C4CBme*a07*qo IM6N<$g6Xe=bpQYW literal 3765 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 literal 3751 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 W#^eyOwOvyH0000ZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/assets/icons/iButton/DolphinMafia_115x62_sfw.png b/assets/icons/iButton/DolphinMafia_115x62_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..66fdb40ff2651916faed4a2ae1d564cafdbf7bcb GIT binary patch literal 2504 zcmbVO3se(V8ji5Kg5ZmV3I#ewZHbsZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj literal 0 HcmV?d00001 diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index a299d3630239b4486e249cc501872bed5996df3b..43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d 100644 GIT binary patch literal 5422 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QM|5y* 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 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png new file mode 100644 index 0000000000000000000000000000000000000000..423e079199b00df0d910981caf8944cbaf8ee67e GIT binary patch literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 0 HcmV?d00001 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..90b589ff8e9f18b28d7fd75ca525a1db24381b06 100644 GIT binary patch literal 4719 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 literal 2157 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 Og3&$z^CF>$-U|nYD9Y9V diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm deleted file mode 100644 index 843aed27c7df836480f795cd649be62ae08f6ad7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm deleted file mode 100644 index ad87a307189f29491e693d9665e9c9780584f1ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm deleted file mode 100644 index 78de0389efdf70991b33b41f855675bea8b3248f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm deleted file mode 100644 index 32bb893cae287bcce9d3d5d3590bc1cd890ac96d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm deleted file mode 100644 index d39d7c7095b350f6fe85554e7246e86cb2d6a0f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm deleted file mode 100644 index c3bea7837cc3042b11306dc8c2a4b2e122cc786b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm deleted file mode 100644 index 83fb1ac60079bf92019ad0610c834fe7e662d02a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm deleted file mode 100644 index d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm deleted file mode 100644 index b4a87c26bb2d04806872f9dbd425a6c92a58db9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm deleted file mode 100644 index b73177b87d59022dd64c4bdc1dfb43378fadddcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm deleted file mode 100644 index feb5ab082ec1d3388d93a6ea6290666f20dcd358..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta deleted file mode 100644 index 485a421b6e4585f9e7e41e4cc628e815074fdb04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx deleted file mode 100644 index 4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx deleted file mode 100644 index 9dc1e5dcf52f7721ffe6f002905111825fcda8c9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx deleted file mode 100644 index 03c6304a2d4d334ac519d1861981fcfd0393755f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx deleted file mode 100644 index 8af359b940e9a14587bb8ae598aeff8e53b6542a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx deleted file mode 100644 index 715815f0cad52a4138cb8ff3db6a9fa15e4a2b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx deleted file mode 100644 index 715815f0cad52a4138cb8ff3db6a9fa15e4a2b46..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx deleted file mode 100644 index cb92216c4e5943d4d845c979c7d8c59df040b95b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx deleted file mode 100644 index 8932c5ed3ba499c5a9259d2d228330e5f4a5e94d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx deleted file mode 100644 index 3eefd86bc994996073b05eee2f627dba8b8bb9cf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx deleted file mode 100644 index 22a76dd4daaa01bda8be01f522bf58c0a9e22e2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx deleted file mode 100644 index 75f57367cf539f9f075e06c983de83f6fb826cbd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx deleted file mode 100644 index 4a569c1eb27507a1301fa34d2ec5d96d62fe43be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx deleted file mode 100644 index 582247d43e0c1cfbed6e9d8061ec26b7ad417339..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx deleted file mode 100644 index fae05bb57f5b883c6fd532a150899d556ce750cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx deleted file mode 100644 index c91714b3f1114d842e45cb614c456d3b40b164c5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx deleted file mode 100644 index c37cd533d7a3df21e738cf4cbffb4483f9a551ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx deleted file mode 100644 index 47b611a6ad2b3c2fa21cda23b155e56742d70c77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp bytes: return data -def pack_anim(src: pathlib.Path, dst: pathlib.Path): - if not (src / "meta.txt").is_file(): - return - dst.mkdir(parents=True, exist_ok=True) - for frame in src.iterdir(): - if not frame.is_file(): - continue - if frame.name == "meta.txt": - shutil.copyfile(src / "meta.txt", dst / "meta.txt") - continue - elif frame.name.startswith("frame_"): - (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) - - -def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): - if not (src / "frame_rate").is_file(): - return - dst.mkdir(parents=True, exist_ok=True) - frame_count = 0 - frame_rate = None - size = None - for frame in src.iterdir(): - if not frame.is_file(): - continue - if frame.name == "frame_rate": - frame_rate = int((src / "frame_rate").read_text()) - continue - elif frame.name.startswith("frame_"): - frame_count += 1 - if not size: - size = Image.open(frame).size - (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) - (dst / "meta").write_bytes(struct.pack(" Date: Fri, 10 Feb 2023 20:04:45 +0100 Subject: [PATCH 161/231] Adds LED state report characteristic into bt GATT hid service (for now writing by host isn't working) --- applications/main/bad_kb/bad_kb_script.c | 2 +- firmware/targets/f7/api_symbols.csv | 3 +- firmware/targets/f7/ble_glue/hid_service.c | 105 +++++++++++++++++- firmware/targets/f7/ble_glue/hid_service.h | 4 + .../targets/f7/furi_hal/furi_hal_bt_hid.c | 75 ++++++++++++- .../furi_hal_include/furi_hal_bt_hid.h | 6 + 6 files changed, 185 insertions(+), 10 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index f27e1a268..9cf989eb8 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -243,7 +243,7 @@ static bool ducky_is_line_end(const char chr) { static void ducky_numlock_on(BadKbScript* bad_kb) { if(bad_kb->bt) { - if((furi_hal_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME + if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); furi_delay_ms(bt_timeout); diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 95bcce708..a8180d19e 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,13.5,, +Version,v,13.9,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1015,6 +1015,7 @@ Function,+,furi_hal_bt_get_transmitted_packets,uint32_t, Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, +Function,+,furi_hal_bt_hid_get_led_state,uint8_t, Function,+,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index 47d242d4d..31eeb03da 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -14,10 +14,36 @@ typedef struct { uint16_t report_map_char_handle; uint16_t info_char_handle; uint16_t ctrl_point_char_handle; + // led state + uint16_t led_state_char_handle; + uint16_t led_state_desc_handle; + HidLedStateEventCallback led_state_event_callback; + void* led_state_ctx; } HIDSvc; static HIDSvc* hid_svc = NULL; +// #define N_BYTE_PER_LINE 16 +// static void hexdump(uint8_t* data, uint32_t len) { +// uint32_t n_line = len / N_BYTE_PER_LINE + 1; +// char line[len * 3 + n_line + 1]; +// memset(line, 0, sizeof(line)); +// uint32_t i; +// for(i = 0; i < len; i++) { +// if(i % N_BYTE_PER_LINE == 0) { +// if(i != 0) { +// FURI_LOG_D(TAG, "%s", line); +// } +// memset(line, 0, sizeof(line)); +// } +// uint32_t line_len = strlen(line); +// snprintf(line + line_len, sizeof(line) - line_len, "%02X ", data[i]); +// } +// if(strlen(line) > 0) { +// FURI_LOG_D(TAG, "%s", line); +// } +// } + static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); @@ -30,6 +56,27 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { } else if(blecore_evt->ecode == ACI_GATT_SERVER_CONFIRMATION_VSEVT_CODE) { // Process notification confirmation ret = SVCCTL_EvtAckFlowEnable; + } else if(blecore_evt->ecode == ACI_GATT_WRITE_PERMIT_REQ_VSEVT_CODE) { + // Process write request + aci_gatt_write_permit_req_event_rp0* req = + (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; + + // FURI_LOG_I(TAG, "GATT write request"); + // size_t len = 2 + event_pckt->plen; + // hexdump((uint8_t*)event_pckt, len); + // FURI_LOG_D(TAG, "conn_handle = %04x", req->Connection_Handle); + // FURI_LOG_D(TAG, "attr handle = %04x", req->Attribute_Handle); + // FURI_LOG_D(TAG, "led char handle = %04x", hid_svc->led_state_char_handle); + // FURI_LOG_D(TAG, "led state = %02x", req->Data[0]); + + furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); + + // this check is likely to be incorrect, it will actually work in our case + // but we need to investigate gatt api to see what is the rules + // that specify attibute handle value from char handle (or the reverse) + if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { + hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + } } } return ret; @@ -55,8 +102,8 @@ void hid_svc_start() { PRIMARY_SERVICE, 2 + /* protocol mode */ (4 * HID_SVC_INPUT_REPORT_COUNT) + (3 * HID_SVC_OUTPUT_REPORT_COUNT) + - (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + - 2, /* Service + Report Map + HID Information + HID Control Point */ + (3 * HID_SVC_FEATURE_REPORT_COUNT) + 1 + 2 + 2 + 2 + + 4, /* Service + Report Map + HID Information + HID Control Point + LED state */ &hid_svc->svc_handle); if(status) { FURI_LOG_E(TAG, "Failed to add HID service: %d", status); @@ -198,6 +245,44 @@ void hid_svc_start() { } } #endif + // add led state output report + char_uuid.Char_UUID_16 = REPORT_CHAR_UUID; + status = aci_gatt_add_char( + hid_svc->svc_handle, + UUID_TYPE_16, + &char_uuid, + 1, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + ATTR_PERMISSION_NONE, + GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, + 10, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_char_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state characteristic: %d", status); + } + + // add led state char descriptor specifying it is an output report + uint8_t buf[2] = {HID_SVC_REPORT_COUNT + 1, 2}; + desc_uuid.Char_UUID_16 = REPORT_REFERENCE_DESCRIPTOR_UUID; + status = aci_gatt_add_char_desc( + hid_svc->svc_handle, + hid_svc->led_state_char_handle, + UUID_TYPE_16, + &desc_uuid, + HID_SVC_REPORT_REF_LEN, + HID_SVC_REPORT_REF_LEN, + buf, + ATTR_PERMISSION_NONE, + ATTR_ACCESS_READ_WRITE, + GATT_DONT_NOTIFY_EVENTS, + MIN_ENCRY_KEY_SIZE, + CHAR_VALUE_LEN_CONSTANT, + &(hid_svc->led_state_desc_handle)); + if(status) { + FURI_LOG_E(TAG, "Failed to add led state descriptor: %d", status); + } + // Add Report Map characteristic char_uuid.Char_UUID_16 = REPORT_MAP_CHAR_UUID; status = aci_gatt_add_char( @@ -247,6 +332,9 @@ void hid_svc_start() { if(status) { FURI_LOG_E(TAG, "Failed to add control point characteristic: %d", status); } + + hid_svc->led_state_event_callback = NULL; + hid_svc->led_state_ctx = NULL; } bool hid_svc_update_report_map(const uint8_t* data, uint16_t len) { @@ -288,6 +376,15 @@ bool hid_svc_update_info(uint8_t* data, uint16_t len) { return true; } +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context) { + furi_assert(hid_svc); + furi_assert(callback); + furi_assert(context); + + hid_svc->led_state_event_callback = callback; + hid_svc->led_state_ctx = context; +} + bool hid_svc_is_started() { return hid_svc != NULL; } @@ -320,6 +417,10 @@ void hid_svc_stop() { if(status) { FURI_LOG_E(TAG, "Failed to delete Control Point characteristic: %d", status); } + status = aci_gatt_del_char(hid_svc->svc_handle, hid_svc->led_state_char_handle); + if(status) { + FURI_LOG_E(TAG, "Failed to delete led state characteristic: %d", status); + } // Delete service status = aci_gatt_del_service(hid_svc->svc_handle); if(status) { diff --git a/firmware/targets/f7/ble_glue/hid_service.h b/firmware/targets/f7/ble_glue/hid_service.h index 723460d49..40d973340 100644 --- a/firmware/targets/f7/ble_glue/hid_service.h +++ b/firmware/targets/f7/ble_glue/hid_service.h @@ -15,6 +15,8 @@ #define HID_SVC_REPORT_COUNT \ (HID_SVC_INPUT_REPORT_COUNT + HID_SVC_OUTPUT_REPORT_COUNT + HID_SVC_FEATURE_REPORT_COUNT) +typedef uint16_t (*HidLedStateEventCallback)(uint8_t state, void* ctx); + void hid_svc_start(); void hid_svc_stop(); @@ -26,3 +28,5 @@ bool hid_svc_update_report_map(const uint8_t* data, uint16_t len); bool hid_svc_update_input_report(uint8_t input_report_num, uint8_t* data, uint16_t len); bool hid_svc_update_info(uint8_t* data, uint16_t len); + +void hid_svc_register_led_state_callback(HidLedStateEventCallback callback, void* context); \ No newline at end of file diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 424999fc0..307b0ac83 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -20,6 +20,7 @@ enum HidReportId { ReportIdKeyboard = 1, ReportIdMouse = 2, ReportIdConsumer = 3, + ReportIdLEDState = 4, }; // Report numbers corresponded to the report id with an offset of 1 enum HidInputNumber { @@ -63,12 +64,6 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = { HID_REPORT_COUNT(1), HID_REPORT_SIZE(8), HID_INPUT(HID_IOF_CONSTANT | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), - HID_USAGE_PAGE(HID_PAGE_LED), - HID_REPORT_COUNT(8), - HID_REPORT_SIZE(1), - HID_USAGE_MINIMUM(1), - HID_USAGE_MAXIMUM(8), - HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_REPORT_COUNT(FURI_HAL_BT_HID_KB_MAX_KEYS), HID_REPORT_SIZE(8), HID_LOGICAL_MINIMUM(0), @@ -77,6 +72,13 @@ static const uint8_t furi_hal_bt_hid_report_map_data[] = { HID_USAGE_MINIMUM(0), HID_USAGE_MAXIMUM(101), HID_INPUT(HID_IOF_DATA | HID_IOF_ARRAY | HID_IOF_ABSOLUTE), + HID_REPORT_ID(ReportIdLEDState), + HID_USAGE_PAGE(HID_PAGE_LED), + HID_REPORT_COUNT(8), + HID_REPORT_SIZE(1), + HID_USAGE_MINIMUM(1), + HID_USAGE_MAXIMUM(8), + HID_OUTPUT(HID_IOF_DATA | HID_IOF_VARIABLE | HID_IOF_ABSOLUTE), HID_END_COLLECTION, // Mouse Report HID_USAGE_PAGE(HID_PAGE_DESKTOP), @@ -125,6 +127,62 @@ FuriHalBtHidKbReport* kb_report = NULL; FuriHalBtHidMouseReport* mouse_report = NULL; FuriHalBtHidConsumerReport* consumer_report = NULL; +typedef struct { +// shortcuts +#define s_undefined data.bits.b_undefined +#define s_num_lock data.bits.b_num_lock +#define s_caps_lock data.bits.b_caps_lock +#define s_scroll_lock data.bits.b_scroll_lock +#define s_compose data.bits.b_compose +#define s_kana data.bits.b_kana +#define s_power data.bits.b_power +#define s_shift data.bits.b_shift +#define s_value data.value + union { + struct { + uint8_t b_undefined : 1; + uint8_t b_num_lock : 1; + uint8_t b_caps_lock : 1; + uint8_t b_scroll_lock : 1; + uint8_t b_compose : 1; + uint8_t b_kana : 1; + uint8_t b_power : 1; + uint8_t b_shift : 1; + } bits; + uint8_t value; + } data; +} __attribute__((__packed__)) FuriHalBtHidLedState; + +FuriHalBtHidLedState hid_host_led_state = {.s_value = 0}; + +uint16_t furi_hal_bt_hid_led_state_cb(uint8_t state, void* ctx) { + FuriHalBtHidLedState* led_state = (FuriHalBtHidLedState*)ctx; + + FURI_LOG_D("HalBtHid", "LED state updated !"); + + led_state->s_value = state; + + return 0; +} + +uint8_t furi_hal_bt_hid_get_led_state(void) { + FURI_LOG_D( + "HalBtHid", + "LED state: RFU=%d NUMLOCK=%d CAPSLOCK=%d SCROLLLOCK=%d COMPOSE=%d KANA=%d POWER=%d SHIFT=%d", + hid_host_led_state.s_undefined, + hid_host_led_state.s_num_lock, + hid_host_led_state.s_caps_lock, + hid_host_led_state.s_scroll_lock, + hid_host_led_state.s_compose, + hid_host_led_state.s_kana, + hid_host_led_state.s_power, + hid_host_led_state.s_shift); + + return (hid_host_led_state.s_value >> 1); // bit 0 is undefined (after shift bit location + // match with HID led state bits defines) + // see bad_kb_script.c (ducky_numlock_on function) +} + void furi_hal_bt_hid_start() { // Start device info if(!dev_info_svc_is_started()) { @@ -139,6 +197,8 @@ void furi_hal_bt_hid_start() { hid_svc_start(); } + hid_svc_register_led_state_callback(furi_hal_bt_hid_led_state_cb, &hid_host_led_state); + kb_report = malloc(sizeof(FuriHalBtHidKbReport)); mouse_report = malloc(sizeof(FuriHalBtHidMouseReport)); consumer_report = malloc(sizeof(FuriHalBtHidConsumerReport)); @@ -161,6 +221,9 @@ void furi_hal_bt_hid_stop() { furi_assert(kb_report); furi_assert(mouse_report); furi_assert(consumer_report); + + hid_svc_register_led_state_callback(NULL, NULL); + // Stop all services if(dev_info_svc_is_started()) { dev_info_svc_stop(); diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h index e7b40f079..b787c7c3e 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h @@ -95,6 +95,12 @@ bool furi_hal_bt_hid_consumer_key_release_all(); */ bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots); +/** Retrieves LED state from remote BT HID host + * + * (look at HID usage page to know what each bit of the returned byte means) +*/ +uint8_t furi_hal_bt_hid_get_led_state(void); + #ifdef __cplusplus } #endif From 176c01c0da6f39a80f72f8d802ec5c5df9be2c5a Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Fri, 10 Feb 2023 21:16:40 +0100 Subject: [PATCH 162/231] More subghz (btw.. entire subghz update, credits to unleashed. will be in release note too) --- firmware/targets/f7/api_symbols.csv | 6 + lib/subghz/protocols/alutech_at_4n.c | 303 +++++++++++++++++++++++---- lib/subghz/protocols/alutech_at_4n.h | 56 ++++- 3 files changed, 328 insertions(+), 37 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index cbe0ec2ed..d229192fd 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -2679,6 +2679,7 @@ Function,-,subghz_keystore_load,_Bool,"SubGhzKeystore*, const char*" Function,-,subghz_keystore_raw_encrypted_save,_Bool,"const char*, const char*, uint8_t*" Function,-,subghz_keystore_raw_get_data,_Bool,"const char*, size_t, uint8_t*, size_t" Function,-,subghz_keystore_save,_Bool,"SubGhzKeystore*, const char*, uint8_t*" +Function,-,subghz_protocol_alutech_at_4n_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_blocks_add_bit,void,"SubGhzBlockDecoder*, uint8_t" Function,+,subghz_protocol_blocks_add_bytes,uint8_t,"const uint8_t[], size_t" Function,+,subghz_protocol_blocks_add_to_128_bit,void,"SubGhzBlockDecoder*, uint8_t, uint64_t*" @@ -3039,6 +3040,11 @@ Function,-,subghz_protocol_decoder_star_line_free,void,void* Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_star_line_reset,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* +Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" +Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* +Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" diff --git a/lib/subghz/protocols/alutech_at_4n.c b/lib/subghz/protocols/alutech_at_4n.c index 6bcf9f25d..7849f7b21 100644 --- a/lib/subghz/protocols/alutech_at_4n.c +++ b/lib/subghz/protocols/alutech_at_4n.c @@ -34,6 +34,8 @@ struct SubGhzProtocolEncoderAlutech_at_4n { SubGhzProtocolBlockEncoder encoder; SubGhzBlockGeneric generic; + const char* alutech_at_4n_rainbow_table_file_name; + uint32_t crc; }; typedef enum { @@ -57,23 +59,77 @@ const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_alutech_at_4n_alloc, + .free = subghz_protocol_encoder_alutech_at_4n_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_alutech_at_4n_deserialize, + .stop = subghz_protocol_encoder_alutech_at_4n_stop, + .yield = subghz_protocol_encoder_alutech_at_4n_yield, }; const SubGhzProtocol subghz_protocol_alutech_at_4n = { .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, .decoder = &subghz_protocol_alutech_at_4n_decoder, .encoder = &subghz_protocol_alutech_at_4n_encoder, }; +void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderAlutech_at_4n* instance = + malloc(sizeof(SubGhzProtocolEncoderAlutech_at_4n)); + + instance->base.protocol = &subghz_protocol_alutech_at_4n; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 512; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + + instance->alutech_at_4n_rainbow_table_file_name = + subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); + if(instance->alutech_at_4n_rainbow_table_file_name) { + FURI_LOG_I( + TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); + } + + return instance; +} + +void subghz_protocol_encoder_alutech_at_4n_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + free(instance->encoder.upload); + free(instance); +} + +void subghz_protocol_encoder_alutech_at_4n_stop(void* context) { + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_alutech_at_4n_yield(void* context) { + SubGhzProtocolEncoderAlutech_at_4n* 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; +} + /** * Read bytes from rainbow table * @param file_name Full path to rainbow table the file @@ -164,37 +220,212 @@ static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* return data; } -// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { -// uint8_t* p = (uint8_t*)&data; -// uint32_t data1 = 0; -// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; -// uint32_t magic_data[] = { -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; +static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { + uint8_t* p = (uint8_t*)&data; + uint32_t data1 = 0; + uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; + uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; + uint32_t magic_data[] = { + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), + subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; -// do { -// data1 = data1 + magic_data[0]; -// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ -// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); -// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ -// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); -// } while(data1 != magic_data[5]); -// p[0] = (uint8_t)(data2 >> 24); -// p[1] = (uint8_t)(data2 >> 16); -// p[3] = (uint8_t)data2; -// p[4] = (uint8_t)(data3 >> 24); -// p[5] = (uint8_t)(data3 >> 16); -// p[2] = (uint8_t)(data2 >> 8); -// p[6] = (uint8_t)(data3 >> 8); -// p[7] = (uint8_t)data3; + do { + data1 = data1 + magic_data[0]; + data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ + ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); + data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ + ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); + } while(data1 != magic_data[5]); + p[0] = (uint8_t)(data2 >> 24); + p[1] = (uint8_t)(data2 >> 16); + p[3] = (uint8_t)data2; + p[4] = (uint8_t)(data3 >> 24); + p[5] = (uint8_t)(data3 >> 16); + p[2] = (uint8_t)(data2 >> 8); + p[6] = (uint8_t)(data3 >> 8); + p[7] = (uint8_t)data3; -// return data; -// } + return data; +} + +static bool subghz_protocol_alutech_at_4n_gen_data( + SubGhzProtocolEncoderAlutech_at_4n* instance, + uint8_t btn) { + UNUSED(btn); + + uint64_t data = subghz_protocol_blocks_reverse_key(instance->generic.data, 64); + + data = subghz_protocol_alutech_at_4n_decrypt( + data, instance->alutech_at_4n_rainbow_table_file_name); + uint8_t crc = data >> 56; + if(crc == subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF))) { + instance->generic.btn = (uint8_t)data & 0xFF; + instance->generic.cnt = (uint16_t)(data >> 8) & 0xFFFF; + instance->generic.serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; + } + + if(instance->generic.cnt < 0xFFFF) { + instance->generic.cnt++; + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + crc = subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)(instance->generic.cnt & 0xFF)); + data = (uint64_t)crc << 56 | (uint64_t)instance->generic.serial << 24 | + (uint32_t)instance->generic.cnt << 8 | instance->generic.btn; + + data = subghz_protocol_alutech_at_4n_encrypt( + data, instance->alutech_at_4n_rainbow_table_file_name); + crc = subghz_protocol_alutech_at_4n_crc(data); + instance->generic.data = subghz_protocol_blocks_reverse_key(data, 64); + instance->crc = subghz_protocol_blocks_reverse_key(crc, 8); + return true; +} + +bool subghz_protocol_alutech_at_4n_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 72; + bool res = subghz_protocol_alutech_at_4n_gen_data(instance, btn); + if(res) { + res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); + } + return res; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance + * @return true On success + */ +static bool subghz_protocol_encoder_alutech_at_4n_get_upload( + SubGhzProtocolEncoderAlutech_at_4n* instance, + uint8_t btn) { + furi_assert(instance); + + //gen new key + if(subghz_protocol_alutech_at_4n_gen_data(instance, btn)) { + //ToDo if you need to add a callback to automatically update the data on the display + } else { + return false; + } + + size_t index = 0; + // Send preambula + for(uint8_t i = 0; i < 12; ++i) { + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); // 1 + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); // 0 + } + + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 9; + + // Send key data + for(uint8_t i = 64; i > 0; --i) { + if(bit_read(instance->generic.data, i - 1)) { + //1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + } else { + //0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + } + } + // Send crc + for(uint8_t i = 8; i > 0; --i) { + if(bit_read(instance->crc, i - 1)) { + //1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + } else { + //0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_alutech_at_4n_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_alutech_at_4n_const.te_short); + } + } + // Inter-frame silence + instance->encoder.upload[index - 1].duration += + (uint32_t)subghz_protocol_alutech_at_4n_const.te_long * 20; + + size_t size_upload = index; + + 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; + } + return true; +} + +bool subghz_protocol_encoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderAlutech_at_4n* instance = context; + bool res = false; + do { + if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { + FURI_LOG_E(TAG, "Deserialize error"); + break; + } + + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_encoder_alutech_at_4n_get_upload(instance, instance->generic.btn); + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + if(!flipper_format_update_uint32(flipper_format, "CRC", &instance->crc, 1)) { + FURI_LOG_E(TAG, "Unable to add CRC"); + break; + } + + instance->encoder.is_running = true; + + res = true; + } while(false); + + return res; +} void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { SubGhzProtocolDecoderAlutech_at_4n* instance = @@ -452,4 +683,4 @@ void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* instance->generic.serial, instance->generic.btn, instance->generic.cnt); -} +} \ No newline at end of file diff --git a/lib/subghz/protocols/alutech_at_4n.h b/lib/subghz/protocols/alutech_at_4n.h index 38bac3ea6..b6d51439b 100644 --- a/lib/subghz/protocols/alutech_at_4n.h +++ b/lib/subghz/protocols/alutech_at_4n.h @@ -10,6 +10,60 @@ extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; extern const SubGhzProtocol subghz_protocol_alutech_at_4n; +/** + * Allocate SubGhzProtocolEncoderAlutech_at_4n. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderAlutech_at_4n* pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void* subghz_protocol_encoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderAlutech_at_4n. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void subghz_protocol_encoder_alutech_at_4n_free(void* context); + +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number, 24 bit + * @param btn Button number, 8 bit + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_alutech_at_4n_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint8_t btn, + uint16_t cnt, + SubGhzRadioPreset* preset); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return true On success + */ +bool subghz_protocol_encoder_alutech_at_4n_deserialize( + void* context, + FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + */ +void subghz_protocol_encoder_alutech_at_4n_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderAlutech_at_4n instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_alutech_at_4n_yield(void* context); + /** * Allocate SubGhzProtocolDecoderAlutech_at_4n. * @param environment Pointer to a SubGhzEnvironment instance @@ -71,4 +125,4 @@ bool subghz_protocol_decoder_alutech_at_4n_deserialize( * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance * @param output Resulting text */ -void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); +void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); \ No newline at end of file From 6555082e2aa115fc16e2bdf668bee6be1c447def Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Fri, 10 Feb 2023 20:50:30 +0000 Subject: [PATCH 163/231] Revert "Merge branch 'dev' of https://github.com/ClaraCrazy/Flipper-Xtreme into dev" This reverts commit 708dd167c8c57b453978d406448c6769fccb5435. --- .gitignore | 2 + .../main/archive/helpers/archive_browser.c | 12 +- .../main/archive/helpers/archive_browser.h | 2 + .../main/archive/views/archive_browser_view.c | 1 + .../main/archive/views/archive_browser_view.h | 1 + .../main/bad_usb/scenes/bad_usb_scene_error.c | 4 +- .../main/bad_usb/views/bad_usb_view.c | 9 +- .../main/u2f/scenes/u2f_scene_error.c | 4 +- applications/main/u2f/views/u2f_view.c | 10 +- .../plugins/nightstand/application.fam | 1 - applications/plugins/pomodoro/application.fam | 2 +- .../plugins/scrambler/application.fam | 2 +- .../desktop/animations/animation_manager.c | 20 +- .../desktop/animations/animation_storage.c | 10 +- applications/services/desktop/desktop.c | 5 + .../desktop/scenes/desktop_scene_fault.c | 4 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/xtreme_settings_scene_start.c | 14 - .../settings/xtreme_settings/xtreme_assets.c | 282 ++++++++---------- .../settings/xtreme_settings/xtreme_assets.h | 13 +- .../xtreme_settings/xtreme_settings.h | 1 - .../PaxGod_TikTok_Marketing/frame_0.png | Bin .../PaxGod_TikTok_Marketing/frame_1.png | Bin .../PaxGod_TikTok_Marketing/frame_10.png | Bin .../PaxGod_TikTok_Marketing/frame_11.png | Bin .../PaxGod_TikTok_Marketing/frame_12.png | Bin .../PaxGod_TikTok_Marketing/frame_13.png | Bin .../PaxGod_TikTok_Marketing/frame_14.png | Bin .../PaxGod_TikTok_Marketing/frame_15.png | Bin .../PaxGod_TikTok_Marketing/frame_16.png | Bin .../PaxGod_TikTok_Marketing/frame_17.png | Bin .../PaxGod_TikTok_Marketing/frame_18.png | Bin .../PaxGod_TikTok_Marketing/frame_19.png | Bin .../PaxGod_TikTok_Marketing/frame_2.png | Bin .../PaxGod_TikTok_Marketing/frame_20.png | Bin .../PaxGod_TikTok_Marketing/frame_21.png | Bin .../PaxGod_TikTok_Marketing/frame_22.png | Bin .../PaxGod_TikTok_Marketing/frame_23.png | Bin .../PaxGod_TikTok_Marketing/frame_24.png | Bin .../PaxGod_TikTok_Marketing/frame_25.png | Bin .../PaxGod_TikTok_Marketing/frame_26.png | Bin .../PaxGod_TikTok_Marketing/frame_27.png | Bin .../PaxGod_TikTok_Marketing/frame_3.png | Bin .../PaxGod_TikTok_Marketing/frame_4.png | Bin .../PaxGod_TikTok_Marketing/frame_5.png | Bin .../PaxGod_TikTok_Marketing/frame_6.png | Bin .../PaxGod_TikTok_Marketing/frame_7.png | Bin .../PaxGod_TikTok_Marketing/frame_8.png | Bin .../PaxGod_TikTok_Marketing/frame_9.png | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 0 .../NSFW/Anims}/lvl_1/frame_0.png | Bin .../NSFW/Anims}/lvl_1/frame_1.png | Bin .../NSFW/Anims}/lvl_1/frame_10.png | Bin .../NSFW/Anims}/lvl_1/frame_11.png | Bin .../NSFW/Anims}/lvl_1/frame_12.png | Bin .../NSFW/Anims}/lvl_1/frame_13.png | Bin .../NSFW/Anims}/lvl_1/frame_14.png | Bin .../NSFW/Anims}/lvl_1/frame_15.png | Bin .../NSFW/Anims}/lvl_1/frame_16.png | Bin .../NSFW/Anims}/lvl_1/frame_17.png | Bin .../NSFW/Anims}/lvl_1/frame_18.png | Bin .../NSFW/Anims}/lvl_1/frame_19.png | Bin .../NSFW/Anims}/lvl_1/frame_2.png | Bin .../NSFW/Anims}/lvl_1/frame_20.png | Bin .../NSFW/Anims}/lvl_1/frame_21.png | Bin .../NSFW/Anims}/lvl_1/frame_22.png | Bin .../NSFW/Anims}/lvl_1/frame_23.png | Bin .../NSFW/Anims}/lvl_1/frame_24.png | Bin .../NSFW/Anims}/lvl_1/frame_25.png | Bin .../NSFW/Anims}/lvl_1/frame_26.png | Bin .../NSFW/Anims}/lvl_1/frame_27.png | Bin .../NSFW/Anims}/lvl_1/frame_28.png | Bin .../NSFW/Anims}/lvl_1/frame_29.png | Bin .../NSFW/Anims}/lvl_1/frame_3.png | Bin .../NSFW/Anims}/lvl_1/frame_30.png | Bin .../NSFW/Anims}/lvl_1/frame_4.png | Bin .../NSFW/Anims}/lvl_1/frame_5.png | Bin .../NSFW/Anims}/lvl_1/frame_6.png | Bin .../NSFW/Anims}/lvl_1/frame_7.png | Bin .../NSFW/Anims}/lvl_1/frame_8.png | Bin .../NSFW/Anims}/lvl_1/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.png | Bin .../NSFW/Anims}/lvl_10/frame_1.png | Bin .../NSFW/Anims}/lvl_10/frame_10.png | Bin .../NSFW/Anims}/lvl_10/frame_11.png | Bin .../NSFW/Anims}/lvl_10/frame_12.png | Bin .../NSFW/Anims}/lvl_10/frame_13.png | Bin .../NSFW/Anims}/lvl_10/frame_14.png | Bin .../NSFW/Anims}/lvl_10/frame_15.png | Bin .../NSFW/Anims}/lvl_10/frame_16.png | Bin .../NSFW/Anims}/lvl_10/frame_17.png | Bin .../NSFW/Anims}/lvl_10/frame_18.png | Bin .../NSFW/Anims}/lvl_10/frame_19.png | Bin .../NSFW/Anims}/lvl_10/frame_2.png | Bin .../NSFW/Anims}/lvl_10/frame_20.png | Bin .../NSFW/Anims}/lvl_10/frame_21.png | Bin .../NSFW/Anims}/lvl_10/frame_22.png | Bin .../NSFW/Anims}/lvl_10/frame_23.png | Bin .../NSFW/Anims}/lvl_10/frame_24.png | Bin .../NSFW/Anims}/lvl_10/frame_25.png | Bin .../NSFW/Anims}/lvl_10/frame_26.png | Bin .../NSFW/Anims}/lvl_10/frame_27.png | Bin .../NSFW/Anims}/lvl_10/frame_3.png | Bin .../NSFW/Anims}/lvl_10/frame_4.png | Bin .../NSFW/Anims}/lvl_10/frame_5.png | Bin .../NSFW/Anims}/lvl_10/frame_6.png | Bin .../NSFW/Anims}/lvl_10/frame_7.png | Bin .../NSFW/Anims}/lvl_10/frame_8.png | Bin .../NSFW/Anims}/lvl_10/frame_9.png | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.png | Bin .../NSFW/Anims}/lvl_11/frame_1.png | Bin .../NSFW/Anims}/lvl_11/frame_10.png | Bin .../NSFW/Anims}/lvl_11/frame_11.png | Bin .../NSFW/Anims}/lvl_11/frame_12.png | Bin .../NSFW/Anims}/lvl_11/frame_13.png | Bin .../NSFW/Anims}/lvl_11/frame_14.png | Bin .../NSFW/Anims}/lvl_11/frame_15.png | Bin .../NSFW/Anims}/lvl_11/frame_16.png | Bin .../NSFW/Anims}/lvl_11/frame_17.png | Bin .../NSFW/Anims}/lvl_11/frame_18.png | Bin .../NSFW/Anims}/lvl_11/frame_19.png | Bin .../NSFW/Anims}/lvl_11/frame_2.png | Bin .../NSFW/Anims}/lvl_11/frame_20.png | Bin .../NSFW/Anims}/lvl_11/frame_21.png | Bin .../NSFW/Anims}/lvl_11/frame_22.png | Bin .../NSFW/Anims}/lvl_11/frame_23.png | Bin .../NSFW/Anims}/lvl_11/frame_24.png | Bin .../NSFW/Anims}/lvl_11/frame_25.png | Bin .../NSFW/Anims}/lvl_11/frame_26.png | Bin .../NSFW/Anims}/lvl_11/frame_27.png | Bin .../NSFW/Anims}/lvl_11/frame_28.png | Bin .../NSFW/Anims}/lvl_11/frame_29.png | Bin .../NSFW/Anims}/lvl_11/frame_3.png | Bin .../NSFW/Anims}/lvl_11/frame_30.png | Bin .../NSFW/Anims}/lvl_11/frame_31.png | Bin .../NSFW/Anims}/lvl_11/frame_32.png | Bin .../NSFW/Anims}/lvl_11/frame_33.png | Bin .../NSFW/Anims}/lvl_11/frame_34.png | Bin .../NSFW/Anims}/lvl_11/frame_35.png | Bin .../NSFW/Anims}/lvl_11/frame_36.png | Bin .../NSFW/Anims}/lvl_11/frame_37.png | Bin .../NSFW/Anims}/lvl_11/frame_38.png | Bin .../NSFW/Anims}/lvl_11/frame_39.png | Bin .../NSFW/Anims}/lvl_11/frame_4.png | Bin .../NSFW/Anims}/lvl_11/frame_40.png | Bin .../NSFW/Anims}/lvl_11/frame_41.png | Bin .../NSFW/Anims}/lvl_11/frame_42.png | Bin .../NSFW/Anims}/lvl_11/frame_43.png | Bin .../NSFW/Anims}/lvl_11/frame_44.png | Bin .../NSFW/Anims}/lvl_11/frame_45.png | Bin .../NSFW/Anims}/lvl_11/frame_46.png | Bin .../NSFW/Anims}/lvl_11/frame_47.png | Bin .../NSFW/Anims}/lvl_11/frame_48.png | Bin .../NSFW/Anims}/lvl_11/frame_49.png | Bin .../NSFW/Anims}/lvl_11/frame_5.png | Bin .../NSFW/Anims}/lvl_11/frame_6.png | Bin .../NSFW/Anims}/lvl_11/frame_7.png | Bin .../NSFW/Anims}/lvl_11/frame_8.png | Bin .../NSFW/Anims}/lvl_11/frame_9.png | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.png | Bin .../NSFW/Anims}/lvl_12/frame_1.png | Bin .../NSFW/Anims}/lvl_12/frame_10.png | Bin .../NSFW/Anims}/lvl_12/frame_11.png | Bin .../NSFW/Anims}/lvl_12/frame_12.png | Bin .../NSFW/Anims}/lvl_12/frame_13.png | Bin .../NSFW/Anims}/lvl_12/frame_14.png | Bin .../NSFW/Anims}/lvl_12/frame_15.png | Bin .../NSFW/Anims}/lvl_12/frame_2.png | Bin .../NSFW/Anims}/lvl_12/frame_3.png | Bin .../NSFW/Anims}/lvl_12/frame_4.png | Bin .../NSFW/Anims}/lvl_12/frame_5.png | Bin .../NSFW/Anims}/lvl_12/frame_6.png | Bin .../NSFW/Anims}/lvl_12/frame_7.png | Bin .../NSFW/Anims}/lvl_12/frame_8.png | Bin .../NSFW/Anims}/lvl_12/frame_9.png | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.png | Bin .../NSFW/Anims}/lvl_13/frame_1.png | Bin .../NSFW/Anims}/lvl_13/frame_10.png | Bin .../NSFW/Anims}/lvl_13/frame_2.png | Bin .../NSFW/Anims}/lvl_13/frame_3.png | Bin .../NSFW/Anims}/lvl_13/frame_4.png | Bin .../NSFW/Anims}/lvl_13/frame_5.png | Bin .../NSFW/Anims}/lvl_13/frame_6.png | Bin .../NSFW/Anims}/lvl_13/frame_7.png | Bin .../NSFW/Anims}/lvl_13/frame_8.png | Bin .../NSFW/Anims}/lvl_13/frame_9.png | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.png | Bin .../NSFW/Anims}/lvl_14/frame_1.png | Bin .../NSFW/Anims}/lvl_14/frame_2.png | Bin .../NSFW/Anims}/lvl_14/frame_3.png | Bin .../NSFW/Anims}/lvl_14/frame_4.png | Bin .../NSFW/Anims}/lvl_14/frame_5.png | Bin .../NSFW/Anims}/lvl_14/frame_6.png | Bin .../NSFW/Anims}/lvl_14/frame_7.png | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.png | Bin .../NSFW/Anims}/lvl_15/frame_1.png | Bin .../NSFW/Anims}/lvl_15/frame_10.png | Bin .../NSFW/Anims}/lvl_15/frame_11.png | Bin .../NSFW/Anims}/lvl_15/frame_12.png | Bin .../NSFW/Anims}/lvl_15/frame_13.png | Bin .../NSFW/Anims}/lvl_15/frame_14.png | Bin .../NSFW/Anims}/lvl_15/frame_15.png | Bin .../NSFW/Anims}/lvl_15/frame_16.png | Bin .../NSFW/Anims}/lvl_15/frame_17.png | Bin .../NSFW/Anims}/lvl_15/frame_18.png | Bin .../NSFW/Anims}/lvl_15/frame_19.png | Bin .../NSFW/Anims}/lvl_15/frame_2.png | Bin .../NSFW/Anims}/lvl_15/frame_20.png | Bin .../NSFW/Anims}/lvl_15/frame_21.png | Bin .../NSFW/Anims}/lvl_15/frame_3.png | Bin .../NSFW/Anims}/lvl_15/frame_4.png | Bin .../NSFW/Anims}/lvl_15/frame_5.png | Bin .../NSFW/Anims}/lvl_15/frame_6.png | Bin .../NSFW/Anims}/lvl_15/frame_7.png | Bin .../NSFW/Anims}/lvl_15/frame_8.png | Bin .../NSFW/Anims}/lvl_15/frame_9.png | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.png | Bin .../NSFW/Anims}/lvl_16/frame_1.png | Bin .../NSFW/Anims}/lvl_16/frame_10.png | Bin .../NSFW/Anims}/lvl_16/frame_11.png | Bin .../NSFW/Anims}/lvl_16/frame_12.png | Bin .../NSFW/Anims}/lvl_16/frame_13.png | Bin .../NSFW/Anims}/lvl_16/frame_14.png | Bin .../NSFW/Anims}/lvl_16/frame_15.png | Bin .../NSFW/Anims}/lvl_16/frame_16.png | Bin .../NSFW/Anims}/lvl_16/frame_17.png | Bin .../NSFW/Anims}/lvl_16/frame_18.png | Bin .../NSFW/Anims}/lvl_16/frame_19.png | Bin .../NSFW/Anims}/lvl_16/frame_2.png | Bin .../NSFW/Anims}/lvl_16/frame_20.png | Bin .../NSFW/Anims}/lvl_16/frame_3.png | Bin .../NSFW/Anims}/lvl_16/frame_4.png | Bin .../NSFW/Anims}/lvl_16/frame_5.png | Bin .../NSFW/Anims}/lvl_16/frame_6.png | Bin .../NSFW/Anims}/lvl_16/frame_7.png | Bin .../NSFW/Anims}/lvl_16/frame_8.png | Bin .../NSFW/Anims}/lvl_16/frame_9.png | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.png | Bin .../NSFW/Anims}/lvl_17/frame_1.png | Bin .../NSFW/Anims}/lvl_17/frame_10.png | Bin .../NSFW/Anims}/lvl_17/frame_11.png | Bin .../NSFW/Anims}/lvl_17/frame_12.png | Bin .../NSFW/Anims}/lvl_17/frame_13.png | Bin .../NSFW/Anims}/lvl_17/frame_14.png | Bin .../NSFW/Anims}/lvl_17/frame_15.png | Bin .../NSFW/Anims}/lvl_17/frame_16.png | Bin .../NSFW/Anims}/lvl_17/frame_17.png | Bin .../NSFW/Anims}/lvl_17/frame_18.png | Bin .../NSFW/Anims}/lvl_17/frame_19.png | Bin .../NSFW/Anims}/lvl_17/frame_2.png | Bin .../NSFW/Anims}/lvl_17/frame_20.png | Bin .../NSFW/Anims}/lvl_17/frame_21.png | Bin .../NSFW/Anims}/lvl_17/frame_22.png | Bin .../NSFW/Anims}/lvl_17/frame_23.png | Bin .../NSFW/Anims}/lvl_17/frame_24.png | Bin .../NSFW/Anims}/lvl_17/frame_25.png | Bin .../NSFW/Anims}/lvl_17/frame_26.png | Bin .../NSFW/Anims}/lvl_17/frame_27.png | Bin .../NSFW/Anims}/lvl_17/frame_28.png | Bin .../NSFW/Anims}/lvl_17/frame_29.png | Bin .../NSFW/Anims}/lvl_17/frame_3.png | Bin .../NSFW/Anims}/lvl_17/frame_30.png | Bin .../NSFW/Anims}/lvl_17/frame_31.png | Bin .../NSFW/Anims}/lvl_17/frame_4.png | Bin .../NSFW/Anims}/lvl_17/frame_5.png | Bin .../NSFW/Anims}/lvl_17/frame_6.png | Bin .../NSFW/Anims}/lvl_17/frame_7.png | Bin .../NSFW/Anims}/lvl_17/frame_8.png | Bin .../NSFW/Anims}/lvl_17/frame_9.png | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.png | Bin .../NSFW/Anims}/lvl_18/frame_1.png | Bin .../NSFW/Anims}/lvl_18/frame_10.png | Bin .../NSFW/Anims}/lvl_18/frame_11.png | Bin .../NSFW/Anims}/lvl_18/frame_12.png | Bin .../NSFW/Anims}/lvl_18/frame_13.png | Bin .../NSFW/Anims}/lvl_18/frame_14.png | Bin .../NSFW/Anims}/lvl_18/frame_15.png | Bin .../NSFW/Anims}/lvl_18/frame_16.png | Bin .../NSFW/Anims}/lvl_18/frame_17.png | Bin .../NSFW/Anims}/lvl_18/frame_18.png | Bin .../NSFW/Anims}/lvl_18/frame_19.png | Bin .../NSFW/Anims}/lvl_18/frame_2.png | Bin .../NSFW/Anims}/lvl_18/frame_20.png | Bin .../NSFW/Anims}/lvl_18/frame_21.png | Bin .../NSFW/Anims}/lvl_18/frame_22.png | Bin .../NSFW/Anims}/lvl_18/frame_3.png | Bin .../NSFW/Anims}/lvl_18/frame_4.png | Bin .../NSFW/Anims}/lvl_18/frame_5.png | Bin .../NSFW/Anims}/lvl_18/frame_6.png | Bin .../NSFW/Anims}/lvl_18/frame_7.png | Bin .../NSFW/Anims}/lvl_18/frame_8.png | Bin .../NSFW/Anims}/lvl_18/frame_9.png | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.png | Bin .../NSFW/Anims}/lvl_19/frame_1.png | Bin .../NSFW/Anims}/lvl_19/frame_10.png | Bin .../NSFW/Anims}/lvl_19/frame_11.png | Bin .../NSFW/Anims}/lvl_19/frame_12.png | Bin .../NSFW/Anims}/lvl_19/frame_13.png | Bin .../NSFW/Anims}/lvl_19/frame_14.png | Bin .../NSFW/Anims}/lvl_19/frame_15.png | Bin .../NSFW/Anims}/lvl_19/frame_16.png | Bin .../NSFW/Anims}/lvl_19/frame_17.png | Bin .../NSFW/Anims}/lvl_19/frame_18.png | Bin .../NSFW/Anims}/lvl_19/frame_19.png | Bin .../NSFW/Anims}/lvl_19/frame_2.png | Bin .../NSFW/Anims}/lvl_19/frame_20.png | Bin .../NSFW/Anims}/lvl_19/frame_21.png | Bin .../NSFW/Anims}/lvl_19/frame_3.png | Bin .../NSFW/Anims}/lvl_19/frame_4.png | Bin .../NSFW/Anims}/lvl_19/frame_5.png | Bin .../NSFW/Anims}/lvl_19/frame_6.png | Bin .../NSFW/Anims}/lvl_19/frame_7.png | Bin .../NSFW/Anims}/lvl_19/frame_8.png | Bin .../NSFW/Anims}/lvl_19/frame_9.png | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.png | Bin .../NSFW/Anims}/lvl_2/frame_1.png | Bin .../NSFW/Anims}/lvl_2/frame_10.png | Bin .../NSFW/Anims}/lvl_2/frame_11.png | Bin .../NSFW/Anims}/lvl_2/frame_12.png | Bin .../NSFW/Anims}/lvl_2/frame_13.png | Bin .../NSFW/Anims}/lvl_2/frame_14.png | Bin .../NSFW/Anims}/lvl_2/frame_2.png | Bin .../NSFW/Anims}/lvl_2/frame_3.png | Bin .../NSFW/Anims}/lvl_2/frame_4.png | Bin .../NSFW/Anims}/lvl_2/frame_5.png | Bin .../NSFW/Anims}/lvl_2/frame_6.png | Bin .../NSFW/Anims}/lvl_2/frame_7.png | Bin .../NSFW/Anims}/lvl_2/frame_8.png | Bin .../NSFW/Anims}/lvl_2/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.png | Bin .../NSFW/Anims}/lvl_20/frame_1.png | Bin .../NSFW/Anims}/lvl_20/frame_10.png | Bin .../NSFW/Anims}/lvl_20/frame_11.png | Bin .../NSFW/Anims}/lvl_20/frame_12.png | Bin .../NSFW/Anims}/lvl_20/frame_13.png | Bin .../NSFW/Anims}/lvl_20/frame_2.png | Bin .../NSFW/Anims}/lvl_20/frame_3.png | Bin .../NSFW/Anims}/lvl_20/frame_4.png | Bin .../NSFW/Anims}/lvl_20/frame_5.png | Bin .../NSFW/Anims}/lvl_20/frame_6.png | Bin .../NSFW/Anims}/lvl_20/frame_7.png | Bin .../NSFW/Anims}/lvl_20/frame_8.png | Bin .../NSFW/Anims}/lvl_20/frame_9.png | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.png | Bin .../NSFW/Anims}/lvl_21/frame_1.png | Bin .../NSFW/Anims}/lvl_21/frame_2.png | Bin .../NSFW/Anims}/lvl_21/frame_3.png | Bin .../NSFW/Anims}/lvl_21/frame_4.png | Bin .../NSFW/Anims}/lvl_21/frame_5.png | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.png | Bin .../NSFW/Anims}/lvl_22/frame_1.png | Bin .../NSFW/Anims}/lvl_22/frame_10.png | Bin .../NSFW/Anims}/lvl_22/frame_11.png | Bin .../NSFW/Anims}/lvl_22/frame_12.png | Bin .../NSFW/Anims}/lvl_22/frame_13.png | Bin .../NSFW/Anims}/lvl_22/frame_14.png | Bin .../NSFW/Anims}/lvl_22/frame_15.png | Bin .../NSFW/Anims}/lvl_22/frame_16.png | Bin .../NSFW/Anims}/lvl_22/frame_17.png | Bin .../NSFW/Anims}/lvl_22/frame_18.png | Bin .../NSFW/Anims}/lvl_22/frame_19.png | Bin .../NSFW/Anims}/lvl_22/frame_2.png | Bin .../NSFW/Anims}/lvl_22/frame_20.png | Bin .../NSFW/Anims}/lvl_22/frame_21.png | Bin .../NSFW/Anims}/lvl_22/frame_22.png | Bin .../NSFW/Anims}/lvl_22/frame_23.png | Bin .../NSFW/Anims}/lvl_22/frame_24.png | Bin .../NSFW/Anims}/lvl_22/frame_25.png | Bin .../NSFW/Anims}/lvl_22/frame_26.png | Bin .../NSFW/Anims}/lvl_22/frame_27.png | Bin .../NSFW/Anims}/lvl_22/frame_28.png | Bin .../NSFW/Anims}/lvl_22/frame_29.png | Bin .../NSFW/Anims}/lvl_22/frame_3.png | Bin .../NSFW/Anims}/lvl_22/frame_30.png | Bin .../NSFW/Anims}/lvl_22/frame_31.png | Bin .../NSFW/Anims}/lvl_22/frame_32.png | Bin .../NSFW/Anims}/lvl_22/frame_33.png | Bin .../NSFW/Anims}/lvl_22/frame_34.png | Bin .../NSFW/Anims}/lvl_22/frame_35.png | Bin .../NSFW/Anims}/lvl_22/frame_36.png | Bin .../NSFW/Anims}/lvl_22/frame_37.png | Bin .../NSFW/Anims}/lvl_22/frame_38.png | Bin .../NSFW/Anims}/lvl_22/frame_39.png | Bin .../NSFW/Anims}/lvl_22/frame_4.png | Bin .../NSFW/Anims}/lvl_22/frame_40.png | Bin .../NSFW/Anims}/lvl_22/frame_41.png | Bin .../NSFW/Anims}/lvl_22/frame_42.png | Bin .../NSFW/Anims}/lvl_22/frame_43.png | Bin .../NSFW/Anims}/lvl_22/frame_44.png | Bin .../NSFW/Anims}/lvl_22/frame_45.png | Bin .../NSFW/Anims}/lvl_22/frame_46.png | Bin .../NSFW/Anims}/lvl_22/frame_47.png | Bin .../NSFW/Anims}/lvl_22/frame_48.png | Bin .../NSFW/Anims}/lvl_22/frame_49.png | Bin .../NSFW/Anims}/lvl_22/frame_5.png | Bin .../NSFW/Anims}/lvl_22/frame_50.png | Bin .../NSFW/Anims}/lvl_22/frame_51.png | Bin .../NSFW/Anims}/lvl_22/frame_52.png | Bin .../NSFW/Anims}/lvl_22/frame_53.png | Bin .../NSFW/Anims}/lvl_22/frame_54.png | Bin .../NSFW/Anims}/lvl_22/frame_55.png | Bin .../NSFW/Anims}/lvl_22/frame_56.png | Bin .../NSFW/Anims}/lvl_22/frame_57.png | Bin .../NSFW/Anims}/lvl_22/frame_58.png | Bin .../NSFW/Anims}/lvl_22/frame_59.png | Bin .../NSFW/Anims}/lvl_22/frame_6.png | Bin .../NSFW/Anims}/lvl_22/frame_7.png | Bin .../NSFW/Anims}/lvl_22/frame_8.png | Bin .../NSFW/Anims}/lvl_22/frame_9.png | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.png | Bin .../NSFW/Anims}/lvl_23/frame_1.png | Bin .../NSFW/Anims}/lvl_23/frame_10.png | Bin .../NSFW/Anims}/lvl_23/frame_11.png | Bin .../NSFW/Anims}/lvl_23/frame_12.png | Bin .../NSFW/Anims}/lvl_23/frame_13.png | Bin .../NSFW/Anims}/lvl_23/frame_14.png | Bin .../NSFW/Anims}/lvl_23/frame_15.png | Bin .../NSFW/Anims}/lvl_23/frame_16.png | Bin .../NSFW/Anims}/lvl_23/frame_2.png | Bin .../NSFW/Anims}/lvl_23/frame_3.png | Bin .../NSFW/Anims}/lvl_23/frame_4.png | Bin .../NSFW/Anims}/lvl_23/frame_5.png | Bin .../NSFW/Anims}/lvl_23/frame_6.png | Bin .../NSFW/Anims}/lvl_23/frame_7.png | Bin .../NSFW/Anims}/lvl_23/frame_8.png | Bin .../NSFW/Anims}/lvl_23/frame_9.png | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.png | Bin .../NSFW/Anims}/lvl_24/frame_1.png | Bin .../NSFW/Anims}/lvl_24/frame_10.png | Bin .../NSFW/Anims}/lvl_24/frame_11.png | Bin .../NSFW/Anims}/lvl_24/frame_12.png | Bin .../NSFW/Anims}/lvl_24/frame_13.png | Bin .../NSFW/Anims}/lvl_24/frame_14.png | Bin .../NSFW/Anims}/lvl_24/frame_15.png | Bin .../NSFW/Anims}/lvl_24/frame_16.png | Bin .../NSFW/Anims}/lvl_24/frame_17.png | Bin .../NSFW/Anims}/lvl_24/frame_18.png | Bin .../NSFW/Anims}/lvl_24/frame_19.png | Bin .../NSFW/Anims}/lvl_24/frame_2.png | Bin .../NSFW/Anims}/lvl_24/frame_20.png | Bin .../NSFW/Anims}/lvl_24/frame_21.png | Bin .../NSFW/Anims}/lvl_24/frame_22.png | Bin .../NSFW/Anims}/lvl_24/frame_23.png | Bin .../NSFW/Anims}/lvl_24/frame_24.png | Bin .../NSFW/Anims}/lvl_24/frame_25.png | Bin .../NSFW/Anims}/lvl_24/frame_26.png | Bin .../NSFW/Anims}/lvl_24/frame_27.png | Bin .../NSFW/Anims}/lvl_24/frame_28.png | Bin .../NSFW/Anims}/lvl_24/frame_29.png | Bin .../NSFW/Anims}/lvl_24/frame_3.png | Bin .../NSFW/Anims}/lvl_24/frame_4.png | Bin .../NSFW/Anims}/lvl_24/frame_5.png | Bin .../NSFW/Anims}/lvl_24/frame_6.png | Bin .../NSFW/Anims}/lvl_24/frame_7.png | Bin .../NSFW/Anims}/lvl_24/frame_8.png | Bin .../NSFW/Anims}/lvl_24/frame_9.png | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.png | Bin .../NSFW/Anims}/lvl_25/frame_1.png | Bin .../NSFW/Anims}/lvl_25/frame_10.png | Bin .../NSFW/Anims}/lvl_25/frame_11.png | Bin .../NSFW/Anims}/lvl_25/frame_12.png | Bin .../NSFW/Anims}/lvl_25/frame_13.png | Bin .../NSFW/Anims}/lvl_25/frame_14.png | Bin .../NSFW/Anims}/lvl_25/frame_15.png | Bin .../NSFW/Anims}/lvl_25/frame_16.png | Bin .../NSFW/Anims}/lvl_25/frame_17.png | Bin .../NSFW/Anims}/lvl_25/frame_18.png | Bin .../NSFW/Anims}/lvl_25/frame_19.png | Bin .../NSFW/Anims}/lvl_25/frame_2.png | Bin .../NSFW/Anims}/lvl_25/frame_20.png | Bin .../NSFW/Anims}/lvl_25/frame_21.png | Bin .../NSFW/Anims}/lvl_25/frame_22.png | Bin .../NSFW/Anims}/lvl_25/frame_23.png | Bin .../NSFW/Anims}/lvl_25/frame_24.png | Bin .../NSFW/Anims}/lvl_25/frame_25.png | Bin .../NSFW/Anims}/lvl_25/frame_26.png | Bin .../NSFW/Anims}/lvl_25/frame_27.png | Bin .../NSFW/Anims}/lvl_25/frame_28.png | Bin .../NSFW/Anims}/lvl_25/frame_29.png | Bin .../NSFW/Anims}/lvl_25/frame_3.png | Bin .../NSFW/Anims}/lvl_25/frame_30.png | Bin .../NSFW/Anims}/lvl_25/frame_31.png | Bin .../NSFW/Anims}/lvl_25/frame_32.png | Bin .../NSFW/Anims}/lvl_25/frame_33.png | Bin .../NSFW/Anims}/lvl_25/frame_34.png | Bin .../NSFW/Anims}/lvl_25/frame_35.png | Bin .../NSFW/Anims}/lvl_25/frame_4.png | Bin .../NSFW/Anims}/lvl_25/frame_5.png | Bin .../NSFW/Anims}/lvl_25/frame_6.png | Bin .../NSFW/Anims}/lvl_25/frame_7.png | Bin .../NSFW/Anims}/lvl_25/frame_8.png | Bin .../NSFW/Anims}/lvl_25/frame_9.png | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.png | Bin .../NSFW/Anims}/lvl_26/frame_1.png | Bin .../NSFW/Anims}/lvl_26/frame_10.png | Bin .../NSFW/Anims}/lvl_26/frame_11.png | Bin .../NSFW/Anims}/lvl_26/frame_2.png | Bin .../NSFW/Anims}/lvl_26/frame_3.png | Bin .../NSFW/Anims}/lvl_26/frame_4.png | Bin .../NSFW/Anims}/lvl_26/frame_5.png | Bin .../NSFW/Anims}/lvl_26/frame_6.png | Bin .../NSFW/Anims}/lvl_26/frame_7.png | Bin .../NSFW/Anims}/lvl_26/frame_8.png | Bin .../NSFW/Anims}/lvl_26/frame_9.png | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.png | Bin .../NSFW/Anims}/lvl_27/frame_1.png | Bin .../NSFW/Anims}/lvl_27/frame_10.png | Bin .../NSFW/Anims}/lvl_27/frame_11.png | Bin .../NSFW/Anims}/lvl_27/frame_12.png | Bin .../NSFW/Anims}/lvl_27/frame_13.png | Bin .../NSFW/Anims}/lvl_27/frame_14.png | Bin .../NSFW/Anims}/lvl_27/frame_15.png | Bin .../NSFW/Anims}/lvl_27/frame_16.png | Bin .../NSFW/Anims}/lvl_27/frame_17.png | Bin .../NSFW/Anims}/lvl_27/frame_18.png | Bin .../NSFW/Anims}/lvl_27/frame_19.png | Bin .../NSFW/Anims}/lvl_27/frame_2.png | Bin .../NSFW/Anims}/lvl_27/frame_20.png | Bin .../NSFW/Anims}/lvl_27/frame_21.png | Bin .../NSFW/Anims}/lvl_27/frame_3.png | Bin .../NSFW/Anims}/lvl_27/frame_4.png | Bin .../NSFW/Anims}/lvl_27/frame_5.png | Bin .../NSFW/Anims}/lvl_27/frame_6.png | Bin .../NSFW/Anims}/lvl_27/frame_7.png | Bin .../NSFW/Anims}/lvl_27/frame_8.png | Bin .../NSFW/Anims}/lvl_27/frame_9.png | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.png | Bin .../NSFW/Anims}/lvl_28/frame_1.png | Bin .../NSFW/Anims}/lvl_28/frame_2.png | Bin .../NSFW/Anims}/lvl_28/frame_3.png | Bin .../NSFW/Anims}/lvl_28/frame_4.png | Bin .../NSFW/Anims}/lvl_28/frame_5.png | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.png | Bin .../NSFW/Anims}/lvl_29/frame_1.png | Bin .../NSFW/Anims}/lvl_29/frame_10.png | Bin .../NSFW/Anims}/lvl_29/frame_11.png | Bin .../NSFW/Anims}/lvl_29/frame_12.png | Bin .../NSFW/Anims}/lvl_29/frame_13.png | Bin .../NSFW/Anims}/lvl_29/frame_14.png | Bin .../NSFW/Anims}/lvl_29/frame_15.png | Bin .../NSFW/Anims}/lvl_29/frame_16.png | Bin .../NSFW/Anims}/lvl_29/frame_17.png | Bin .../NSFW/Anims}/lvl_29/frame_18.png | Bin .../NSFW/Anims}/lvl_29/frame_19.png | Bin .../NSFW/Anims}/lvl_29/frame_2.png | Bin .../NSFW/Anims}/lvl_29/frame_20.png | Bin .../NSFW/Anims}/lvl_29/frame_21.png | Bin .../NSFW/Anims}/lvl_29/frame_22.png | Bin .../NSFW/Anims}/lvl_29/frame_23.png | Bin .../NSFW/Anims}/lvl_29/frame_24.png | Bin .../NSFW/Anims}/lvl_29/frame_25.png | Bin .../NSFW/Anims}/lvl_29/frame_26.png | Bin .../NSFW/Anims}/lvl_29/frame_27.png | Bin .../NSFW/Anims}/lvl_29/frame_28.png | Bin .../NSFW/Anims}/lvl_29/frame_29.png | Bin .../NSFW/Anims}/lvl_29/frame_3.png | Bin .../NSFW/Anims}/lvl_29/frame_30.png | Bin .../NSFW/Anims}/lvl_29/frame_31.png | Bin .../NSFW/Anims}/lvl_29/frame_32.png | Bin .../NSFW/Anims}/lvl_29/frame_33.png | Bin .../NSFW/Anims}/lvl_29/frame_34.png | Bin .../NSFW/Anims}/lvl_29/frame_35.png | Bin .../NSFW/Anims}/lvl_29/frame_36.png | Bin .../NSFW/Anims}/lvl_29/frame_37.png | Bin .../NSFW/Anims}/lvl_29/frame_38.png | Bin .../NSFW/Anims}/lvl_29/frame_39.png | Bin .../NSFW/Anims}/lvl_29/frame_4.png | Bin .../NSFW/Anims}/lvl_29/frame_40.png | Bin .../NSFW/Anims}/lvl_29/frame_41.png | Bin .../NSFW/Anims}/lvl_29/frame_42.png | Bin .../NSFW/Anims}/lvl_29/frame_43.png | Bin .../NSFW/Anims}/lvl_29/frame_44.png | Bin .../NSFW/Anims}/lvl_29/frame_45.png | Bin .../NSFW/Anims}/lvl_29/frame_46.png | Bin .../NSFW/Anims}/lvl_29/frame_47.png | Bin .../NSFW/Anims}/lvl_29/frame_48.png | Bin .../NSFW/Anims}/lvl_29/frame_49.png | Bin .../NSFW/Anims}/lvl_29/frame_5.png | Bin .../NSFW/Anims}/lvl_29/frame_50.png | Bin .../NSFW/Anims}/lvl_29/frame_51.png | Bin .../NSFW/Anims}/lvl_29/frame_6.png | Bin .../NSFW/Anims}/lvl_29/frame_7.png | Bin .../NSFW/Anims}/lvl_29/frame_8.png | Bin .../NSFW/Anims}/lvl_29/frame_9.png | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.png | Bin .../NSFW/Anims}/lvl_3/frame_1.png | Bin .../NSFW/Anims}/lvl_3/frame_10.png | Bin .../NSFW/Anims}/lvl_3/frame_11.png | Bin .../NSFW/Anims}/lvl_3/frame_12.png | Bin .../NSFW/Anims}/lvl_3/frame_13.png | Bin .../NSFW/Anims}/lvl_3/frame_14.png | Bin .../NSFW/Anims}/lvl_3/frame_2.png | Bin .../NSFW/Anims}/lvl_3/frame_3.png | Bin .../NSFW/Anims}/lvl_3/frame_4.png | Bin .../NSFW/Anims}/lvl_3/frame_5.png | Bin .../NSFW/Anims}/lvl_3/frame_6.png | Bin .../NSFW/Anims}/lvl_3/frame_7.png | Bin .../NSFW/Anims}/lvl_3/frame_8.png | Bin .../NSFW/Anims}/lvl_3/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.png | Bin .../NSFW/Anims}/lvl_30/frame_1.png | Bin .../NSFW/Anims}/lvl_30/frame_10.png | Bin .../NSFW/Anims}/lvl_30/frame_11.png | Bin .../NSFW/Anims}/lvl_30/frame_12.png | Bin .../NSFW/Anims}/lvl_30/frame_13.png | Bin .../NSFW/Anims}/lvl_30/frame_14.png | Bin .../NSFW/Anims}/lvl_30/frame_15.png | Bin .../NSFW/Anims}/lvl_30/frame_16.png | Bin .../NSFW/Anims}/lvl_30/frame_17.png | Bin .../NSFW/Anims}/lvl_30/frame_18.png | Bin .../NSFW/Anims}/lvl_30/frame_19.png | Bin .../NSFW/Anims}/lvl_30/frame_2.png | Bin .../NSFW/Anims}/lvl_30/frame_20.png | Bin .../NSFW/Anims}/lvl_30/frame_21.png | Bin .../NSFW/Anims}/lvl_30/frame_22.png | Bin .../NSFW/Anims}/lvl_30/frame_23.png | Bin .../NSFW/Anims}/lvl_30/frame_24.png | Bin .../NSFW/Anims}/lvl_30/frame_25.png | Bin .../NSFW/Anims}/lvl_30/frame_26.png | Bin .../NSFW/Anims}/lvl_30/frame_27.png | Bin .../NSFW/Anims}/lvl_30/frame_28.png | Bin .../NSFW/Anims}/lvl_30/frame_29.png | Bin .../NSFW/Anims}/lvl_30/frame_3.png | Bin .../NSFW/Anims}/lvl_30/frame_30.png | Bin .../NSFW/Anims}/lvl_30/frame_31.png | Bin .../NSFW/Anims}/lvl_30/frame_32.png | Bin .../NSFW/Anims}/lvl_30/frame_33.png | Bin .../NSFW/Anims}/lvl_30/frame_34.png | Bin .../NSFW/Anims}/lvl_30/frame_35.png | Bin .../NSFW/Anims}/lvl_30/frame_36.png | Bin .../NSFW/Anims}/lvl_30/frame_37.png | Bin .../NSFW/Anims}/lvl_30/frame_38.png | Bin .../NSFW/Anims}/lvl_30/frame_39.png | Bin .../NSFW/Anims}/lvl_30/frame_4.png | Bin .../NSFW/Anims}/lvl_30/frame_40.png | Bin .../NSFW/Anims}/lvl_30/frame_41.png | Bin .../NSFW/Anims}/lvl_30/frame_42.png | Bin .../NSFW/Anims}/lvl_30/frame_43.png | Bin .../NSFW/Anims}/lvl_30/frame_44.png | Bin .../NSFW/Anims}/lvl_30/frame_45.png | Bin .../NSFW/Anims}/lvl_30/frame_46.png | Bin .../NSFW/Anims}/lvl_30/frame_47.png | Bin .../NSFW/Anims}/lvl_30/frame_48.png | Bin .../NSFW/Anims}/lvl_30/frame_49.png | Bin .../NSFW/Anims}/lvl_30/frame_5.png | Bin .../NSFW/Anims}/lvl_30/frame_6.png | Bin .../NSFW/Anims}/lvl_30/frame_7.png | Bin .../NSFW/Anims}/lvl_30/frame_8.png | Bin .../NSFW/Anims}/lvl_30/frame_9.png | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.png | Bin .../NSFW/Anims}/lvl_4/frame_1.png | Bin .../NSFW/Anims}/lvl_4/frame_10.png | Bin .../NSFW/Anims}/lvl_4/frame_11.png | Bin .../NSFW/Anims}/lvl_4/frame_12.png | Bin .../NSFW/Anims}/lvl_4/frame_13.png | Bin .../NSFW/Anims}/lvl_4/frame_14.png | Bin .../NSFW/Anims}/lvl_4/frame_15.png | Bin .../NSFW/Anims}/lvl_4/frame_16.png | Bin .../NSFW/Anims}/lvl_4/frame_17.png | Bin .../NSFW/Anims}/lvl_4/frame_18.png | Bin .../NSFW/Anims}/lvl_4/frame_19.png | Bin .../NSFW/Anims}/lvl_4/frame_2.png | Bin .../NSFW/Anims}/lvl_4/frame_3.png | Bin .../NSFW/Anims}/lvl_4/frame_4.png | Bin .../NSFW/Anims}/lvl_4/frame_5.png | Bin .../NSFW/Anims}/lvl_4/frame_6.png | Bin .../NSFW/Anims}/lvl_4/frame_7.png | Bin .../NSFW/Anims}/lvl_4/frame_8.png | Bin .../NSFW/Anims}/lvl_4/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.png | Bin .../NSFW/Anims}/lvl_5/frame_1.png | Bin .../NSFW/Anims}/lvl_5/frame_10.png | Bin .../NSFW/Anims}/lvl_5/frame_11.png | Bin .../NSFW/Anims}/lvl_5/frame_12.png | Bin .../NSFW/Anims}/lvl_5/frame_13.png | Bin .../NSFW/Anims}/lvl_5/frame_14.png | Bin .../NSFW/Anims}/lvl_5/frame_15.png | Bin .../NSFW/Anims}/lvl_5/frame_16.png | Bin .../NSFW/Anims}/lvl_5/frame_17.png | Bin .../NSFW/Anims}/lvl_5/frame_18.png | Bin .../NSFW/Anims}/lvl_5/frame_19.png | Bin .../NSFW/Anims}/lvl_5/frame_2.png | Bin .../NSFW/Anims}/lvl_5/frame_20.png | Bin .../NSFW/Anims}/lvl_5/frame_21.png | Bin .../NSFW/Anims}/lvl_5/frame_22.png | Bin .../NSFW/Anims}/lvl_5/frame_23.png | Bin .../NSFW/Anims}/lvl_5/frame_24.png | Bin .../NSFW/Anims}/lvl_5/frame_25.png | Bin .../NSFW/Anims}/lvl_5/frame_26.png | Bin .../NSFW/Anims}/lvl_5/frame_27.png | Bin .../NSFW/Anims}/lvl_5/frame_3.png | Bin .../NSFW/Anims}/lvl_5/frame_4.png | Bin .../NSFW/Anims}/lvl_5/frame_5.png | Bin .../NSFW/Anims}/lvl_5/frame_6.png | Bin .../NSFW/Anims}/lvl_5/frame_7.png | Bin .../NSFW/Anims}/lvl_5/frame_8.png | Bin .../NSFW/Anims}/lvl_5/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.png | Bin .../NSFW/Anims}/lvl_6/frame_1.png | Bin .../NSFW/Anims}/lvl_6/frame_2.png | Bin .../NSFW/Anims}/lvl_6/frame_3.png | Bin .../NSFW/Anims}/lvl_6/frame_4.png | Bin .../NSFW/Anims}/lvl_6/frame_5.png | Bin .../NSFW/Anims}/lvl_6/frame_6.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.png | Bin .../NSFW/Anims}/lvl_7/frame_1.png | Bin .../NSFW/Anims}/lvl_7/frame_10.png | Bin .../NSFW/Anims}/lvl_7/frame_11.png | Bin .../NSFW/Anims}/lvl_7/frame_12.png | Bin .../NSFW/Anims}/lvl_7/frame_13.png | Bin .../NSFW/Anims}/lvl_7/frame_2.png | Bin .../NSFW/Anims}/lvl_7/frame_3.png | Bin .../NSFW/Anims}/lvl_7/frame_4.png | Bin .../NSFW/Anims}/lvl_7/frame_5.png | Bin .../NSFW/Anims}/lvl_7/frame_6.png | Bin .../NSFW/Anims}/lvl_7/frame_7.png | Bin .../NSFW/Anims}/lvl_7/frame_8.png | Bin .../NSFW/Anims}/lvl_7/frame_9.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_7/meta.txt | 0 .../NSFW/Anims}/lvl_8/frame_0.png | Bin .../NSFW/Anims}/lvl_8/frame_1.png | Bin .../NSFW/Anims}/lvl_8/frame_2.png | Bin .../NSFW/Anims}/lvl_8/frame_3.png | Bin .../NSFW/Anims}/lvl_8/frame_4.png | Bin .../NSFW/Anims}/lvl_8/frame_5.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.png | Bin .../NSFW/Anims}/lvl_9/frame_1.png | Bin .../NSFW/Anims}/lvl_9/frame_2.png | Bin .../NSFW/Anims}/lvl_9/frame_3.png | Bin .../NSFW/Anims}/lvl_9/frame_4.png | Bin .../NSFW/Anims}/lvl_9/frame_5.png | Bin .../NSFW/Anims}/lvl_9/frame_6.png | Bin .../NSFW/Anims}/lvl_9/frame_7.png | Bin .../nsfw => custom/NSFW/Anims}/lvl_9/meta.txt | 0 .../nsfw => custom/NSFW/Anims}/manifest.txt | 0 .../Animations/Levelup_128x64}/frame_00.png | Bin .../Animations/Levelup_128x64}/frame_01.png | Bin .../Animations/Levelup_128x64}/frame_02.png | Bin .../Animations/Levelup_128x64}/frame_03.png | Bin .../Animations/Levelup_128x64}/frame_04.png | Bin .../Animations/Levelup_128x64}/frame_05.png | Bin .../Animations/Levelup_128x64}/frame_06.png | Bin .../Animations/Levelup_128x64}/frame_07.png | Bin .../Animations/Levelup_128x64}/frame_08.png | Bin .../Animations/Levelup_128x64}/frame_09.png | Bin .../Animations/Levelup_128x64}/frame_10.png | Bin .../Animations/Levelup_128x64}/frame_11.png | Bin .../Animations/Levelup_128x64}/frame_12.png | Bin .../Animations/Levelup_128x64}/frame_13.png | Bin .../Animations/Levelup_128x64}/frame_14.png | Bin .../Animations/Levelup_128x64}/frame_15.png | Bin .../Animations/Levelup_128x64}/frame_16.png | Bin .../Animations/Levelup_128x64}/frame_17.png | Bin .../Animations/Levelup_128x64}/frame_18.png | Bin .../Animations/Levelup_128x64}/frame_19.png | Bin .../Animations/Levelup_128x64}/frame_20.png | Bin .../Animations/Levelup_128x64}/frame_21.png | Bin .../Animations/Levelup_128x64}/frame_22.png | Bin .../Animations/Levelup_128x64}/frame_23.png | Bin .../Animations/Levelup_128x64}/frame_24.png | Bin .../Animations/Levelup_128x64}/frame_25.png | Bin .../Animations/Levelup_128x64}/frame_26.png | Bin .../Animations/Levelup_128x64}/frame_27.png | Bin .../Animations/Levelup_128x64}/frame_28.png | Bin .../Animations/Levelup_128x64}/frame_29.png | Bin .../Animations/Levelup_128x64}/frame_30.png | Bin .../Animations/Levelup_128x64}/frame_31.png | Bin .../Animations/Levelup_128x64}/frame_rate | 0 .../NSFW/Icons/BLE/BLE_Pairing_128x64.png | Bin 0 -> 2610 bytes .../Icons/Dolphin/DolphinCommon_56x48.png | Bin 0 -> 3376 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 0 -> 5390 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 0 -> 4224 bytes .../NSFW/Icons/Passport/passport_DB.png | Bin 0 -> 852 bytes .../Icons/Passport/passport_bad_46x49.png} | Bin .../Icons/Passport/passport_happy_46x49.png | Bin 0 -> 6373 bytes .../Icons/Passport/passport_okay_46x49.png | Bin 0 -> 6373 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.png | Bin 0 -> 4862 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.png | Bin 0 -> 4882 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 0 -> 4466 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.png | Bin 0 -> 3798 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.png | Bin 0 -> 4092 bytes .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 0 -> 1864 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 0 -> 1895 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 0 -> 1874 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 0 -> 1863 bytes .../Icons/iButton/DolphinMafia_115x62.png | Bin 0 -> 6876 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.png | Bin 0 -> 5422 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.png | Bin 0 -> 5122 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 0 -> 4719 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.png | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.png | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.png | Bin .../{sfw => }/L1_Cry_128x64/frame_1.png | Bin .../{sfw => }/L1_Cry_128x64/frame_2.png | Bin .../{sfw => }/L1_Cry_128x64/frame_3.png | Bin .../{sfw => }/L1_Cry_128x64/frame_4.png | Bin .../{sfw => }/L1_Cry_128x64/frame_5.png | Bin .../{sfw => }/L1_Cry_128x64/frame_6.png | Bin .../{sfw => }/L1_Cry_128x64/frame_7.png | Bin .../external/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.png | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.png | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.png | Bin .../L1_Happy_holidays_128x64/frame_1.png | Bin .../L1_Happy_holidays_128x64/frame_10.png | Bin .../L1_Happy_holidays_128x64/frame_11.png | Bin .../L1_Happy_holidays_128x64/frame_12.png | Bin .../L1_Happy_holidays_128x64/frame_2.png | Bin .../L1_Happy_holidays_128x64/frame_3.png | Bin .../L1_Happy_holidays_128x64/frame_4.png | Bin .../L1_Happy_holidays_128x64/frame_5.png | Bin .../L1_Happy_holidays_128x64/frame_6.png | Bin .../L1_Happy_holidays_128x64/frame_7.png | Bin .../L1_Happy_holidays_128x64/frame_8.png | Bin .../L1_Happy_holidays_128x64/frame_9.png | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.png | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.png | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.png | Bin .../L1_Leaving_sad_128x64/frame_1.png | Bin .../L1_Leaving_sad_128x64/frame_10.png | Bin .../L1_Leaving_sad_128x64/frame_11.png | Bin .../L1_Leaving_sad_128x64/frame_12.png | Bin .../L1_Leaving_sad_128x64/frame_2.png | Bin .../L1_Leaving_sad_128x64/frame_3.png | Bin .../L1_Leaving_sad_128x64/frame_4.png | Bin .../L1_Leaving_sad_128x64/frame_5.png | Bin .../L1_Leaving_sad_128x64/frame_6.png | Bin .../L1_Leaving_sad_128x64/frame_7.png | Bin .../L1_Leaving_sad_128x64/frame_8.png | Bin .../L1_Leaving_sad_128x64/frame_9.png | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.png | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.png | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.png | Bin .../{sfw => }/L1_Mods_128x64/frame_1.png | Bin .../{sfw => }/L1_Mods_128x64/frame_10.png | Bin .../{sfw => }/L1_Mods_128x64/frame_11.png | Bin .../{sfw => }/L1_Mods_128x64/frame_12.png | Bin .../{sfw => }/L1_Mods_128x64/frame_13.png | Bin .../{sfw => }/L1_Mods_128x64/frame_14.png | Bin .../{sfw => }/L1_Mods_128x64/frame_15.png | Bin .../{sfw => }/L1_Mods_128x64/frame_16.png | Bin .../{sfw => }/L1_Mods_128x64/frame_17.png | Bin .../{sfw => }/L1_Mods_128x64/frame_18.png | Bin .../{sfw => }/L1_Mods_128x64/frame_19.png | Bin .../{sfw => }/L1_Mods_128x64/frame_2.png | Bin .../{sfw => }/L1_Mods_128x64/frame_20.png | Bin .../{sfw => }/L1_Mods_128x64/frame_21.png | Bin .../{sfw => }/L1_Mods_128x64/frame_22.png | Bin .../{sfw => }/L1_Mods_128x64/frame_23.png | Bin .../{sfw => }/L1_Mods_128x64/frame_24.png | Bin .../{sfw => }/L1_Mods_128x64/frame_25.png | Bin .../{sfw => }/L1_Mods_128x64/frame_26.png | Bin .../{sfw => }/L1_Mods_128x64/frame_27.png | Bin .../{sfw => }/L1_Mods_128x64/frame_28.png | Bin .../{sfw => }/L1_Mods_128x64/frame_29.png | Bin .../{sfw => }/L1_Mods_128x64/frame_3.png | Bin .../{sfw => }/L1_Mods_128x64/frame_30.png | Bin .../{sfw => }/L1_Mods_128x64/frame_31.png | Bin .../{sfw => }/L1_Mods_128x64/frame_32.png | Bin .../{sfw => }/L1_Mods_128x64/frame_33.png | Bin .../{sfw => }/L1_Mods_128x64/frame_34.png | Bin .../{sfw => }/L1_Mods_128x64/frame_35.png | Bin .../{sfw => }/L1_Mods_128x64/frame_36.png | Bin .../{sfw => }/L1_Mods_128x64/frame_37.png | Bin .../{sfw => }/L1_Mods_128x64/frame_38.png | Bin .../{sfw => }/L1_Mods_128x64/frame_39.png | Bin .../{sfw => }/L1_Mods_128x64/frame_4.png | Bin .../{sfw => }/L1_Mods_128x64/frame_40.png | Bin .../{sfw => }/L1_Mods_128x64/frame_5.png | Bin .../{sfw => }/L1_Mods_128x64/frame_6.png | Bin .../{sfw => }/L1_Mods_128x64/frame_7.png | Bin .../{sfw => }/L1_Mods_128x64/frame_8.png | Bin .../{sfw => }/L1_Mods_128x64/frame_9.png | Bin .../{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.png | Bin .../{sfw => }/L1_Painting_128x64/frame_1.png | Bin .../{sfw => }/L1_Painting_128x64/frame_10.png | Bin .../{sfw => }/L1_Painting_128x64/frame_11.png | Bin .../{sfw => }/L1_Painting_128x64/frame_2.png | Bin .../{sfw => }/L1_Painting_128x64/frame_3.png | Bin .../{sfw => }/L1_Painting_128x64/frame_4.png | Bin .../{sfw => }/L1_Painting_128x64/frame_5.png | Bin .../{sfw => }/L1_Painting_128x64/frame_6.png | Bin .../{sfw => }/L1_Painting_128x64/frame_7.png | Bin .../{sfw => }/L1_Painting_128x64/frame_8.png | Bin .../{sfw => }/L1_Painting_128x64/frame_9.png | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../L1_Read_books_128x64/frame_0.png | Bin .../L1_Read_books_128x64/frame_1.png | Bin .../L1_Read_books_128x64/frame_2.png | Bin .../L1_Read_books_128x64/frame_3.png | Bin .../L1_Read_books_128x64/frame_4.png | Bin .../L1_Read_books_128x64/frame_5.png | Bin .../L1_Read_books_128x64/frame_6.png | Bin .../L1_Read_books_128x64/frame_7.png | Bin .../L1_Read_books_128x64/frame_8.png | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.png | Bin .../{sfw => }/L1_Recording_128x51/frame_1.png | Bin .../L1_Recording_128x51/frame_10.png | Bin .../L1_Recording_128x51/frame_11.png | Bin .../{sfw => }/L1_Recording_128x51/frame_2.png | Bin .../{sfw => }/L1_Recording_128x51/frame_3.png | Bin .../{sfw => }/L1_Recording_128x51/frame_4.png | Bin .../{sfw => }/L1_Recording_128x51/frame_5.png | Bin .../{sfw => }/L1_Recording_128x51/frame_6.png | Bin .../{sfw => }/L1_Recording_128x51/frame_7.png | Bin .../{sfw => }/L1_Recording_128x51/frame_8.png | Bin .../{sfw => }/L1_Recording_128x51/frame_9.png | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.png | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.png | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.png | Bin .../L1_Sleigh_ride_128x64/frame_1.png | Bin .../L1_Sleigh_ride_128x64/frame_10.png | Bin .../L1_Sleigh_ride_128x64/frame_11.png | Bin .../L1_Sleigh_ride_128x64/frame_12.png | Bin .../L1_Sleigh_ride_128x64/frame_13.png | Bin .../L1_Sleigh_ride_128x64/frame_14.png | Bin .../L1_Sleigh_ride_128x64/frame_15.png | Bin .../L1_Sleigh_ride_128x64/frame_16.png | Bin .../L1_Sleigh_ride_128x64/frame_17.png | Bin .../L1_Sleigh_ride_128x64/frame_18.png | Bin .../L1_Sleigh_ride_128x64/frame_19.png | Bin .../L1_Sleigh_ride_128x64/frame_2.png | Bin .../L1_Sleigh_ride_128x64/frame_20.png | Bin .../L1_Sleigh_ride_128x64/frame_21.png | Bin .../L1_Sleigh_ride_128x64/frame_22.png | Bin .../L1_Sleigh_ride_128x64/frame_23.png | Bin .../L1_Sleigh_ride_128x64/frame_24.png | Bin .../L1_Sleigh_ride_128x64/frame_25.png | Bin .../L1_Sleigh_ride_128x64/frame_26.png | Bin .../L1_Sleigh_ride_128x64/frame_27.png | Bin .../L1_Sleigh_ride_128x64/frame_28.png | Bin .../L1_Sleigh_ride_128x64/frame_29.png | Bin .../L1_Sleigh_ride_128x64/frame_3.png | Bin .../L1_Sleigh_ride_128x64/frame_30.png | Bin .../L1_Sleigh_ride_128x64/frame_31.png | Bin .../L1_Sleigh_ride_128x64/frame_32.png | Bin .../L1_Sleigh_ride_128x64/frame_33.png | Bin .../L1_Sleigh_ride_128x64/frame_34.png | Bin .../L1_Sleigh_ride_128x64/frame_35.png | Bin .../L1_Sleigh_ride_128x64/frame_36.png | Bin .../L1_Sleigh_ride_128x64/frame_4.png | Bin .../L1_Sleigh_ride_128x64/frame_5.png | Bin .../L1_Sleigh_ride_128x64/frame_6.png | Bin .../L1_Sleigh_ride_128x64/frame_7.png | Bin .../L1_Sleigh_ride_128x64/frame_8.png | Bin .../L1_Sleigh_ride_128x64/frame_9.png | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.png | Bin .../{sfw => }/L1_Waves_128x50/frame_1.png | Bin .../{sfw => }/L1_Waves_128x50/frame_2.png | Bin .../{sfw => }/L1_Waves_128x50/frame_3.png | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.png | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.png | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../L2_Hacking_pc_128x64/frame_0.png | Bin .../L2_Hacking_pc_128x64/frame_1.png | Bin .../L2_Hacking_pc_128x64/frame_2.png | Bin .../L2_Hacking_pc_128x64/frame_3.png | Bin .../L2_Hacking_pc_128x64/frame_4.png | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.png | Bin .../L2_Soldering_128x64/frame_10.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.png | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.png | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.png | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.png | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.png | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.png | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.png | Bin .../L3_Hijack_radio_128x64/frame_1.png | Bin .../L3_Hijack_radio_128x64/frame_10.png | Bin .../L3_Hijack_radio_128x64/frame_11.png | Bin .../L3_Hijack_radio_128x64/frame_12.png | Bin .../L3_Hijack_radio_128x64/frame_13.png | Bin .../L3_Hijack_radio_128x64/frame_2.png | Bin .../L3_Hijack_radio_128x64/frame_3.png | Bin .../L3_Hijack_radio_128x64/frame_4.png | Bin .../L3_Hijack_radio_128x64/frame_5.png | Bin .../L3_Hijack_radio_128x64/frame_6.png | Bin .../L3_Hijack_radio_128x64/frame_7.png | Bin .../L3_Hijack_radio_128x64/frame_8.png | Bin .../L3_Hijack_radio_128x64/frame_9.png | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.png | Bin .../L3_Lab_research_128x54/frame_1.png | Bin .../L3_Lab_research_128x54/frame_10.png | Bin .../L3_Lab_research_128x54/frame_11.png | Bin .../L3_Lab_research_128x54/frame_12.png | Bin .../L3_Lab_research_128x54/frame_13.png | Bin .../L3_Lab_research_128x54/frame_2.png | Bin .../L3_Lab_research_128x54/frame_3.png | Bin .../L3_Lab_research_128x54/frame_4.png | Bin .../L3_Lab_research_128x54/frame_5.png | Bin .../L3_Lab_research_128x54/frame_6.png | Bin .../L3_Lab_research_128x54/frame_7.png | Bin .../L3_Lab_research_128x54/frame_8.png | Bin .../L3_Lab_research_128x54/frame_9.png | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../dolphin/external/{sfw => }/manifest.txt | 0 .../Levelup1_128x64_sfw/frame_00.png | Bin 1326 -> 0 bytes .../Levelup1_128x64_sfw/frame_01.png | Bin 1597 -> 0 bytes .../Levelup1_128x64_sfw/frame_02.png | Bin 1754 -> 0 bytes .../Levelup1_128x64_sfw/frame_03.png | Bin 1828 -> 0 bytes .../Levelup1_128x64_sfw/frame_04.png | Bin 1686 -> 0 bytes .../Levelup1_128x64_sfw/frame_05.png | Bin 1672 -> 0 bytes .../Levelup1_128x64_sfw/frame_06.png | Bin 1659 -> 0 bytes .../Levelup1_128x64_sfw/frame_07.png | Bin 1540 -> 0 bytes .../Levelup1_128x64_sfw/frame_08.png | Bin 1557 -> 0 bytes .../Levelup1_128x64_sfw/frame_09.png | Bin 1551 -> 0 bytes .../Levelup1_128x64_sfw/frame_10.png | Bin 1604 -> 0 bytes .../Animations/Levelup2_128x64_sfw/frame_rate | 1 - .../frame_00.png | Bin .../frame_01.png | Bin .../frame_02.png | Bin .../frame_03.png | Bin .../frame_04.png | Bin .../frame_05.png | Bin .../frame_06.png | Bin .../frame_07.png | Bin .../frame_08.png | Bin .../frame_09.png | Bin .../frame_10.png | Bin .../frame_rate | 0 assets/icons/BLE/BLE_Pairing_128x64.png | Bin 2610 -> 2307 bytes assets/icons/BLE/BLE_Pairing_128x64_sfw.png | Bin 2307 -> 0 bytes assets/icons/Dolphin/DolphinCommon_56x48.png | Bin 3376 -> 1416 bytes .../icons/Dolphin/DolphinCommon_56x48_sfw.png | Bin 1416 -> 0 bytes .../Infrared/DolphinReadingSuccess_59x63.png | Bin 5390 -> 1177 bytes .../DolphinReadingSuccess_59x63_sfw.png | Bin 1177 -> 0 bytes assets/icons/Interface/SmallArrowDown_4x7.png | Bin 8340 -> 0 bytes assets/icons/Interface/SmallArrowUp_4x7.png | Bin 8552 -> 0 bytes assets/icons/NFC/ArrowC_1_36x36.png | Bin 3692 -> 0 bytes assets/icons/NFC/Detailed_chip_17x13.png | Bin 981 -> 0 bytes assets/icons/NFC/Medium-chip-22x21.png | Bin 3740 -> 0 bytes .../icons/NFC/NFC_dolphin_emulation_47x61.png | Bin 4224 -> 1541 bytes .../NFC/NFC_dolphin_emulation_47x61_sfw.png | Bin 1541 -> 0 bytes assets/icons/NFC/Tap_reader_36x38.png | Bin 3748 -> 0 bytes assets/icons/Passport/passport_DB.png | Bin 852 -> 750 bytes assets/icons/Passport/passport_DB_sfw.png | Bin 750 -> 0 bytes .../Passport/passport_bad1_46x49_sfw.png | Bin 1237 -> 0 bytes .../Passport/passport_bad3_46x49_sfw.png | Bin 1304 -> 0 bytes ...2_46x49_sfw.png => passport_bad_46x49.png} | Bin .../Passport/passport_happy1_46x49_sfw.png | Bin 1296 -> 0 bytes .../Passport/passport_happy3_46x49_sfw.png | Bin 1348 -> 0 bytes ...46x49_sfw.png => passport_happy_46x49.png} | Bin .../Passport/passport_okay1_46x49_sfw.png | Bin 1244 -> 0 bytes .../Passport/passport_okay3_46x49_sfw.png | Bin 1304 -> 0 bytes ..._46x49_sfw.png => passport_okay_46x49.png} | Bin .../icons/RFID/RFIDDolphinReceive_97x61.png | Bin 4862 -> 1421 bytes .../RFID/RFIDDolphinReceive_97x61_sfw.png | Bin 1421 -> 0 bytes assets/icons/RFID/RFIDDolphinSend_97x61.png | Bin 4882 -> 1418 bytes .../icons/RFID/RFIDDolphinSend_97x61_sfw.png | Bin 1418 -> 0 bytes .../icons/RFID/RFIDDolphinSuccess_108x57.png | Bin 4466 -> 2681 bytes .../RFID/RFIDDolphinSuccess_108x57_sfw.png | Bin 2681 -> 0 bytes assets/icons/Settings/Cry_dolph_55x52.png | Bin 3798 -> 3898 bytes assets/icons/Settings/Cry_dolph_55x52_sfw.png | Bin 3898 -> 0 bytes assets/icons/StatusBar/Alert_9x8.png | Bin 3611 -> 0 bytes assets/icons/StatusBar/Attention_5x8.png | Bin 1690 -> 0 bytes ...g_9x10.png => Charging_lightning_9x10.png} | Bin ...0.png => Charging_lightning_mask_9x10.png} | Bin assets/icons/StatusBar/GameMode_11x8.png | Bin 3610 -> 0 bytes assets/icons/SubGhz/Scanning_123x52.png | Bin 4092 -> 1690 bytes assets/icons/SubGhz/Scanning_123x52_sfw.png | Bin 1690 -> 0 bytes assets/icons/U2F/Auth_62x31.png | Bin 1864 -> 3761 bytes assets/icons/U2F/Auth_62x31_sfw.png | Bin 3761 -> 0 bytes assets/icons/U2F/Connect_me_62x31.png | Bin 1895 -> 3767 bytes assets/icons/U2F/Connect_me_62x31_sfw.png | Bin 3767 -> 0 bytes assets/icons/U2F/Connected_62x31.png | Bin 1874 -> 3765 bytes assets/icons/U2F/Connected_62x31_sfw.png | Bin 3765 -> 0 bytes assets/icons/U2F/Error_62x31.png | Bin 1863 -> 3751 bytes assets/icons/U2F/Error_62x31_sfw.png | Bin 3751 -> 0 bytes assets/icons/iButton/DolphinMafia_115x62.png | Bin 6876 -> 2504 bytes .../icons/iButton/DolphinMafia_115x62_sfw.png | Bin 2504 -> 0 bytes assets/icons/iButton/DolphinNice_96x59.png | Bin 5422 -> 2459 bytes .../icons/iButton/DolphinNice_96x59_sfw.png | Bin 2459 -> 0 bytes assets/icons/iButton/DolphinWait_61x59.png | Bin 5122 -> 2023 bytes .../icons/iButton/DolphinWait_61x59_sfw.png | Bin 2023 -> 0 bytes .../iButtonDolphinVerySuccess_108x52.png | Bin 4719 -> 2157 bytes .../iButtonDolphinVerySuccess_108x52_sfw.png | Bin 2157 -> 0 bytes .../{sfw => }/L1_Boxing_128x64/frame_0.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_1.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_2.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_3.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_4.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_5.bm | Bin .../{sfw => }/L1_Boxing_128x64/frame_6.bm | Bin .../{sfw => }/L1_Boxing_128x64/meta.txt | 0 .../{sfw => }/L1_Cry_128x64/frame_0.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_1.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_2.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_3.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_4.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_5.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_6.bm | Bin .../{sfw => }/L1_Cry_128x64/frame_7.bm | Bin .../dolphin/{sfw => }/L1_Cry_128x64/meta.txt | 0 .../{sfw => }/L1_Furippa1_128x64/frame_0.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_1.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_10.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_11.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_12.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_13.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_14.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_15.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_16.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_17.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_18.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_2.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_3.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_4.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_5.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_6.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_7.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_8.bm | Bin .../{sfw => }/L1_Furippa1_128x64/frame_9.bm | Bin .../{sfw => }/L1_Furippa1_128x64/meta.txt | 0 .../L1_Happy_holidays_128x64/frame_0.bm | Bin .../L1_Happy_holidays_128x64/frame_1.bm | Bin .../L1_Happy_holidays_128x64/frame_10.bm | Bin .../L1_Happy_holidays_128x64/frame_11.bm | Bin .../L1_Happy_holidays_128x64/frame_12.bm | Bin .../L1_Happy_holidays_128x64/frame_2.bm | Bin .../L1_Happy_holidays_128x64/frame_3.bm | Bin .../L1_Happy_holidays_128x64/frame_4.bm | Bin .../L1_Happy_holidays_128x64/frame_5.bm | Bin .../L1_Happy_holidays_128x64/frame_6.bm | Bin .../L1_Happy_holidays_128x64/frame_7.bm | Bin .../L1_Happy_holidays_128x64/frame_8.bm | Bin .../L1_Happy_holidays_128x64/frame_9.bm | Bin .../L1_Happy_holidays_128x64/meta.txt | 0 .../{sfw => }/L1_Laptop_128x51/frame_0.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_1.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_2.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_3.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_4.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_5.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_6.bm | Bin .../{sfw => }/L1_Laptop_128x51/frame_7.bm | Bin .../{sfw => }/L1_Laptop_128x51/meta.txt | 0 .../L1_Leaving_sad_128x64/frame_0.bm | Bin .../L1_Leaving_sad_128x64/frame_1.bm | Bin .../L1_Leaving_sad_128x64/frame_10.bm | Bin .../L1_Leaving_sad_128x64/frame_11.bm | Bin .../L1_Leaving_sad_128x64/frame_12.bm | Bin .../L1_Leaving_sad_128x64/frame_2.bm | Bin .../L1_Leaving_sad_128x64/frame_3.bm | Bin .../L1_Leaving_sad_128x64/frame_4.bm | Bin .../L1_Leaving_sad_128x64/frame_5.bm | Bin .../L1_Leaving_sad_128x64/frame_6.bm | Bin .../L1_Leaving_sad_128x64/frame_7.bm | Bin .../L1_Leaving_sad_128x64/frame_8.bm | Bin .../L1_Leaving_sad_128x64/frame_9.bm | Bin .../{sfw => }/L1_Leaving_sad_128x64/meta.txt | 0 .../{sfw => }/L1_Mad_fist_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/frame_9.bm | Bin .../{sfw => }/L1_Mad_fist_128x64/meta.txt | 0 .../{sfw => }/L1_Mods_128x64/frame_0.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_1.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_10.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_11.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_12.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_13.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_14.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_15.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_16.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_17.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_18.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_19.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_2.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_20.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_21.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_22.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_23.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_24.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_25.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_26.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_27.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_28.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_29.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_3.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_30.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_31.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_32.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_33.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_34.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_35.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_36.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_37.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_38.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_39.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_4.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_40.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_5.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_6.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_7.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_8.bm | Bin .../{sfw => }/L1_Mods_128x64/frame_9.bm | Bin .../dolphin/{sfw => }/L1_Mods_128x64/meta.txt | 0 .../{sfw => }/L1_Painting_128x64/frame_0.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_1.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_10.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_11.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_2.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_3.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_4.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_5.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_6.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_7.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_8.bm | Bin .../{sfw => }/L1_Painting_128x64/frame_9.bm | Bin .../{sfw => }/L1_Painting_128x64/meta.txt | 0 .../{sfw => }/L1_Read_books_128x64/frame_0.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_1.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_2.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_3.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_4.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_5.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_6.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_7.bm | Bin .../{sfw => }/L1_Read_books_128x64/frame_8.bm | Bin .../{sfw => }/L1_Read_books_128x64/meta.txt | 0 .../{sfw => }/L1_Recording_128x51/frame_0.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_1.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_10.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_11.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_2.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_3.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_4.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_5.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_6.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_7.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_8.bm | Bin .../{sfw => }/L1_Recording_128x51/frame_9.bm | Bin .../{sfw => }/L1_Recording_128x51/meta.txt | 0 .../{sfw => }/L1_Sleep_128x64/frame_0.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_1.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_2.bm | Bin .../{sfw => }/L1_Sleep_128x64/frame_3.bm | Bin .../{sfw => }/L1_Sleep_128x64/meta.txt | 0 .../L1_Sleigh_ride_128x64/frame_0.bm | Bin .../L1_Sleigh_ride_128x64/frame_1.bm | Bin .../L1_Sleigh_ride_128x64/frame_10.bm | Bin .../L1_Sleigh_ride_128x64/frame_11.bm | Bin .../L1_Sleigh_ride_128x64/frame_12.bm | Bin .../L1_Sleigh_ride_128x64/frame_13.bm | Bin .../L1_Sleigh_ride_128x64/frame_14.bm | Bin .../L1_Sleigh_ride_128x64/frame_15.bm | Bin .../L1_Sleigh_ride_128x64/frame_16.bm | Bin .../L1_Sleigh_ride_128x64/frame_17.bm | Bin .../L1_Sleigh_ride_128x64/frame_18.bm | Bin .../L1_Sleigh_ride_128x64/frame_19.bm | Bin .../L1_Sleigh_ride_128x64/frame_2.bm | Bin .../L1_Sleigh_ride_128x64/frame_20.bm | Bin .../L1_Sleigh_ride_128x64/frame_21.bm | Bin .../L1_Sleigh_ride_128x64/frame_22.bm | Bin .../L1_Sleigh_ride_128x64/frame_23.bm | Bin .../L1_Sleigh_ride_128x64/frame_24.bm | Bin .../L1_Sleigh_ride_128x64/frame_25.bm | Bin .../L1_Sleigh_ride_128x64/frame_26.bm | Bin .../L1_Sleigh_ride_128x64/frame_27.bm | Bin .../L1_Sleigh_ride_128x64/frame_28.bm | Bin .../L1_Sleigh_ride_128x64/frame_29.bm | Bin .../L1_Sleigh_ride_128x64/frame_3.bm | Bin .../L1_Sleigh_ride_128x64/frame_30.bm | Bin .../L1_Sleigh_ride_128x64/frame_31.bm | Bin .../L1_Sleigh_ride_128x64/frame_32.bm | Bin .../L1_Sleigh_ride_128x64/frame_33.bm | Bin .../L1_Sleigh_ride_128x64/frame_34.bm | Bin .../L1_Sleigh_ride_128x64/frame_35.bm | Bin .../L1_Sleigh_ride_128x64/frame_36.bm | Bin .../L1_Sleigh_ride_128x64/frame_4.bm | Bin .../L1_Sleigh_ride_128x64/frame_5.bm | Bin .../L1_Sleigh_ride_128x64/frame_6.bm | Bin .../L1_Sleigh_ride_128x64/frame_7.bm | Bin .../L1_Sleigh_ride_128x64/frame_8.bm | Bin .../L1_Sleigh_ride_128x64/frame_9.bm | Bin .../{sfw => }/L1_Sleigh_ride_128x64/meta.txt | 0 .../{sfw => }/L1_Waves_128x50/frame_0.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_1.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_2.bm | Bin .../{sfw => }/L1_Waves_128x50/frame_3.bm | Bin .../{sfw => }/L1_Waves_128x50/meta.txt | 0 .../{sfw => }/L2_Furippa2_128x64/frame_0.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_1.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_10.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_11.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_12.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_13.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_14.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_15.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_16.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_17.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_18.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_2.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_3.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_4.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_5.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_6.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_7.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_8.bm | Bin .../{sfw => }/L2_Furippa2_128x64/frame_9.bm | Bin .../{sfw => }/L2_Furippa2_128x64/meta.txt | 0 .../{sfw => }/L2_Hacking_pc_128x64/frame_0.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_1.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_2.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_3.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/frame_4.bm | Bin .../{sfw => }/L2_Hacking_pc_128x64/meta.txt | 0 .../{sfw => }/L2_Soldering_128x64/frame_0.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_1.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_10.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_2.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_3.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_4.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_5.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_6.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_7.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_8.bm | Bin .../{sfw => }/L2_Soldering_128x64/frame_9.bm | Bin .../{sfw => }/L2_Soldering_128x64/meta.txt | 0 .../{sfw => }/L2_Wake_up_128x64/frame_0.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_1.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_10.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_11.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_12.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_13.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_14.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_15.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_16.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_17.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_18.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_19.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_2.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_20.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_3.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_4.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_5.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_6.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_7.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_8.bm | Bin .../{sfw => }/L2_Wake_up_128x64/frame_9.bm | Bin .../{sfw => }/L2_Wake_up_128x64/meta.txt | 0 .../{sfw => }/L3_Furippa3_128x64/frame_0.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_1.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_10.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_11.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_12.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_13.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_14.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_15.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_16.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_17.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_18.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_2.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_3.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_4.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_5.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_6.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_7.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_8.bm | Bin .../{sfw => }/L3_Furippa3_128x64/frame_9.bm | Bin .../{sfw => }/L3_Furippa3_128x64/meta.txt | 0 .../L3_Hijack_radio_128x64/frame_0.bm | Bin .../L3_Hijack_radio_128x64/frame_1.bm | Bin .../L3_Hijack_radio_128x64/frame_10.bm | Bin .../L3_Hijack_radio_128x64/frame_11.bm | Bin .../L3_Hijack_radio_128x64/frame_12.bm | Bin .../L3_Hijack_radio_128x64/frame_13.bm | Bin .../L3_Hijack_radio_128x64/frame_2.bm | Bin .../L3_Hijack_radio_128x64/frame_3.bm | Bin .../L3_Hijack_radio_128x64/frame_4.bm | Bin .../L3_Hijack_radio_128x64/frame_5.bm | Bin .../L3_Hijack_radio_128x64/frame_6.bm | Bin .../L3_Hijack_radio_128x64/frame_7.bm | Bin .../L3_Hijack_radio_128x64/frame_8.bm | Bin .../L3_Hijack_radio_128x64/frame_9.bm | Bin .../{sfw => }/L3_Hijack_radio_128x64/meta.txt | 0 .../L3_Lab_research_128x54/frame_0.bm | Bin .../L3_Lab_research_128x54/frame_1.bm | Bin .../L3_Lab_research_128x54/frame_10.bm | Bin .../L3_Lab_research_128x54/frame_11.bm | Bin .../L3_Lab_research_128x54/frame_12.bm | Bin .../L3_Lab_research_128x54/frame_13.bm | Bin .../L3_Lab_research_128x54/frame_2.bm | Bin .../L3_Lab_research_128x54/frame_3.bm | Bin .../L3_Lab_research_128x54/frame_4.bm | Bin .../L3_Lab_research_128x54/frame_5.bm | Bin .../L3_Lab_research_128x54/frame_6.bm | Bin .../L3_Lab_research_128x54/frame_7.bm | Bin .../L3_Lab_research_128x54/frame_8.bm | Bin .../L3_Lab_research_128x54/frame_9.bm | Bin .../{sfw => }/L3_Lab_research_128x54/meta.txt | 0 .../resources/dolphin/{sfw => }/manifest.txt | 0 .../Anims}/PaxGod_TikTok_Marketing/frame_0.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_1.bm | Bin .../PaxGod_TikTok_Marketing/frame_10.bm | Bin .../PaxGod_TikTok_Marketing/frame_11.bm | Bin .../PaxGod_TikTok_Marketing/frame_12.bm | Bin .../PaxGod_TikTok_Marketing/frame_13.bm | Bin .../PaxGod_TikTok_Marketing/frame_14.bm | Bin .../PaxGod_TikTok_Marketing/frame_15.bm | Bin .../PaxGod_TikTok_Marketing/frame_16.bm | Bin .../PaxGod_TikTok_Marketing/frame_17.bm | Bin .../PaxGod_TikTok_Marketing/frame_18.bm | Bin .../PaxGod_TikTok_Marketing/frame_19.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_2.bm | Bin .../PaxGod_TikTok_Marketing/frame_20.bm | Bin .../PaxGod_TikTok_Marketing/frame_21.bm | Bin .../Anims/PaxGod_TikTok_Marketing/frame_22.bm | Bin 0 -> 472 bytes .../Anims/PaxGod_TikTok_Marketing/frame_23.bm | Bin 0 -> 462 bytes .../Anims/PaxGod_TikTok_Marketing/frame_24.bm | Bin 0 -> 487 bytes .../Anims/PaxGod_TikTok_Marketing/frame_25.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_26.bm | Bin 0 -> 483 bytes .../Anims/PaxGod_TikTok_Marketing/frame_27.bm | Bin 0 -> 488 bytes .../Anims}/PaxGod_TikTok_Marketing/frame_3.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_4.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_5.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_6.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_7.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_8.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/frame_9.bm | Bin .../Anims}/PaxGod_TikTok_Marketing/meta.txt | 3 +- .../NSFW/Anims}/lvl_1/frame_0.bm | Bin .../NSFW/Anims}/lvl_1/frame_1.bm | Bin .../NSFW/Anims}/lvl_1/frame_10.bm | Bin .../NSFW/Anims}/lvl_1/frame_11.bm | Bin .../NSFW/Anims}/lvl_1/frame_12.bm | Bin .../NSFW/Anims}/lvl_1/frame_13.bm | Bin .../NSFW/Anims}/lvl_1/frame_14.bm | Bin .../NSFW/Anims}/lvl_1/frame_15.bm | Bin .../NSFW/Anims}/lvl_1/frame_16.bm | Bin .../NSFW/Anims}/lvl_1/frame_17.bm | Bin .../NSFW/Anims}/lvl_1/frame_18.bm | Bin .../NSFW/Anims}/lvl_1/frame_19.bm | Bin .../NSFW/Anims}/lvl_1/frame_2.bm | Bin .../NSFW/Anims}/lvl_1/frame_20.bm | Bin .../NSFW/Anims}/lvl_1/frame_21.bm | Bin .../NSFW/Anims}/lvl_1/frame_22.bm | Bin .../NSFW/Anims}/lvl_1/frame_23.bm | Bin .../NSFW/Anims}/lvl_1/frame_24.bm | Bin .../NSFW/Anims}/lvl_1/frame_25.bm | Bin .../NSFW/Anims}/lvl_1/frame_26.bm | Bin .../NSFW/Anims}/lvl_1/frame_27.bm | Bin .../NSFW/Anims}/lvl_1/frame_28.bm | Bin .../NSFW/Anims}/lvl_1/frame_29.bm | Bin .../NSFW/Anims}/lvl_1/frame_3.bm | Bin .../NSFW/Anims}/lvl_1/frame_30.bm | Bin .../NSFW/Anims}/lvl_1/frame_4.bm | Bin .../NSFW/Anims}/lvl_1/frame_5.bm | Bin .../NSFW/Anims}/lvl_1/frame_6.bm | Bin .../NSFW/Anims}/lvl_1/frame_7.bm | Bin .../NSFW/Anims}/lvl_1/frame_8.bm | Bin .../NSFW/Anims}/lvl_1/frame_9.bm | Bin .../NSFW/Anims}/lvl_1/meta.txt | 0 .../NSFW/Anims}/lvl_10/frame_0.bm | Bin .../NSFW/Anims}/lvl_10/frame_1.bm | Bin .../NSFW/Anims}/lvl_10/frame_10.bm | Bin .../NSFW/Anims}/lvl_10/frame_11.bm | Bin .../NSFW/Anims}/lvl_10/frame_12.bm | Bin .../NSFW/Anims}/lvl_10/frame_13.bm | Bin .../NSFW/Anims}/lvl_10/frame_14.bm | Bin .../NSFW/Anims}/lvl_10/frame_15.bm | Bin .../NSFW/Anims}/lvl_10/frame_16.bm | Bin .../NSFW/Anims}/lvl_10/frame_17.bm | Bin .../NSFW/Anims}/lvl_10/frame_18.bm | Bin .../NSFW/Anims}/lvl_10/frame_19.bm | Bin .../NSFW/Anims}/lvl_10/frame_2.bm | Bin .../NSFW/Anims}/lvl_10/frame_20.bm | Bin .../NSFW/Anims}/lvl_10/frame_21.bm | Bin .../NSFW/Anims}/lvl_10/frame_22.bm | Bin .../NSFW/Anims}/lvl_10/frame_23.bm | Bin .../NSFW/Anims}/lvl_10/frame_24.bm | Bin .../NSFW/Anims}/lvl_10/frame_25.bm | Bin .../NSFW/Anims}/lvl_10/frame_26.bm | Bin .../NSFW/Anims}/lvl_10/frame_27.bm | Bin .../NSFW/Anims}/lvl_10/frame_3.bm | Bin .../NSFW/Anims}/lvl_10/frame_4.bm | Bin .../NSFW/Anims}/lvl_10/frame_5.bm | Bin .../NSFW/Anims}/lvl_10/frame_6.bm | Bin .../NSFW/Anims}/lvl_10/frame_7.bm | Bin .../NSFW/Anims}/lvl_10/frame_8.bm | Bin .../NSFW/Anims}/lvl_10/frame_9.bm | Bin .../NSFW/Anims}/lvl_10/meta.txt | 0 .../NSFW/Anims}/lvl_11/frame_0.bm | Bin .../NSFW/Anims}/lvl_11/frame_1.bm | Bin .../NSFW/Anims}/lvl_11/frame_10.bm | Bin .../NSFW/Anims}/lvl_11/frame_11.bm | Bin .../NSFW/Anims}/lvl_11/frame_12.bm | Bin .../NSFW/Anims}/lvl_11/frame_13.bm | Bin .../NSFW/Anims}/lvl_11/frame_14.bm | Bin .../NSFW/Anims}/lvl_11/frame_15.bm | Bin .../NSFW/Anims}/lvl_11/frame_16.bm | Bin .../NSFW/Anims}/lvl_11/frame_17.bm | Bin .../NSFW/Anims}/lvl_11/frame_18.bm | Bin .../NSFW/Anims}/lvl_11/frame_19.bm | Bin .../NSFW/Anims}/lvl_11/frame_2.bm | Bin .../NSFW/Anims}/lvl_11/frame_20.bm | Bin .../NSFW/Anims}/lvl_11/frame_21.bm | Bin .../NSFW/Anims}/lvl_11/frame_22.bm | Bin .../NSFW/Anims}/lvl_11/frame_23.bm | Bin .../NSFW/Anims}/lvl_11/frame_24.bm | Bin .../NSFW/Anims}/lvl_11/frame_25.bm | Bin .../NSFW/Anims}/lvl_11/frame_26.bm | Bin .../NSFW/Anims}/lvl_11/frame_27.bm | Bin .../NSFW/Anims}/lvl_11/frame_28.bm | Bin .../NSFW/Anims}/lvl_11/frame_29.bm | Bin .../NSFW/Anims}/lvl_11/frame_3.bm | Bin .../NSFW/Anims}/lvl_11/frame_30.bm | Bin .../NSFW/Anims}/lvl_11/frame_31.bm | Bin .../NSFW/Anims}/lvl_11/frame_32.bm | Bin .../NSFW/Anims}/lvl_11/frame_33.bm | Bin .../NSFW/Anims}/lvl_11/frame_34.bm | Bin .../NSFW/Anims}/lvl_11/frame_35.bm | Bin .../NSFW/Anims}/lvl_11/frame_36.bm | Bin .../NSFW/Anims}/lvl_11/frame_37.bm | Bin .../NSFW/Anims}/lvl_11/frame_38.bm | Bin .../NSFW/Anims}/lvl_11/frame_39.bm | Bin .../NSFW/Anims}/lvl_11/frame_4.bm | Bin .../NSFW/Anims}/lvl_11/frame_40.bm | Bin .../NSFW/Anims}/lvl_11/frame_41.bm | Bin .../NSFW/Anims}/lvl_11/frame_42.bm | Bin .../NSFW/Anims}/lvl_11/frame_43.bm | Bin .../NSFW/Anims}/lvl_11/frame_44.bm | Bin .../NSFW/Anims}/lvl_11/frame_45.bm | Bin .../NSFW/Anims}/lvl_11/frame_46.bm | Bin .../NSFW/Anims}/lvl_11/frame_47.bm | Bin .../NSFW/Anims}/lvl_11/frame_48.bm | Bin .../NSFW/Anims}/lvl_11/frame_49.bm | Bin .../NSFW/Anims}/lvl_11/frame_5.bm | Bin .../NSFW/Anims}/lvl_11/frame_6.bm | Bin .../NSFW/Anims}/lvl_11/frame_7.bm | Bin .../NSFW/Anims}/lvl_11/frame_8.bm | Bin .../NSFW/Anims}/lvl_11/frame_9.bm | Bin .../NSFW/Anims}/lvl_11/meta.txt | 0 .../NSFW/Anims}/lvl_12/frame_0.bm | Bin .../NSFW/Anims}/lvl_12/frame_1.bm | Bin .../NSFW/Anims}/lvl_12/frame_10.bm | Bin .../NSFW/Anims}/lvl_12/frame_11.bm | Bin .../NSFW/Anims}/lvl_12/frame_12.bm | Bin .../NSFW/Anims}/lvl_12/frame_13.bm | Bin .../NSFW/Anims}/lvl_12/frame_14.bm | Bin .../NSFW/Anims}/lvl_12/frame_15.bm | Bin .../NSFW/Anims}/lvl_12/frame_2.bm | Bin .../NSFW/Anims}/lvl_12/frame_3.bm | Bin .../NSFW/Anims}/lvl_12/frame_4.bm | Bin .../NSFW/Anims}/lvl_12/frame_5.bm | Bin .../NSFW/Anims}/lvl_12/frame_6.bm | Bin .../NSFW/Anims}/lvl_12/frame_7.bm | Bin .../NSFW/Anims}/lvl_12/frame_8.bm | Bin .../NSFW/Anims}/lvl_12/frame_9.bm | Bin .../NSFW/Anims}/lvl_12/meta.txt | 0 .../NSFW/Anims}/lvl_13/frame_0.bm | Bin .../NSFW/Anims}/lvl_13/frame_1.bm | Bin .../NSFW/Anims}/lvl_13/frame_10.bm | Bin .../NSFW/Anims}/lvl_13/frame_2.bm | Bin .../NSFW/Anims}/lvl_13/frame_3.bm | Bin .../NSFW/Anims}/lvl_13/frame_4.bm | Bin .../NSFW/Anims}/lvl_13/frame_5.bm | Bin .../NSFW/Anims}/lvl_13/frame_6.bm | Bin .../NSFW/Anims}/lvl_13/frame_7.bm | Bin .../NSFW/Anims}/lvl_13/frame_8.bm | Bin .../NSFW/Anims}/lvl_13/frame_9.bm | Bin .../NSFW/Anims}/lvl_13/meta.txt | 0 .../NSFW/Anims}/lvl_14/frame_0.bm | Bin .../NSFW/Anims}/lvl_14/frame_1.bm | Bin .../NSFW/Anims}/lvl_14/frame_2.bm | Bin .../NSFW/Anims}/lvl_14/frame_3.bm | Bin .../NSFW/Anims}/lvl_14/frame_4.bm | Bin .../NSFW/Anims}/lvl_14/frame_5.bm | Bin .../NSFW/Anims}/lvl_14/frame_6.bm | Bin .../NSFW/Anims}/lvl_14/frame_7.bm | Bin .../NSFW/Anims}/lvl_14/meta.txt | 0 .../NSFW/Anims}/lvl_15/frame_0.bm | Bin .../NSFW/Anims}/lvl_15/frame_1.bm | Bin .../NSFW/Anims}/lvl_15/frame_10.bm | Bin .../NSFW/Anims}/lvl_15/frame_11.bm | Bin .../NSFW/Anims}/lvl_15/frame_12.bm | Bin .../NSFW/Anims}/lvl_15/frame_13.bm | Bin .../NSFW/Anims}/lvl_15/frame_14.bm | Bin .../NSFW/Anims}/lvl_15/frame_15.bm | Bin .../NSFW/Anims}/lvl_15/frame_16.bm | Bin .../NSFW/Anims}/lvl_15/frame_17.bm | Bin .../NSFW/Anims}/lvl_15/frame_18.bm | Bin .../NSFW/Anims}/lvl_15/frame_19.bm | Bin .../NSFW/Anims}/lvl_15/frame_2.bm | Bin .../NSFW/Anims}/lvl_15/frame_20.bm | Bin .../NSFW/Anims}/lvl_15/frame_21.bm | Bin .../NSFW/Anims}/lvl_15/frame_3.bm | Bin .../NSFW/Anims}/lvl_15/frame_4.bm | Bin .../NSFW/Anims}/lvl_15/frame_5.bm | Bin .../NSFW/Anims}/lvl_15/frame_6.bm | Bin .../NSFW/Anims}/lvl_15/frame_7.bm | Bin .../NSFW/Anims}/lvl_15/frame_8.bm | Bin .../NSFW/Anims}/lvl_15/frame_9.bm | Bin .../NSFW/Anims}/lvl_15/meta.txt | 0 .../NSFW/Anims}/lvl_16/frame_0.bm | Bin .../NSFW/Anims}/lvl_16/frame_1.bm | Bin .../NSFW/Anims}/lvl_16/frame_10.bm | Bin .../NSFW/Anims}/lvl_16/frame_11.bm | Bin .../NSFW/Anims}/lvl_16/frame_12.bm | Bin .../NSFW/Anims}/lvl_16/frame_13.bm | Bin .../NSFW/Anims}/lvl_16/frame_14.bm | Bin .../NSFW/Anims}/lvl_16/frame_15.bm | Bin .../NSFW/Anims}/lvl_16/frame_16.bm | Bin .../NSFW/Anims}/lvl_16/frame_17.bm | Bin .../NSFW/Anims}/lvl_16/frame_18.bm | Bin .../NSFW/Anims}/lvl_16/frame_19.bm | Bin .../NSFW/Anims}/lvl_16/frame_2.bm | Bin .../NSFW/Anims}/lvl_16/frame_20.bm | Bin .../NSFW/Anims}/lvl_16/frame_3.bm | Bin .../NSFW/Anims}/lvl_16/frame_4.bm | Bin .../NSFW/Anims}/lvl_16/frame_5.bm | Bin .../NSFW/Anims}/lvl_16/frame_6.bm | Bin .../NSFW/Anims}/lvl_16/frame_7.bm | Bin .../NSFW/Anims}/lvl_16/frame_8.bm | Bin .../NSFW/Anims}/lvl_16/frame_9.bm | Bin .../NSFW/Anims}/lvl_16/meta.txt | 0 .../NSFW/Anims}/lvl_17/frame_0.bm | Bin .../NSFW/Anims}/lvl_17/frame_1.bm | Bin .../NSFW/Anims}/lvl_17/frame_10.bm | Bin .../NSFW/Anims}/lvl_17/frame_11.bm | Bin .../NSFW/Anims}/lvl_17/frame_12.bm | Bin .../NSFW/Anims}/lvl_17/frame_13.bm | Bin .../NSFW/Anims}/lvl_17/frame_14.bm | Bin .../NSFW/Anims}/lvl_17/frame_15.bm | Bin .../NSFW/Anims}/lvl_17/frame_16.bm | Bin .../NSFW/Anims}/lvl_17/frame_17.bm | Bin .../NSFW/Anims}/lvl_17/frame_18.bm | Bin .../NSFW/Anims}/lvl_17/frame_19.bm | Bin .../NSFW/Anims}/lvl_17/frame_2.bm | Bin .../NSFW/Anims}/lvl_17/frame_20.bm | Bin .../NSFW/Anims}/lvl_17/frame_21.bm | Bin .../NSFW/Anims}/lvl_17/frame_22.bm | Bin .../NSFW/Anims}/lvl_17/frame_23.bm | Bin .../NSFW/Anims}/lvl_17/frame_24.bm | Bin .../NSFW/Anims}/lvl_17/frame_25.bm | Bin .../NSFW/Anims}/lvl_17/frame_26.bm | Bin .../NSFW/Anims}/lvl_17/frame_27.bm | Bin .../NSFW/Anims}/lvl_17/frame_28.bm | Bin .../NSFW/Anims}/lvl_17/frame_29.bm | Bin .../NSFW/Anims}/lvl_17/frame_3.bm | Bin .../NSFW/Anims}/lvl_17/frame_30.bm | Bin .../NSFW/Anims}/lvl_17/frame_31.bm | Bin .../NSFW/Anims}/lvl_17/frame_4.bm | Bin .../NSFW/Anims}/lvl_17/frame_5.bm | Bin .../NSFW/Anims}/lvl_17/frame_6.bm | Bin .../NSFW/Anims}/lvl_17/frame_7.bm | Bin .../NSFW/Anims}/lvl_17/frame_8.bm | Bin .../NSFW/Anims}/lvl_17/frame_9.bm | Bin .../NSFW/Anims}/lvl_17/meta.txt | 0 .../NSFW/Anims}/lvl_18/frame_0.bm | Bin .../NSFW/Anims}/lvl_18/frame_1.bm | Bin .../NSFW/Anims}/lvl_18/frame_10.bm | Bin .../NSFW/Anims}/lvl_18/frame_11.bm | Bin .../NSFW/Anims}/lvl_18/frame_12.bm | Bin .../NSFW/Anims}/lvl_18/frame_13.bm | Bin .../NSFW/Anims}/lvl_18/frame_14.bm | Bin .../NSFW/Anims}/lvl_18/frame_15.bm | Bin .../NSFW/Anims}/lvl_18/frame_16.bm | Bin .../NSFW/Anims}/lvl_18/frame_17.bm | Bin .../NSFW/Anims}/lvl_18/frame_18.bm | Bin .../NSFW/Anims}/lvl_18/frame_19.bm | Bin .../NSFW/Anims}/lvl_18/frame_2.bm | Bin .../NSFW/Anims}/lvl_18/frame_20.bm | Bin .../NSFW/Anims}/lvl_18/frame_21.bm | Bin .../NSFW/Anims}/lvl_18/frame_22.bm | Bin .../NSFW/Anims}/lvl_18/frame_3.bm | Bin .../NSFW/Anims}/lvl_18/frame_4.bm | Bin .../NSFW/Anims}/lvl_18/frame_5.bm | Bin .../NSFW/Anims}/lvl_18/frame_6.bm | Bin .../NSFW/Anims}/lvl_18/frame_7.bm | Bin .../NSFW/Anims}/lvl_18/frame_8.bm | Bin .../NSFW/Anims}/lvl_18/frame_9.bm | Bin .../NSFW/Anims}/lvl_18/meta.txt | 0 .../NSFW/Anims}/lvl_19/frame_0.bm | Bin .../NSFW/Anims}/lvl_19/frame_1.bm | Bin .../NSFW/Anims}/lvl_19/frame_10.bm | Bin .../NSFW/Anims}/lvl_19/frame_11.bm | Bin .../NSFW/Anims}/lvl_19/frame_12.bm | Bin .../NSFW/Anims}/lvl_19/frame_13.bm | Bin .../NSFW/Anims}/lvl_19/frame_14.bm | Bin .../NSFW/Anims}/lvl_19/frame_15.bm | Bin .../NSFW/Anims}/lvl_19/frame_16.bm | Bin .../NSFW/Anims}/lvl_19/frame_17.bm | Bin .../NSFW/Anims}/lvl_19/frame_18.bm | Bin .../NSFW/Anims}/lvl_19/frame_19.bm | Bin .../NSFW/Anims}/lvl_19/frame_2.bm | Bin .../NSFW/Anims}/lvl_19/frame_20.bm | Bin .../NSFW/Anims}/lvl_19/frame_21.bm | Bin .../NSFW/Anims}/lvl_19/frame_3.bm | Bin .../NSFW/Anims}/lvl_19/frame_4.bm | Bin .../NSFW/Anims}/lvl_19/frame_5.bm | Bin .../NSFW/Anims}/lvl_19/frame_6.bm | Bin .../NSFW/Anims}/lvl_19/frame_7.bm | Bin .../NSFW/Anims}/lvl_19/frame_8.bm | Bin .../NSFW/Anims}/lvl_19/frame_9.bm | Bin .../NSFW/Anims}/lvl_19/meta.txt | 0 .../NSFW/Anims}/lvl_2/frame_0.bm | Bin .../NSFW/Anims}/lvl_2/frame_1.bm | Bin .../NSFW/Anims}/lvl_2/frame_10.bm | Bin .../NSFW/Anims}/lvl_2/frame_11.bm | Bin .../NSFW/Anims}/lvl_2/frame_12.bm | Bin .../NSFW/Anims}/lvl_2/frame_13.bm | Bin .../NSFW/Anims}/lvl_2/frame_14.bm | Bin .../NSFW/Anims}/lvl_2/frame_2.bm | Bin .../NSFW/Anims}/lvl_2/frame_3.bm | Bin .../NSFW/Anims}/lvl_2/frame_4.bm | Bin .../NSFW/Anims}/lvl_2/frame_5.bm | Bin .../NSFW/Anims}/lvl_2/frame_6.bm | Bin .../NSFW/Anims}/lvl_2/frame_7.bm | Bin .../NSFW/Anims}/lvl_2/frame_8.bm | Bin .../NSFW/Anims}/lvl_2/frame_9.bm | Bin .../NSFW/Anims}/lvl_2/meta.txt | 0 .../NSFW/Anims}/lvl_20/frame_0.bm | Bin .../NSFW/Anims}/lvl_20/frame_1.bm | Bin .../NSFW/Anims}/lvl_20/frame_10.bm | Bin .../NSFW/Anims}/lvl_20/frame_11.bm | Bin .../NSFW/Anims}/lvl_20/frame_12.bm | Bin .../NSFW/Anims}/lvl_20/frame_13.bm | Bin .../NSFW/Anims}/lvl_20/frame_2.bm | Bin .../NSFW/Anims}/lvl_20/frame_3.bm | Bin .../NSFW/Anims}/lvl_20/frame_4.bm | Bin .../NSFW/Anims}/lvl_20/frame_5.bm | Bin .../NSFW/Anims}/lvl_20/frame_6.bm | Bin .../NSFW/Anims}/lvl_20/frame_7.bm | Bin .../NSFW/Anims}/lvl_20/frame_8.bm | Bin .../NSFW/Anims}/lvl_20/frame_9.bm | Bin .../NSFW/Anims}/lvl_20/meta.txt | 0 .../NSFW/Anims}/lvl_21/frame_0.bm | Bin .../NSFW/Anims}/lvl_21/frame_1.bm | Bin .../NSFW/Anims}/lvl_21/frame_2.bm | Bin .../NSFW/Anims}/lvl_21/frame_3.bm | Bin .../NSFW/Anims}/lvl_21/frame_4.bm | Bin .../NSFW/Anims}/lvl_21/frame_5.bm | Bin .../NSFW/Anims}/lvl_21/meta.txt | 0 .../NSFW/Anims}/lvl_22/frame_0.bm | Bin .../NSFW/Anims}/lvl_22/frame_1.bm | Bin .../NSFW/Anims}/lvl_22/frame_10.bm | Bin .../NSFW/Anims}/lvl_22/frame_11.bm | Bin .../NSFW/Anims}/lvl_22/frame_12.bm | Bin .../NSFW/Anims}/lvl_22/frame_13.bm | Bin .../NSFW/Anims}/lvl_22/frame_14.bm | Bin .../NSFW/Anims}/lvl_22/frame_15.bm | Bin .../NSFW/Anims}/lvl_22/frame_16.bm | Bin .../NSFW/Anims}/lvl_22/frame_17.bm | Bin .../NSFW/Anims}/lvl_22/frame_18.bm | Bin .../NSFW/Anims}/lvl_22/frame_19.bm | Bin .../NSFW/Anims}/lvl_22/frame_2.bm | Bin .../NSFW/Anims}/lvl_22/frame_20.bm | Bin .../NSFW/Anims}/lvl_22/frame_21.bm | Bin .../NSFW/Anims}/lvl_22/frame_22.bm | Bin .../NSFW/Anims}/lvl_22/frame_23.bm | Bin .../NSFW/Anims}/lvl_22/frame_24.bm | Bin .../NSFW/Anims}/lvl_22/frame_25.bm | Bin .../NSFW/Anims}/lvl_22/frame_26.bm | Bin .../NSFW/Anims}/lvl_22/frame_27.bm | Bin .../NSFW/Anims}/lvl_22/frame_28.bm | Bin .../NSFW/Anims}/lvl_22/frame_29.bm | Bin .../NSFW/Anims}/lvl_22/frame_3.bm | Bin .../NSFW/Anims}/lvl_22/frame_30.bm | Bin .../NSFW/Anims}/lvl_22/frame_31.bm | Bin .../NSFW/Anims}/lvl_22/frame_32.bm | Bin .../NSFW/Anims}/lvl_22/frame_33.bm | Bin .../NSFW/Anims}/lvl_22/frame_34.bm | Bin .../NSFW/Anims}/lvl_22/frame_35.bm | Bin .../NSFW/Anims}/lvl_22/frame_36.bm | Bin .../NSFW/Anims}/lvl_22/frame_37.bm | Bin .../NSFW/Anims}/lvl_22/frame_38.bm | Bin .../NSFW/Anims}/lvl_22/frame_39.bm | Bin .../NSFW/Anims}/lvl_22/frame_4.bm | Bin .../NSFW/Anims}/lvl_22/frame_40.bm | Bin .../NSFW/Anims}/lvl_22/frame_41.bm | Bin .../NSFW/Anims}/lvl_22/frame_42.bm | Bin .../NSFW/Anims}/lvl_22/frame_43.bm | Bin .../NSFW/Anims}/lvl_22/frame_44.bm | Bin .../NSFW/Anims}/lvl_22/frame_45.bm | Bin .../NSFW/Anims}/lvl_22/frame_46.bm | Bin .../NSFW/Anims}/lvl_22/frame_47.bm | Bin .../NSFW/Anims}/lvl_22/frame_48.bm | Bin .../NSFW/Anims}/lvl_22/frame_49.bm | Bin .../NSFW/Anims}/lvl_22/frame_5.bm | Bin .../NSFW/Anims}/lvl_22/frame_50.bm | Bin .../NSFW/Anims}/lvl_22/frame_51.bm | Bin .../NSFW/Anims}/lvl_22/frame_52.bm | Bin .../NSFW/Anims}/lvl_22/frame_53.bm | Bin .../NSFW/Anims}/lvl_22/frame_54.bm | Bin .../NSFW/Anims}/lvl_22/frame_55.bm | Bin .../NSFW/Anims}/lvl_22/frame_56.bm | Bin .../NSFW/Anims}/lvl_22/frame_57.bm | Bin .../NSFW/Anims}/lvl_22/frame_58.bm | Bin .../NSFW/Anims}/lvl_22/frame_59.bm | Bin .../NSFW/Anims}/lvl_22/frame_6.bm | Bin .../NSFW/Anims}/lvl_22/frame_7.bm | Bin .../NSFW/Anims}/lvl_22/frame_8.bm | Bin .../NSFW/Anims}/lvl_22/frame_9.bm | Bin .../NSFW/Anims}/lvl_22/meta.txt | 0 .../NSFW/Anims}/lvl_23/frame_0.bm | Bin .../NSFW/Anims}/lvl_23/frame_1.bm | Bin .../NSFW/Anims}/lvl_23/frame_10.bm | Bin .../NSFW/Anims}/lvl_23/frame_11.bm | Bin .../NSFW/Anims}/lvl_23/frame_12.bm | Bin .../NSFW/Anims}/lvl_23/frame_13.bm | Bin .../NSFW/Anims}/lvl_23/frame_14.bm | Bin .../NSFW/Anims}/lvl_23/frame_15.bm | Bin .../NSFW/Anims}/lvl_23/frame_16.bm | Bin .../NSFW/Anims}/lvl_23/frame_2.bm | Bin .../NSFW/Anims}/lvl_23/frame_3.bm | Bin .../NSFW/Anims}/lvl_23/frame_4.bm | Bin .../NSFW/Anims}/lvl_23/frame_5.bm | Bin .../NSFW/Anims}/lvl_23/frame_6.bm | Bin .../NSFW/Anims}/lvl_23/frame_7.bm | Bin .../NSFW/Anims}/lvl_23/frame_8.bm | Bin .../NSFW/Anims}/lvl_23/frame_9.bm | Bin .../NSFW/Anims}/lvl_23/meta.txt | 0 .../NSFW/Anims}/lvl_24/frame_0.bm | Bin .../NSFW/Anims}/lvl_24/frame_1.bm | Bin .../NSFW/Anims}/lvl_24/frame_10.bm | Bin .../NSFW/Anims}/lvl_24/frame_11.bm | Bin .../NSFW/Anims}/lvl_24/frame_12.bm | Bin .../NSFW/Anims}/lvl_24/frame_13.bm | Bin .../NSFW/Anims}/lvl_24/frame_14.bm | Bin .../NSFW/Anims}/lvl_24/frame_15.bm | Bin .../NSFW/Anims}/lvl_24/frame_16.bm | Bin .../NSFW/Anims}/lvl_24/frame_17.bm | Bin .../NSFW/Anims}/lvl_24/frame_18.bm | Bin .../NSFW/Anims}/lvl_24/frame_19.bm | Bin .../NSFW/Anims}/lvl_24/frame_2.bm | Bin .../NSFW/Anims}/lvl_24/frame_20.bm | Bin .../NSFW/Anims}/lvl_24/frame_21.bm | Bin .../NSFW/Anims}/lvl_24/frame_22.bm | Bin .../NSFW/Anims}/lvl_24/frame_23.bm | Bin .../NSFW/Anims}/lvl_24/frame_24.bm | Bin .../NSFW/Anims}/lvl_24/frame_25.bm | Bin .../NSFW/Anims}/lvl_24/frame_26.bm | Bin .../NSFW/Anims}/lvl_24/frame_27.bm | Bin .../NSFW/Anims}/lvl_24/frame_28.bm | Bin .../NSFW/Anims}/lvl_24/frame_29.bm | Bin .../NSFW/Anims}/lvl_24/frame_3.bm | Bin .../NSFW/Anims}/lvl_24/frame_4.bm | Bin .../NSFW/Anims}/lvl_24/frame_5.bm | Bin .../NSFW/Anims}/lvl_24/frame_6.bm | Bin .../NSFW/Anims}/lvl_24/frame_7.bm | Bin .../NSFW/Anims}/lvl_24/frame_8.bm | Bin .../NSFW/Anims}/lvl_24/frame_9.bm | Bin .../NSFW/Anims}/lvl_24/meta.txt | 0 .../NSFW/Anims}/lvl_25/frame_0.bm | Bin .../NSFW/Anims}/lvl_25/frame_1.bm | Bin .../NSFW/Anims}/lvl_25/frame_10.bm | Bin .../NSFW/Anims}/lvl_25/frame_11.bm | Bin .../NSFW/Anims}/lvl_25/frame_12.bm | Bin .../NSFW/Anims}/lvl_25/frame_13.bm | Bin .../NSFW/Anims}/lvl_25/frame_14.bm | Bin .../NSFW/Anims}/lvl_25/frame_15.bm | Bin .../NSFW/Anims}/lvl_25/frame_16.bm | Bin .../NSFW/Anims}/lvl_25/frame_17.bm | Bin .../NSFW/Anims}/lvl_25/frame_18.bm | Bin .../NSFW/Anims}/lvl_25/frame_19.bm | Bin .../NSFW/Anims}/lvl_25/frame_2.bm | Bin .../NSFW/Anims}/lvl_25/frame_20.bm | Bin .../NSFW/Anims}/lvl_25/frame_21.bm | Bin .../NSFW/Anims}/lvl_25/frame_22.bm | Bin .../NSFW/Anims}/lvl_25/frame_23.bm | Bin .../NSFW/Anims}/lvl_25/frame_24.bm | Bin .../NSFW/Anims}/lvl_25/frame_25.bm | Bin .../NSFW/Anims}/lvl_25/frame_26.bm | Bin .../NSFW/Anims}/lvl_25/frame_27.bm | Bin .../NSFW/Anims}/lvl_25/frame_28.bm | Bin .../NSFW/Anims}/lvl_25/frame_29.bm | Bin .../NSFW/Anims}/lvl_25/frame_3.bm | Bin .../NSFW/Anims}/lvl_25/frame_30.bm | Bin .../NSFW/Anims}/lvl_25/frame_31.bm | Bin .../NSFW/Anims}/lvl_25/frame_32.bm | Bin .../NSFW/Anims}/lvl_25/frame_33.bm | Bin .../NSFW/Anims}/lvl_25/frame_34.bm | Bin .../NSFW/Anims}/lvl_25/frame_35.bm | Bin .../NSFW/Anims}/lvl_25/frame_4.bm | Bin .../NSFW/Anims}/lvl_25/frame_5.bm | Bin .../NSFW/Anims}/lvl_25/frame_6.bm | Bin .../NSFW/Anims}/lvl_25/frame_7.bm | Bin .../NSFW/Anims}/lvl_25/frame_8.bm | Bin .../NSFW/Anims}/lvl_25/frame_9.bm | Bin .../NSFW/Anims}/lvl_25/meta.txt | 0 .../NSFW/Anims}/lvl_26/frame_0.bm | Bin .../NSFW/Anims}/lvl_26/frame_1.bm | Bin .../NSFW/Anims}/lvl_26/frame_10.bm | Bin .../NSFW/Anims}/lvl_26/frame_11.bm | Bin .../NSFW/Anims}/lvl_26/frame_2.bm | Bin .../NSFW/Anims}/lvl_26/frame_3.bm | Bin .../NSFW/Anims}/lvl_26/frame_4.bm | Bin .../NSFW/Anims}/lvl_26/frame_5.bm | Bin .../NSFW/Anims}/lvl_26/frame_6.bm | Bin .../NSFW/Anims}/lvl_26/frame_7.bm | Bin .../NSFW/Anims}/lvl_26/frame_8.bm | Bin .../NSFW/Anims}/lvl_26/frame_9.bm | Bin .../NSFW/Anims}/lvl_26/meta.txt | 0 .../NSFW/Anims}/lvl_27/frame_0.bm | Bin .../NSFW/Anims}/lvl_27/frame_1.bm | Bin .../NSFW/Anims}/lvl_27/frame_10.bm | Bin .../NSFW/Anims}/lvl_27/frame_11.bm | Bin .../NSFW/Anims}/lvl_27/frame_12.bm | Bin .../NSFW/Anims}/lvl_27/frame_13.bm | Bin .../NSFW/Anims}/lvl_27/frame_14.bm | Bin .../NSFW/Anims}/lvl_27/frame_15.bm | Bin .../NSFW/Anims}/lvl_27/frame_16.bm | Bin .../NSFW/Anims}/lvl_27/frame_17.bm | Bin .../NSFW/Anims}/lvl_27/frame_18.bm | Bin .../NSFW/Anims}/lvl_27/frame_19.bm | Bin .../NSFW/Anims}/lvl_27/frame_2.bm | Bin .../NSFW/Anims}/lvl_27/frame_20.bm | Bin .../NSFW/Anims}/lvl_27/frame_21.bm | Bin .../NSFW/Anims}/lvl_27/frame_3.bm | Bin .../NSFW/Anims}/lvl_27/frame_4.bm | Bin .../NSFW/Anims}/lvl_27/frame_5.bm | Bin .../NSFW/Anims}/lvl_27/frame_6.bm | Bin .../NSFW/Anims}/lvl_27/frame_7.bm | Bin .../NSFW/Anims}/lvl_27/frame_8.bm | Bin .../NSFW/Anims}/lvl_27/frame_9.bm | Bin .../NSFW/Anims}/lvl_27/meta.txt | 0 .../NSFW/Anims}/lvl_28/frame_0.bm | Bin .../NSFW/Anims}/lvl_28/frame_1.bm | Bin .../NSFW/Anims}/lvl_28/frame_2.bm | Bin .../NSFW/Anims}/lvl_28/frame_3.bm | Bin .../NSFW/Anims}/lvl_28/frame_4.bm | Bin .../NSFW/Anims}/lvl_28/frame_5.bm | Bin .../NSFW/Anims}/lvl_28/meta.txt | 0 .../NSFW/Anims}/lvl_29/frame_0.bm | Bin .../NSFW/Anims}/lvl_29/frame_1.bm | Bin .../NSFW/Anims}/lvl_29/frame_10.bm | Bin .../NSFW/Anims}/lvl_29/frame_11.bm | Bin .../NSFW/Anims}/lvl_29/frame_12.bm | Bin .../NSFW/Anims}/lvl_29/frame_13.bm | Bin .../NSFW/Anims}/lvl_29/frame_14.bm | Bin .../NSFW/Anims}/lvl_29/frame_15.bm | Bin .../NSFW/Anims}/lvl_29/frame_16.bm | Bin .../NSFW/Anims}/lvl_29/frame_17.bm | Bin .../NSFW/Anims}/lvl_29/frame_18.bm | Bin .../NSFW/Anims}/lvl_29/frame_19.bm | Bin .../NSFW/Anims}/lvl_29/frame_2.bm | Bin .../NSFW/Anims}/lvl_29/frame_20.bm | Bin .../NSFW/Anims}/lvl_29/frame_21.bm | Bin .../NSFW/Anims}/lvl_29/frame_22.bm | Bin .../NSFW/Anims}/lvl_29/frame_23.bm | Bin .../NSFW/Anims}/lvl_29/frame_24.bm | Bin .../NSFW/Anims}/lvl_29/frame_25.bm | Bin .../NSFW/Anims}/lvl_29/frame_26.bm | Bin .../NSFW/Anims}/lvl_29/frame_27.bm | Bin .../NSFW/Anims}/lvl_29/frame_28.bm | Bin .../NSFW/Anims}/lvl_29/frame_29.bm | Bin .../NSFW/Anims}/lvl_29/frame_3.bm | Bin .../NSFW/Anims}/lvl_29/frame_30.bm | Bin .../NSFW/Anims}/lvl_29/frame_31.bm | Bin .../NSFW/Anims}/lvl_29/frame_32.bm | Bin .../NSFW/Anims}/lvl_29/frame_33.bm | Bin .../NSFW/Anims}/lvl_29/frame_34.bm | Bin .../NSFW/Anims}/lvl_29/frame_35.bm | Bin .../NSFW/Anims}/lvl_29/frame_36.bm | Bin .../NSFW/Anims}/lvl_29/frame_37.bm | Bin .../NSFW/Anims}/lvl_29/frame_38.bm | Bin .../NSFW/Anims}/lvl_29/frame_39.bm | Bin .../NSFW/Anims}/lvl_29/frame_4.bm | Bin .../NSFW/Anims}/lvl_29/frame_40.bm | Bin .../NSFW/Anims}/lvl_29/frame_41.bm | Bin .../NSFW/Anims}/lvl_29/frame_42.bm | Bin .../NSFW/Anims}/lvl_29/frame_43.bm | Bin .../NSFW/Anims}/lvl_29/frame_44.bm | Bin .../NSFW/Anims}/lvl_29/frame_45.bm | Bin .../NSFW/Anims}/lvl_29/frame_46.bm | Bin .../NSFW/Anims}/lvl_29/frame_47.bm | Bin .../NSFW/Anims}/lvl_29/frame_48.bm | Bin .../NSFW/Anims}/lvl_29/frame_49.bm | Bin .../NSFW/Anims}/lvl_29/frame_5.bm | Bin .../NSFW/Anims}/lvl_29/frame_50.bm | Bin .../NSFW/Anims}/lvl_29/frame_51.bm | Bin .../NSFW/Anims}/lvl_29/frame_6.bm | Bin .../NSFW/Anims}/lvl_29/frame_7.bm | Bin .../NSFW/Anims}/lvl_29/frame_8.bm | Bin .../NSFW/Anims}/lvl_29/frame_9.bm | Bin .../NSFW/Anims}/lvl_29/meta.txt | 0 .../NSFW/Anims}/lvl_3/frame_0.bm | Bin .../NSFW/Anims}/lvl_3/frame_1.bm | Bin .../NSFW/Anims}/lvl_3/frame_10.bm | Bin .../NSFW/Anims}/lvl_3/frame_11.bm | Bin .../NSFW/Anims}/lvl_3/frame_12.bm | Bin .../NSFW/Anims}/lvl_3/frame_13.bm | Bin .../NSFW/Anims}/lvl_3/frame_14.bm | Bin .../NSFW/Anims}/lvl_3/frame_2.bm | Bin .../NSFW/Anims}/lvl_3/frame_3.bm | Bin .../NSFW/Anims}/lvl_3/frame_4.bm | Bin .../NSFW/Anims}/lvl_3/frame_5.bm | Bin .../NSFW/Anims}/lvl_3/frame_6.bm | Bin .../NSFW/Anims}/lvl_3/frame_7.bm | Bin .../NSFW/Anims}/lvl_3/frame_8.bm | Bin .../NSFW/Anims}/lvl_3/frame_9.bm | Bin .../NSFW/Anims}/lvl_3/meta.txt | 0 .../NSFW/Anims}/lvl_30/frame_0.bm | Bin .../NSFW/Anims}/lvl_30/frame_1.bm | Bin .../NSFW/Anims}/lvl_30/frame_10.bm | Bin .../NSFW/Anims}/lvl_30/frame_11.bm | Bin .../NSFW/Anims}/lvl_30/frame_12.bm | Bin .../NSFW/Anims}/lvl_30/frame_13.bm | Bin .../NSFW/Anims}/lvl_30/frame_14.bm | Bin .../NSFW/Anims}/lvl_30/frame_15.bm | Bin .../NSFW/Anims}/lvl_30/frame_16.bm | Bin .../NSFW/Anims}/lvl_30/frame_17.bm | Bin .../NSFW/Anims}/lvl_30/frame_18.bm | Bin .../NSFW/Anims}/lvl_30/frame_19.bm | Bin .../NSFW/Anims}/lvl_30/frame_2.bm | Bin .../NSFW/Anims}/lvl_30/frame_20.bm | Bin .../NSFW/Anims}/lvl_30/frame_21.bm | Bin .../NSFW/Anims}/lvl_30/frame_22.bm | Bin .../NSFW/Anims}/lvl_30/frame_23.bm | Bin .../NSFW/Anims}/lvl_30/frame_24.bm | Bin .../NSFW/Anims}/lvl_30/frame_25.bm | Bin .../NSFW/Anims}/lvl_30/frame_26.bm | Bin .../NSFW/Anims}/lvl_30/frame_27.bm | Bin .../NSFW/Anims}/lvl_30/frame_28.bm | Bin .../NSFW/Anims}/lvl_30/frame_29.bm | Bin .../NSFW/Anims}/lvl_30/frame_3.bm | Bin .../NSFW/Anims}/lvl_30/frame_30.bm | Bin .../NSFW/Anims}/lvl_30/frame_31.bm | Bin .../NSFW/Anims}/lvl_30/frame_32.bm | Bin .../NSFW/Anims}/lvl_30/frame_33.bm | Bin .../NSFW/Anims}/lvl_30/frame_34.bm | Bin .../NSFW/Anims}/lvl_30/frame_35.bm | Bin .../NSFW/Anims}/lvl_30/frame_36.bm | Bin .../NSFW/Anims}/lvl_30/frame_37.bm | Bin .../NSFW/Anims}/lvl_30/frame_38.bm | Bin .../NSFW/Anims}/lvl_30/frame_39.bm | Bin .../NSFW/Anims}/lvl_30/frame_4.bm | Bin .../NSFW/Anims}/lvl_30/frame_40.bm | Bin .../NSFW/Anims}/lvl_30/frame_41.bm | Bin .../NSFW/Anims}/lvl_30/frame_42.bm | Bin .../NSFW/Anims}/lvl_30/frame_43.bm | Bin .../NSFW/Anims}/lvl_30/frame_44.bm | Bin .../NSFW/Anims}/lvl_30/frame_45.bm | Bin .../NSFW/Anims}/lvl_30/frame_46.bm | Bin .../NSFW/Anims}/lvl_30/frame_47.bm | Bin .../NSFW/Anims}/lvl_30/frame_48.bm | Bin .../NSFW/Anims}/lvl_30/frame_49.bm | Bin .../NSFW/Anims}/lvl_30/frame_5.bm | Bin .../NSFW/Anims}/lvl_30/frame_6.bm | Bin .../NSFW/Anims}/lvl_30/frame_7.bm | Bin .../NSFW/Anims}/lvl_30/frame_8.bm | Bin .../NSFW/Anims}/lvl_30/frame_9.bm | Bin .../NSFW/Anims}/lvl_30/meta.txt | 0 .../NSFW/Anims}/lvl_4/frame_0.bm | Bin .../NSFW/Anims}/lvl_4/frame_1.bm | Bin .../NSFW/Anims}/lvl_4/frame_10.bm | Bin .../NSFW/Anims}/lvl_4/frame_11.bm | Bin .../NSFW/Anims}/lvl_4/frame_12.bm | Bin .../NSFW/Anims}/lvl_4/frame_13.bm | Bin .../NSFW/Anims}/lvl_4/frame_14.bm | Bin .../NSFW/Anims}/lvl_4/frame_15.bm | Bin .../NSFW/Anims}/lvl_4/frame_16.bm | Bin .../NSFW/Anims}/lvl_4/frame_17.bm | Bin .../NSFW/Anims}/lvl_4/frame_18.bm | Bin .../NSFW/Anims}/lvl_4/frame_19.bm | Bin .../NSFW/Anims}/lvl_4/frame_2.bm | Bin .../NSFW/Anims}/lvl_4/frame_3.bm | Bin .../NSFW/Anims}/lvl_4/frame_4.bm | Bin .../NSFW/Anims}/lvl_4/frame_5.bm | Bin .../NSFW/Anims}/lvl_4/frame_6.bm | Bin .../NSFW/Anims}/lvl_4/frame_7.bm | Bin .../NSFW/Anims}/lvl_4/frame_8.bm | Bin .../NSFW/Anims}/lvl_4/frame_9.bm | Bin .../NSFW/Anims}/lvl_4/meta.txt | 0 .../NSFW/Anims}/lvl_5/frame_0.bm | Bin .../NSFW/Anims}/lvl_5/frame_1.bm | Bin .../NSFW/Anims}/lvl_5/frame_10.bm | Bin .../NSFW/Anims}/lvl_5/frame_11.bm | Bin .../NSFW/Anims}/lvl_5/frame_12.bm | Bin .../NSFW/Anims}/lvl_5/frame_13.bm | Bin .../NSFW/Anims}/lvl_5/frame_14.bm | Bin .../NSFW/Anims}/lvl_5/frame_15.bm | Bin .../NSFW/Anims}/lvl_5/frame_16.bm | Bin .../NSFW/Anims}/lvl_5/frame_17.bm | Bin .../NSFW/Anims}/lvl_5/frame_18.bm | Bin .../NSFW/Anims}/lvl_5/frame_19.bm | Bin .../NSFW/Anims}/lvl_5/frame_2.bm | Bin .../NSFW/Anims}/lvl_5/frame_20.bm | Bin .../NSFW/Anims}/lvl_5/frame_21.bm | Bin .../NSFW/Anims}/lvl_5/frame_22.bm | Bin .../NSFW/Anims}/lvl_5/frame_23.bm | Bin .../NSFW/Anims}/lvl_5/frame_24.bm | Bin .../NSFW/Anims}/lvl_5/frame_25.bm | Bin .../NSFW/Anims}/lvl_5/frame_26.bm | Bin .../NSFW/Anims}/lvl_5/frame_27.bm | Bin .../NSFW/Anims}/lvl_5/frame_3.bm | Bin .../NSFW/Anims}/lvl_5/frame_4.bm | Bin .../NSFW/Anims}/lvl_5/frame_5.bm | Bin .../NSFW/Anims}/lvl_5/frame_6.bm | Bin .../NSFW/Anims}/lvl_5/frame_7.bm | Bin .../NSFW/Anims}/lvl_5/frame_8.bm | Bin .../NSFW/Anims}/lvl_5/frame_9.bm | Bin .../NSFW/Anims}/lvl_5/meta.txt | 0 .../NSFW/Anims}/lvl_6/frame_0.bm | Bin .../NSFW/Anims}/lvl_6/frame_1.bm | Bin .../NSFW/Anims}/lvl_6/frame_2.bm | Bin .../NSFW/Anims}/lvl_6/frame_3.bm | Bin .../NSFW/Anims}/lvl_6/frame_4.bm | Bin .../NSFW/Anims}/lvl_6/frame_5.bm | Bin .../NSFW/Anims}/lvl_6/frame_6.bm | Bin .../NSFW/Anims}/lvl_6/meta.txt | 0 .../NSFW/Anims}/lvl_7/frame_0.bm | Bin .../NSFW/Anims}/lvl_7/frame_1.bm | Bin .../NSFW/Anims}/lvl_7/frame_10.bm | Bin .../NSFW/Anims}/lvl_7/frame_11.bm | Bin .../NSFW/Anims}/lvl_7/frame_12.bm | Bin .../NSFW/Anims}/lvl_7/frame_13.bm | Bin .../NSFW/Anims}/lvl_7/frame_2.bm | Bin .../NSFW/Anims}/lvl_7/frame_3.bm | Bin .../NSFW/Anims}/lvl_7/frame_4.bm | Bin .../NSFW/Anims}/lvl_7/frame_5.bm | Bin .../NSFW/Anims}/lvl_7/frame_6.bm | Bin .../NSFW/Anims}/lvl_7/frame_7.bm | Bin .../NSFW/Anims}/lvl_7/frame_8.bm | Bin .../NSFW/Anims}/lvl_7/frame_9.bm | Bin .../NSFW/Anims}/lvl_7/meta.txt | 2 +- .../NSFW/Anims}/lvl_8/frame_0.bm | Bin .../NSFW/Anims}/lvl_8/frame_1.bm | Bin .../NSFW/Anims}/lvl_8/frame_2.bm | Bin .../NSFW/Anims}/lvl_8/frame_3.bm | Bin .../NSFW/Anims}/lvl_8/frame_4.bm | Bin .../NSFW/Anims}/lvl_8/frame_5.bm | Bin .../NSFW/Anims}/lvl_8/meta.txt | 0 .../NSFW/Anims}/lvl_9/frame_0.bm | Bin .../NSFW/Anims}/lvl_9/frame_1.bm | Bin .../NSFW/Anims}/lvl_9/frame_2.bm | Bin .../NSFW/Anims}/lvl_9/frame_3.bm | Bin .../NSFW/Anims}/lvl_9/frame_4.bm | Bin .../NSFW/Anims}/lvl_9/frame_5.bm | Bin .../NSFW/Anims}/lvl_9/frame_6.bm | Bin .../NSFW/Anims}/lvl_9/frame_7.bm | Bin .../NSFW/Anims}/lvl_9/meta.txt | 0 .../NSFW/Anims}/manifest.txt | 0 .../Animations/Levelup_128x64/frame_00.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_01.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_02.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_03.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_04.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_05.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_06.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_07.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_08.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_09.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_10.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_11.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_12.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_13.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_14.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_15.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_16.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_17.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_18.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_19.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_20.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_21.bm | Bin 0 -> 217 bytes .../Animations/Levelup_128x64/frame_22.bm | Bin 0 -> 219 bytes .../Animations/Levelup_128x64/frame_23.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_24.bm | Bin 0 -> 221 bytes .../Animations/Levelup_128x64/frame_25.bm | Bin 0 -> 223 bytes .../Animations/Levelup_128x64/frame_26.bm | Bin 0 -> 225 bytes .../Animations/Levelup_128x64/frame_27.bm | Bin 0 -> 227 bytes .../Animations/Levelup_128x64/frame_28.bm | Bin 0 -> 228 bytes .../Animations/Levelup_128x64/frame_29.bm | Bin 0 -> 222 bytes .../Animations/Levelup_128x64/frame_30.bm | Bin 0 -> 224 bytes .../Animations/Levelup_128x64/frame_31.bm | Bin 0 -> 518 bytes .../NSFW/Icons/Animations/Levelup_128x64/meta | Bin 0 -> 16 bytes .../NSFW/Icons/BLE/BLE_Pairing_128x64.bmx | Bin 0 -> 480 bytes .../Icons/Dolphin/DolphinCommon_56x48.bmx | Bin 0 -> 325 bytes .../Infrared/DolphinReadingSuccess_59x63.bmx | Bin 0 -> 513 bytes .../Icons/NFC/NFC_dolphin_emulation_47x61.bmx | Bin 0 -> 375 bytes .../NSFW/Icons/Passport/passport_DB.bmx | Bin 0 -> 286 bytes .../Icons/Passport/passport_bad_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_happy_46x49.bmx | Bin 0 -> 297 bytes .../Icons/Passport/passport_okay_46x49.bmx | Bin 0 -> 297 bytes .../Icons/RFID/RFIDDolphinReceive_97x61.bmx | Bin 0 -> 525 bytes .../NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx | Bin 0 -> 525 bytes .../Icons/RFID/RFIDDolphinSuccess_108x57.bmx | Bin 0 -> 502 bytes .../NSFW/Icons/Settings/Cry_dolph_55x52.bmx | Bin 0 -> 352 bytes .../NSFW/Icons/SubGhz/Scanning_123x52.bmx | Bin 0 -> 458 bytes .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 0 -> 257 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 0 -> 257 bytes .../Icons/iButton/DolphinMafia_115x62.bmx | Bin 0 -> 684 bytes .../NSFW/Icons/iButton/DolphinNice_96x59.bmx | Bin 0 -> 609 bytes .../NSFW/Icons/iButton/DolphinWait_61x59.bmx | Bin 0 -> 481 bytes .../iButtonDolphinVerySuccess_108x52.bmx | Bin 0 -> 482 bytes scripts/asset_packer.py | 70 +++-- scripts/assets.py | 35 +-- 2348 files changed, 216 insertions(+), 298 deletions(-) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_52.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_53.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_54.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_55.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_56.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_57.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_58.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_59.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_50.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_51.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_28.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_29.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_30.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_31.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_32.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_33.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_34.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_35.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_36.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_37.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_38.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_39.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_40.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_41.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_42.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_43.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_44.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_45.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_46.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_47.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_48.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_49.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_14.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_15.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_16.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_17.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_18.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_19.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_20.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_21.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_22.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_23.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_24.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_25.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_26.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_27.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_10.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_11.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_12.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_13.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_8.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/frame_9.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_7/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_0.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_1.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_2.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_3.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_4.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_5.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_6.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/frame_7.png (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/dolphin/{external/nsfw => custom/NSFW/Anims}/manifest.txt (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_00.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_01.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_02.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_03.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_04.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_05.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_06.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_07.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_08.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_09.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_10.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_11.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_12.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_13.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_14.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_15.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_16.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_17.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_18.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_19.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_20.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_21.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_22.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_23.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_24.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_25.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_26.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_27.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_28.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_29.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_30.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_31.png (100%) rename assets/{icons/Animations/Levelup1_128x64 => dolphin/custom/NSFW/Icons/Animations/Levelup_128x64}/frame_rate (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.png create mode 100644 assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_DB.png rename assets/{icons/Passport/flipper.png => dolphin/custom/NSFW/Icons/Passport/passport_bad_46x49.png} (100%) create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_happy_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png create mode 100644 assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png create mode 100644 assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/U2F/Error_62x31.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinMafia_115x62.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinNice_96x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png create mode 100644 assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_37.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_38.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_39.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_40.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_21.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_22.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_23.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_24.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_25.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_26.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_27.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_28.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_29.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_30.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_31.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_32.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_33.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_34.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_35.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_36.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_19.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_20.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_14.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_15.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_16.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_17.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_18.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_0.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_1.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_10.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_11.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_12.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_13.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_2.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_3.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_4.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_5.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_6.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_7.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_8.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/frame_9.png (100%) rename assets/dolphin/external/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/dolphin/external/{sfw => }/manifest.txt (100%) delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_03.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_06.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png delete mode 100644 assets/icons/Animations/Levelup1_128x64_sfw/frame_10.png delete mode 100644 assets/icons/Animations/Levelup2_128x64_sfw/frame_rate rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_00.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_01.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_02.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_03.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_04.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_05.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_06.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_07.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_08.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_09.png (100%) rename assets/icons/Animations/{Levelup2_128x64_sfw => Levelup_128x64}/frame_10.png (100%) rename assets/icons/Animations/{Levelup1_128x64_sfw => Levelup_128x64}/frame_rate (100%) delete mode 100644 assets/icons/BLE/BLE_Pairing_128x64_sfw.png delete mode 100644 assets/icons/Dolphin/DolphinCommon_56x48_sfw.png delete mode 100644 assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png delete mode 100644 assets/icons/Interface/SmallArrowDown_4x7.png delete mode 100644 assets/icons/Interface/SmallArrowUp_4x7.png delete mode 100644 assets/icons/NFC/ArrowC_1_36x36.png delete mode 100644 assets/icons/NFC/Detailed_chip_17x13.png delete mode 100644 assets/icons/NFC/Medium-chip-22x21.png delete mode 100644 assets/icons/NFC/NFC_dolphin_emulation_47x61_sfw.png delete mode 100644 assets/icons/NFC/Tap_reader_36x38.png delete mode 100644 assets/icons/Passport/passport_DB_sfw.png delete mode 100644 assets/icons/Passport/passport_bad1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_bad3_46x49_sfw.png rename assets/icons/Passport/{passport_bad2_46x49_sfw.png => passport_bad_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_happy1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_happy3_46x49_sfw.png rename assets/icons/Passport/{passport_happy2_46x49_sfw.png => passport_happy_46x49.png} (100%) delete mode 100644 assets/icons/Passport/passport_okay1_46x49_sfw.png delete mode 100644 assets/icons/Passport/passport_okay3_46x49_sfw.png rename assets/icons/Passport/{passport_okay2_46x49_sfw.png => passport_okay_46x49.png} (100%) delete mode 100644 assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png delete mode 100644 assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png delete mode 100644 assets/icons/Settings/Cry_dolph_55x52_sfw.png delete mode 100644 assets/icons/StatusBar/Alert_9x8.png delete mode 100644 assets/icons/StatusBar/Attention_5x8.png rename assets/icons/StatusBar/{Charging-lightning_9x10.png => Charging_lightning_9x10.png} (100%) rename assets/icons/StatusBar/{Charging-lightning_mask_9x10.png => Charging_lightning_mask_9x10.png} (100%) delete mode 100644 assets/icons/StatusBar/GameMode_11x8.png delete mode 100644 assets/icons/SubGhz/Scanning_123x52_sfw.png delete mode 100644 assets/icons/U2F/Auth_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connect_me_62x31_sfw.png delete mode 100644 assets/icons/U2F/Connected_62x31_sfw.png delete mode 100644 assets/icons/U2F/Error_62x31_sfw.png delete mode 100644 assets/icons/iButton/DolphinMafia_115x62_sfw.png delete mode 100644 assets/icons/iButton/DolphinNice_96x59_sfw.png delete mode 100644 assets/icons/iButton/DolphinWait_61x59_sfw.png delete mode 100644 assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Boxing_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Cry_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Furippa1_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Happy_holidays_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Laptop_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Leaving_sad_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mad_fist_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_37.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_38.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_39.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_40.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Mods_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Painting_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Read_books_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Recording_128x51/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleep_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_21.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_22.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_23.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_24.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_25.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_26.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_27.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_28.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_29.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_30.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_31.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_32.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_33.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_34.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_35.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_36.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Sleigh_ride_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L1_Waves_128x50/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Furippa2_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Hacking_pc_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Soldering_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_19.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_20.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L2_Wake_up_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_14.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_15.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_16.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_17.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_18.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Furippa3_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Hijack_radio_128x64/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_0.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_1.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_10.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_11.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_12.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_13.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_2.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_3.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_4.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_5.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_6.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_7.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_8.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/frame_9.bm (100%) rename assets/resources/dolphin/{sfw => }/L3_Lab_research_128x54/meta.txt (100%) rename assets/resources/dolphin/{sfw => }/manifest.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_21.bm (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/PaxGod_TikTok_Marketing/meta.txt (93%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_1/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_10/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_11/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_12/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_13/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_14/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_15/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_16/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_17/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_18/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_19/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_2/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_20/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_21/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_52.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_53.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_54.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_55.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_56.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_57.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_58.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_59.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_22/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_23/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_24/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_25/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_26/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_27/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_28/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_50.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_51.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_29/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_3/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_28.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_29.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_30.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_31.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_32.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_33.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_34.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_35.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_36.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_37.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_38.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_39.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_40.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_41.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_42.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_43.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_44.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_45.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_46.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_47.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_48.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_49.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_30/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_4/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_14.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_15.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_16.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_17.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_18.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_19.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_20.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_21.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_22.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_23.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_24.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_25.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_26.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_27.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_5/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_6/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_10.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_11.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_12.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_13.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_8.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/frame_9.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_7/meta.txt (92%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_8/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_0.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_1.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_2.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_3.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_4.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_5.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_6.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/frame_7.bm (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/lvl_9/meta.txt (100%) rename assets/resources/{dolphin/nsfw => dolphin_custom/NSFW/Anims}/manifest.txt (100%) create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.bm create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_bad_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinNice_96x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinWait_61x59.bmx create mode 100644 assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx diff --git a/.gitignore b/.gitignore index bf7addba9..025246faa 100644 --- a/.gitignore +++ b/.gitignore @@ -74,7 +74,9 @@ lib/STM32CubeWB # Asset packs assets/dolphin/custom/* +!assets/dolphin/custom/NSFW/ !assets/dolphin/custom/WatchDogs/ !assets/dolphin/custom/ReadMe.md assets/resources/dolphin_custom/* +!assets/resources/dolphin_custom/NSFW/ !assets/resources/dolphin_custom/WatchDogs/ diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 3163b8cff..9cb66a5e5 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -457,10 +457,14 @@ void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) { browser->last_tab_switch_dir = key; - if(key == InputKeyLeft) { - tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; - } else { - tab = (tab + 1) % ArchiveTabTotal; + for(int i = 0; i < 2; i++) { + if(key == InputKeyLeft) { + tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal; + } else { + tab = (tab + 1) % ArchiveTabTotal; + } + if(tab == ArchiveTabInternal && !furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) continue; + break; } browser->is_root = true; diff --git a/applications/main/archive/helpers/archive_browser.h b/applications/main/archive/helpers/archive_browser.h index 5b13e98da..43a9a651a 100644 --- a/applications/main/archive/helpers/archive_browser.h +++ b/applications/main/archive/helpers/archive_browser.h @@ -17,6 +17,7 @@ static const char* tab_default_paths[] = { [ArchiveTabBadUsb] = ANY_PATH("badusb"), [ArchiveTabU2f] = "/app:u2f", [ArchiveTabApplications] = ANY_PATH("apps"), + [ArchiveTabInternal] = STORAGE_INT_PATH_PREFIX, [ArchiveTabBrowser] = STORAGE_ANY_PATH_PREFIX, }; @@ -44,6 +45,7 @@ static const ArchiveFileTypeEnum known_type[] = { [ArchiveTabBadUsb] = ArchiveFileTypeBadUsb, [ArchiveTabU2f] = ArchiveFileTypeU2f, [ArchiveTabApplications] = ArchiveFileTypeApplication, + [ArchiveTabInternal] = ArchiveFileTypeUnknown, [ArchiveTabBrowser] = ArchiveFileTypeUnknown, }; diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index dce753fde..26ed17d75 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -19,6 +19,7 @@ static const char* ArchiveTabNames[] = { [ArchiveTabBadUsb] = "Bad USB", [ArchiveTabU2f] = "U2F", [ArchiveTabApplications] = "Apps", + [ArchiveTabInternal] = "Internal", [ArchiveTabBrowser] = "Browser", }; diff --git a/applications/main/archive/views/archive_browser_view.h b/applications/main/archive/views/archive_browser_view.h index a525a7db6..6e6582405 100644 --- a/applications/main/archive/views/archive_browser_view.h +++ b/applications/main/archive/views/archive_browser_view.h @@ -31,6 +31,7 @@ typedef enum { ArchiveTabBadUsb, ArchiveTabU2f, ArchiveTabApplications, + ArchiveTabInternal, ArchiveTabBrowser, ArchiveTabTotal, } ArchiveTabEnum; diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index 2c707bbf1..d32eee382 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" typedef enum { BadUsbCustomEventErrorBack, @@ -32,7 +32,7 @@ void bad_usb_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", bad_usb_scene_error_event_callback, app); } else if(app->error == BadUsbAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index ad889cd1c..51bed3835 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define MAX_NAME_LEN 64 @@ -28,7 +28,6 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { elements_string_fit_width(canvas, disp_str, 128 - 2); canvas_set_font(canvas, FontSecondary); canvas_draw_str(canvas, 2, 8, furi_string_get_cstr(disp_str)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); if(strlen(model->layout) == 0) { furi_string_set(disp_str, "(default)"); @@ -49,7 +48,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if((model->state.state == BadUsbStateIdle) || (model->state.state == BadUsbStateDone) || (model->state.state == BadUsbStateNotConnected)) { - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "Cum"); } else { elements_button_center(canvas, "Start"); @@ -68,7 +67,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { if(model->state.state == BadUsbStateNotConnected) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Plug me"); canvas_draw_str_aligned(canvas, 127, 43, AlignRight, AlignBottom, "in, Daddy"); } else { @@ -78,7 +77,7 @@ static void bad_usb_draw_callback(Canvas* canvas, void* _model) { } else if(model->state.state == BadUsbStateWillRun) { canvas_draw_icon(canvas, 4, 26, &I_Clock_18x18); canvas_set_font(canvas, FontPrimary); - if(xtreme_settings->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will cum"); } else { canvas_draw_str_aligned(canvas, 127, 31, AlignRight, AlignBottom, "Will run"); diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 162faf2f1..35a6ce1d9 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); @@ -27,7 +27,7 @@ void u2f_scene_error_on_enter(void* context) { app->widget, GuiButtonTypeLeft, "Back", u2f_scene_error_event_callback, app); } else if(app->error == U2fAppErrorCloseRpc) { widget_add_icon_element(app->widget, 78, 0, &I_ActiveConnection_50x64); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { widget_add_string_multiline_element( app->widget, 3, 2, AlignLeft, AlignTop, FontPrimary, "I am not\na whore!"); widget_add_string_multiline_element( diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index eecd32702..fc1c5c4fa 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -21,7 +21,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { if(model->display_msg == U2fMsgNotConnected) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connect_me_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned( canvas, 128 / 2, 3, AlignCenter, AlignTop, "Plug me in d-daddy"); } else { @@ -32,7 +32,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Connected!"); } else if(model->display_msg == U2fMsgRegister) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -44,7 +44,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { canvas, 128 / 2, 3, AlignCenter, AlignTop, "Press OK to register"); } } else if(model->display_msg == U2fMsgAuth) { - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { elements_button_center(canvas, "CUM"); canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Auth_62x31); canvas_draw_str_aligned( @@ -57,7 +57,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgSuccess) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Connected_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Cum released~"); } else { canvas_draw_str_aligned( @@ -65,7 +65,7 @@ static void u2f_view_draw_callback(Canvas* canvas, void* _model) { } } else if(model->display_msg == U2fMsgError) { canvas_draw_icon(canvas, 22, 15, XTREME_ASSETS()->I_Error_62x31); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { canvas_draw_str_aligned(canvas, 128 / 2, 3, AlignCenter, AlignTop, "Unable to cum"); } else { canvas_draw_str_aligned( diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index caec3cbf6..cea02bd00 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -10,4 +10,3 @@ App( fap_category="Misc", order=81, ) - diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 27e73a0ce..750373342 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -9,4 +9,4 @@ App( fap_icon_assets="images", fap_icon="flipp_pomodoro_10.png", fap_icon_assets_symbol="flipp_pomodoro", -) \ No newline at end of file +) diff --git a/applications/plugins/scrambler/application.fam b/applications/plugins/scrambler/application.fam index 8d87a4a62..4d48d7bb5 100644 --- a/applications/plugins/scrambler/application.fam +++ b/applications/plugins/scrambler/application.fam @@ -1,6 +1,6 @@ # COMPILE ISTRUCTIONS: -# Clean the code and remove old binaries/compilation artefact +# Clean the code and remove old binaries/compilation artefact # ./fbt -c fap_rubiks_cube_scrambler # Compile FAP diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 5239d72d5..0808a3618 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define TAG "AnimationManager" @@ -569,9 +569,6 @@ void animation_manager_load_and_continue_animation(AnimationManager* animation_m static void animation_manager_switch_to_one_shot_view(AnimationManager* animation_manager) { furi_assert(animation_manager); furi_assert(!animation_manager->one_shot_view); - Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); - DolphinStats stats = dolphin_stats(dolphin); - furi_record_close(RECORD_DOLPHIN); animation_manager->one_shot_view = one_shot_view_alloc(); one_shot_view_set_interact_callback( @@ -580,19 +577,8 @@ static void animation_manager_switch_to_one_shot_view(AnimationManager* animatio View* next_view = one_shot_view_get_view(animation_manager->one_shot_view); view_stack_remove_view(animation_manager->view_stack, prev_view); view_stack_add_view(animation_manager->view_stack, next_view); - if(XTREME_SETTINGS()->nsfw_mode) { - one_shot_view_start_animation(animation_manager->one_shot_view, &A_Levelup1_128x64); - } else { - if(stats.level <= 20) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup1_128x64_sfw); - } else if(stats.level >= 21) { - one_shot_view_start_animation( - animation_manager->one_shot_view, &A_Levelup2_128x64_sfw); - } else { - furi_assert(0); - } - } + one_shot_view_start_animation( + animation_manager->one_shot_view, XTREME_ASSETS()->A_Levelup_128x64); } static void animation_manager_switch_to_animation_view(AnimationManager* animation_manager) { diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index a80a958a3..4263dc0a4 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -40,7 +40,7 @@ void animation_handler_select_manifest() { furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); Storage* storage = furi_record_open(RECORD_STORAGE); if(storage_common_stat(storage, furi_string_get_cstr(manifest), NULL) == FSE_OK) { - FURI_LOG_I(TAG, "Custom Manifest selected"); + FURI_LOG_I(TAG, "Custom manifest selected"); } else { use_asset_pack = false; } @@ -48,14 +48,8 @@ void animation_handler_select_manifest() { } if(!use_asset_pack) { furi_string_set(anim_dir, BASE_ANIMATION_DIR); - if(xtreme_settings->nsfw_mode) { - furi_string_cat_str(anim_dir, "/nsfw"); - FURI_LOG_I(TAG, "NSFW Manifest selected"); - } else { - furi_string_cat_str(anim_dir, "/sfw"); - FURI_LOG_I(TAG, "SFW Manifest selected"); - } furi_string_printf(manifest, "%s/manifest.txt", furi_string_get_cstr(anim_dir)); + FURI_LOG_I(TAG, "Base manifest selected"); } strlcpy(ANIMATION_DIR, furi_string_get_cstr(anim_dir), sizeof(ANIMATION_DIR)); strlcpy( diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index 7840cd00a..b5b73668b 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,6 +17,8 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" +#include "../../settings/xtreme_settings/xtreme_assets.h" + static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -305,6 +307,9 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); + // TODO: find a (working) way to run this at startup without hooking desktop + XTREME_ASSETS_LOAD(); + if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index e4f5e788f..c2149253c 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "../../../settings/xtreme_settings/xtreme_assets.h" #define DesktopFaultEventExit 0x00FF00FF @@ -15,7 +15,7 @@ void desktop_scene_fault_on_enter(void* context) { Popup* popup = desktop->hw_mismatch_popup; popup_set_context(popup, desktop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { popup_set_header( popup, "Slut passed out\n but is now back", diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index f0430de5d..450c5af23 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -40,7 +40,7 @@ static void render_callback(Canvas* canvas, void* _ctx) { const char* mood_str = NULL; const Icon* portrait = NULL; - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { if(stats->butthurt <= 4) { portrait = xtreme_assets->I_passport_happy_46x49; mood_str = "Status: Wet"; diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index ecab8c333..6fd26138b 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -12,7 +12,7 @@ void power_settings_scene_power_off_on_enter(void* context) { DialogEx* dialog = app->dialog; dialog_ex_set_header(dialog, "Turn Off Device?", 64, 2, AlignCenter, AlignTop); - if(XTREME_SETTINGS()->nsfw_mode) { + if(XTREME_ASSETS()->is_nsfw) { dialog_ex_set_text( dialog, " I will be\nwaiting for\n you master", 78, 16, AlignLeft, AlignTop); } else { diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 3323cf1df..ba5e65ca0 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -3,15 +3,6 @@ #include #include -static void xtreme_settings_scene_start_base_graphics_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); - bool value = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, value ? "NSFW" : "SFW"); - XTREME_SETTINGS()->nsfw_mode = value; - app->settings_changed = true; - app->assets_changed = true; -} - static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); @@ -172,11 +163,6 @@ void xtreme_settings_scene_start_on_enter(void* context) { storage_file_free(folder); furi_record_close(RECORD_STORAGE); - item = variable_item_list_add( - var_item_list, "Base Graphics", 2, xtreme_settings_scene_start_base_graphics_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->nsfw_mode); - variable_item_set_current_value_text(item, xtreme_settings->nsfw_mode ? "NSFW" : "SFW"); - item = variable_item_list_add( var_item_list, "Asset Pack", diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/settings/xtreme_settings/xtreme_assets.c index 0f6ab998d..13014b8d1 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/settings/xtreme_settings/xtreme_assets.c @@ -2,175 +2,50 @@ #include "assets_icons.h" #include +#define ICONS_FMT PACKS_DIR "/%s/Icons/%s" + XtremeAssets* xtreme_assets = NULL; -XtremeAssets* XTREME_ASSETS() { - if(xtreme_assets == NULL) { - XTREME_ASSETS_LOAD(); - } - return xtreme_assets; +void anim(const Icon** replace, const char* name, FuriString* path, File* file) { + do { + furi_string_printf(path, ICONS_FMT "/meta", XTREME_SETTINGS()->asset_pack, name); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + int32_t width, height, frame_rate, frame_count; + storage_file_read(file, &width, 4); + storage_file_read(file, &height, 4); + storage_file_read(file, &frame_rate, 4); + storage_file_read(file, &frame_count, 4); + storage_file_close(file); + + Icon* icon = malloc(sizeof(Icon)); + FURI_CONST_ASSIGN(icon->width, width); + FURI_CONST_ASSIGN(icon->height, height); + FURI_CONST_ASSIGN(icon->frame_rate, frame_rate); + FURI_CONST_ASSIGN(icon->frame_count, frame_count); + icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); + const char* pack = XTREME_SETTINGS()->asset_pack; + + bool ok = true; + for(int i = 0; ok && i < icon->frame_count; ++i) { + ok = false; + furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; + + uint64_t size = storage_file_size(file); + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); + if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; + storage_file_close(file); + } + if(!ok) break; + + *replace = icon; + } while(false); } -void XTREME_ASSETS_LOAD() { - if(xtreme_assets != NULL) return; - - xtreme_assets = malloc(sizeof(XtremeAssets)); - XtremeSettings* xtreme_settings = XTREME_SETTINGS(); - - if(xtreme_settings->nsfw_mode) { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; - xtreme_assets->I_passport_bad_46x49 = &I_flipper; - xtreme_assets->I_passport_DB = &I_passport_DB; - xtreme_assets->I_passport_happy_46x49 = &I_flipper; - xtreme_assets->I_passport_okay_46x49 = &I_flipper; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; - xtreme_assets->I_Error_62x31 = &I_Error_62x31; - } else { - xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64_sfw; - xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48_sfw; - xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62_sfw; - xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59_sfw; - xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59_sfw; - xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = - &I_iButtonDolphinVerySuccess_108x52_sfw; - xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63_sfw; - xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61_sfw; - xtreme_assets->I_passport_bad_46x49 = &I_passport_bad1_46x49_sfw; - xtreme_assets->I_passport_DB = &I_passport_DB_sfw; - xtreme_assets->I_passport_happy_46x49 = &I_passport_happy1_46x49_sfw; - xtreme_assets->I_passport_okay_46x49 = &I_passport_okay1_46x49_sfw; - xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61_sfw; - xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61_sfw; - xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57_sfw; - xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52_sfw; - xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52_sfw; - xtreme_assets->I_Auth_62x31 = &I_Auth_62x31_sfw; - xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31_sfw; - xtreme_assets->I_Connected_62x31 = &I_Connected_62x31_sfw; - xtreme_assets->I_Error_62x31 = &I_Error_62x31_sfw; - } - - if(xtreme_settings->asset_pack[0] == '\0') return; - FileInfo info; - FuriString* path = furi_string_alloc(); - const char* pack = xtreme_settings->asset_pack; - furi_string_printf(path, PACKS_DIR "/%s", pack); - Storage* storage = furi_record_open(RECORD_STORAGE); - if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && - info.flags & FSF_DIRECTORY) { - File* file = storage_file_alloc(storage); - - swap_bmx_icon( - &xtreme_assets->I_BLE_Pairing_128x64, pack, "BLE/BLE_Pairing_128x64.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinCommon_56x48, - pack, - "Dolphin/DolphinCommon_56x48.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinMafia_115x62, - pack, - "iButton/DolphinMafia_115x62.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinNice_96x59, pack, "iButton/DolphinNice_96x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_DolphinWait_61x59, pack, "iButton/DolphinWait_61x59.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_iButtonDolphinVerySuccess_108x52, - pack, - "iButton/iButtonDolphinVerySuccess_108x52.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_DolphinReadingSuccess_59x63, - pack, - "Infrared/DolphinReadingSuccess_59x63.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_NFC_dolphin_emulation_47x61, - pack, - "NFC/NFC_dolphin_emulation_47x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_bad_46x49, - pack, - "Passport/passport_bad_46x49.bmx", - path, - file); - swap_bmx_icon(&xtreme_assets->I_passport_DB, pack, "Passport/passport_DB.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_passport_happy_46x49, - pack, - "Passport/passport_happy_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_passport_okay_46x49, - pack, - "Passport/passport_okay_46x49.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinReceive_97x61, - pack, - "RFID/RFIDDolphinReceive_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSend_97x61, - pack, - "RFID/RFIDDolphinSend_97x61.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_RFIDDolphinSuccess_108x57, - pack, - "RFID/RFIDDolphinSuccess_108x57.bmx", - path, - file); - swap_bmx_icon( - &xtreme_assets->I_Cry_dolph_55x52, pack, "Settings/Cry_dolph_55x52.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Scanning_123x52, pack, "SubGhz/Scanning_123x52.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Auth_62x31, pack, "U2F/Auth_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connect_me_62x31, pack, "U2F/Connect_me_62x31.bmx", path, file); - swap_bmx_icon( - &xtreme_assets->I_Connected_62x31, pack, "U2F/Connected_62x31.bmx", path, file); - swap_bmx_icon(&xtreme_assets->I_Error_62x31, pack, "U2F/Error_62x31.bmx", path, file); - - storage_file_free(file); - } - furi_record_close(RECORD_STORAGE); - furi_string_free(path); -} - -void swap_bmx_icon( - const Icon** replace, - const char* pack, - const char* name, - FuriString* path, - File* file) { - furi_string_printf(path, PACKS_DIR "/%s/Icons/%s", pack, name); +void icon(const Icon** replace, const char* name, FuriString* path, File* file) { + furi_string_printf(path, ICONS_FMT ".bmx", XTREME_SETTINGS()->asset_pack, name); if(storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) { uint64_t size = storage_file_size(file) - 8; int32_t width, height; @@ -190,3 +65,80 @@ void swap_bmx_icon( storage_file_close(file); } } + +void swap(XtremeAssets* x, FuriString* p, File* f) { + anim(&x->A_Levelup_128x64, "Animations/Levelup_128x64", p, f); + icon(&x->I_BLE_Pairing_128x64, "BLE/BLE_Pairing_128x64", p, f); + icon(&x->I_DolphinCommon_56x48, "Dolphin/DolphinCommon_56x48", p, f); + icon(&x->I_DolphinMafia_115x62, "iButton/DolphinMafia_115x62", p, f); + icon(&x->I_DolphinNice_96x59, "iButton/DolphinNice_96x59", p, f); + icon(&x->I_DolphinWait_61x59, "iButton/DolphinWait_61x59", p, f); + icon(&x->I_iButtonDolphinVerySuccess_108x52, "iButton/iButtonDolphinVerySuccess_108x52", p, f); + icon(&x->I_DolphinReadingSuccess_59x63, "Infrared/DolphinReadingSuccess_59x63", p, f); + icon(&x->I_NFC_dolphin_emulation_47x61, "NFC/NFC_dolphin_emulation_47x61", p, f); + icon(&x->I_passport_bad_46x49, "Passport/passport_bad_46x49", p, f); + icon(&x->I_passport_DB, "Passport/passport_DB", p, f); + icon(&x->I_passport_happy_46x49, "Passport/passport_happy_46x49", p, f); + icon(&x->I_passport_okay_46x49, "Passport/passport_okay_46x49", p, f); + icon(&x->I_RFIDDolphinReceive_97x61, "RFID/RFIDDolphinReceive_97x61", p, f); + icon(&x->I_RFIDDolphinSend_97x61, "RFID/RFIDDolphinSend_97x61", p, f); + icon(&x->I_RFIDDolphinSuccess_108x57, "RFID/RFIDDolphinSuccess_108x57", p, f); + icon(&x->I_Cry_dolph_55x52, "Settings/Cry_dolph_55x52", p, f); + icon(&x->I_Scanning_123x52, "SubGhz/Scanning_123x52", p, f); + icon(&x->I_Auth_62x31, "U2F/Auth_62x31", p, f); + icon(&x->I_Connect_me_62x31, "U2F/Connect_me_62x31", p, f); + icon(&x->I_Connected_62x31, "U2F/Connected_62x31", p, f); + icon(&x->I_Error_62x31, "U2F/Error_62x31", p, f); +} + +void XTREME_ASSETS_LOAD() { + if(xtreme_assets != NULL) return; + + xtreme_assets = malloc(sizeof(XtremeAssets)); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + + xtreme_assets->A_Levelup_128x64 = &A_Levelup_128x64; + xtreme_assets->I_BLE_Pairing_128x64 = &I_BLE_Pairing_128x64; + xtreme_assets->I_DolphinCommon_56x48 = &I_DolphinCommon_56x48; + xtreme_assets->I_DolphinMafia_115x62 = &I_DolphinMafia_115x62; + xtreme_assets->I_DolphinNice_96x59 = &I_DolphinNice_96x59; + xtreme_assets->I_DolphinWait_61x59 = &I_DolphinWait_61x59; + xtreme_assets->I_iButtonDolphinVerySuccess_108x52 = &I_iButtonDolphinVerySuccess_108x52; + xtreme_assets->I_DolphinReadingSuccess_59x63 = &I_DolphinReadingSuccess_59x63; + xtreme_assets->I_NFC_dolphin_emulation_47x61 = &I_NFC_dolphin_emulation_47x61; + xtreme_assets->I_passport_bad_46x49 = &I_passport_bad_46x49; + xtreme_assets->I_passport_DB = &I_passport_DB; + xtreme_assets->I_passport_happy_46x49 = &I_passport_happy_46x49; + xtreme_assets->I_passport_okay_46x49 = &I_passport_okay_46x49; + xtreme_assets->I_RFIDDolphinReceive_97x61 = &I_RFIDDolphinReceive_97x61; + xtreme_assets->I_RFIDDolphinSend_97x61 = &I_RFIDDolphinSend_97x61; + xtreme_assets->I_RFIDDolphinSuccess_108x57 = &I_RFIDDolphinSuccess_108x57; + xtreme_assets->I_Cry_dolph_55x52 = &I_Cry_dolph_55x52; + xtreme_assets->I_Scanning_123x52 = &I_Scanning_123x52; + xtreme_assets->I_Auth_62x31 = &I_Auth_62x31; + xtreme_assets->I_Connect_me_62x31 = &I_Connect_me_62x31; + xtreme_assets->I_Connected_62x31 = &I_Connected_62x31; + xtreme_assets->I_Error_62x31 = &I_Error_62x31; + + if(xtreme_settings->asset_pack[0] == '\0') return; + xtreme_assets->is_nsfw = strncmp(xtreme_settings->asset_pack, "NSFW", strlen("NSFW")) == 0; + FileInfo info; + FuriString* path = furi_string_alloc(); + furi_string_printf(path, PACKS_DIR "/%s", xtreme_settings->asset_pack); + Storage* storage = furi_record_open(RECORD_STORAGE); + if(storage_common_stat(storage, furi_string_get_cstr(path), &info) == FSE_OK && + info.flags & FSF_DIRECTORY) { + File* file = storage_file_alloc(storage); + swap(xtreme_assets, path, file); + storage_file_free(file); + } + furi_record_close(RECORD_STORAGE); + furi_string_free(path); +} + +XtremeAssets* XTREME_ASSETS() { + if(xtreme_assets == NULL) { + XTREME_ASSETS_LOAD(); + } + return xtreme_assets; +} diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/settings/xtreme_settings/xtreme_assets.h index c49f5b590..038372a43 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/settings/xtreme_settings/xtreme_assets.h @@ -7,6 +7,8 @@ #define PACKS_DIR EXT_PATH("dolphin_custom") typedef struct { + bool is_nsfw; + const Icon* A_Levelup_128x64; const Icon* I_BLE_Pairing_128x64; const Icon* I_DolphinCommon_56x48; const Icon* I_DolphinMafia_115x62; @@ -30,15 +32,6 @@ typedef struct { const Icon* I_Error_62x31; } XtremeAssets; -XtremeAssets* XTREME_ASSETS(); - void XTREME_ASSETS_LOAD(); -void swap_bmx_icon( - const Icon** replace, - const char* base, - const char* name, - FuriString* path, - File* file); - -void free_bmx_icon(Icon* icon); +XtremeAssets* XTREME_ASSETS(); diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index ee0ec5583..13bb574ad 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -18,7 +18,6 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; - bool nsfw_mode; char asset_pack[MAX_PACK_NAME_LEN]; BatteryStyle battery_style; uint16_t anim_speed; diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_0.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_1.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_10.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_11.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_12.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_13.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_14.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_15.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_16.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_17.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_18.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_19.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_2.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_20.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_21.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_22.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_26.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.png diff --git a/assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/dolphin/custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_1/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_1/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_1/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_1/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_10/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_10/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_10/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_10/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_11/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_11/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_11/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_11/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_12/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_12/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_12/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_12/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_13/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_13/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_13/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_13/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_14/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_14/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_14/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_14/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_15/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_15/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_15/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_15/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_16/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_16/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_16/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_16/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_17/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_17/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_17/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_17/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_18/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_18/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_18/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_18/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_19/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_19/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_19/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_19/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_2/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_2/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_2/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_2/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_20/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_20/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_20/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_20/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_21/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_21/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_21/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_21/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_52.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_52.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_52.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_53.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_53.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_53.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_54.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_54.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_54.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_55.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_55.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_55.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_56.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_56.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_56.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_57.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_57.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_57.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_58.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_58.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_58.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_59.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_59.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_59.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_22/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_22/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_22/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_22/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_23/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_23/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_23/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_23/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_24/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_24/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_24/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_24/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_25/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_25/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_25/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_25/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_26/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_26/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_26/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_26/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_27/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_27/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_27/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_27/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_28/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_28/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_28/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_28/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_50.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_50.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_50.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_51.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_51.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_51.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_29/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_29/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_29/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_29/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_3/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_3/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_3/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_3/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_28.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_28.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_28.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_29.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_29.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_29.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_30.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_30.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_30.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_31.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_31.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_31.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_32.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_32.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_32.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_33.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_33.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_33.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_34.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_34.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_34.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_35.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_35.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_35.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_36.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_36.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_36.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_37.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_37.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_37.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_38.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_38.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_38.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_39.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_39.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_39.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_40.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_40.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_40.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_41.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_41.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_41.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_42.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_42.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_42.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_43.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_43.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_43.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_44.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_44.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_44.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_45.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_45.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_45.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_46.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_46.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_46.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_47.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_47.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_47.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_48.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_48.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_48.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_49.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_49.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_49.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_30/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_30/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_30/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_30/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_4/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_4/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_4/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_4/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_14.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_14.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_14.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_15.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_15.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_15.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_16.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_16.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_16.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_17.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_17.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_17.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_18.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_18.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_18.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_19.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_19.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_19.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_20.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_20.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_20.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_21.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_21.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_21.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_22.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_22.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_22.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_23.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_23.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_23.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_24.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_24.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_24.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_25.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_25.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_25.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_26.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_26.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_26.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_27.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_27.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_27.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_5/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_5/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_5/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_5/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_6/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_6/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_6/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_6/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_10.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_10.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_10.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_11.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_11.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_11.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_12.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_12.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_12.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_13.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_13.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_13.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_8.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_8.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_8.png diff --git a/assets/dolphin/external/nsfw/lvl_7/frame_9.png b/assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/frame_9.png rename to assets/dolphin/custom/NSFW/Anims/lvl_7/frame_9.png diff --git a/assets/dolphin/external/nsfw/lvl_7/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_7/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_7/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_8/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_8/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_8/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_8/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_0.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_0.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_0.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_1.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_1.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_1.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_2.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_2.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_2.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_3.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_3.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_3.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_4.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_4.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_4.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_5.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_5.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_5.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_6.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_6.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_6.png diff --git a/assets/dolphin/external/nsfw/lvl_9/frame_7.png b/assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/frame_7.png rename to assets/dolphin/custom/NSFW/Anims/lvl_9/frame_7.png diff --git a/assets/dolphin/external/nsfw/lvl_9/meta.txt b/assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/dolphin/external/nsfw/lvl_9/meta.txt rename to assets/dolphin/custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/dolphin/external/nsfw/manifest.txt b/assets/dolphin/custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/dolphin/external/nsfw/manifest.txt rename to assets/dolphin/custom/NSFW/Anims/manifest.txt diff --git a/assets/icons/Animations/Levelup1_128x64/frame_00.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_00.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_01.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_01.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_02.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_02.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_03.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_03.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_04.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_04.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_05.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_05.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_06.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_06.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_07.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_07.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_08.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_08.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_09.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_09.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_10.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_10.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_11.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_11.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_11.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_12.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_12.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_12.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_13.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_13.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_14.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_14.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_15.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_15.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_15.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_16.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_16.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_17.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_17.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_18.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_18.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_19.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_19.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_20.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_20.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_21.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_21.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_22.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_22.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_23.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_23.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_24.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_24.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_24.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_25.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_25.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_25.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_26.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_26.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_27.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_27.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_28.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_28.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_28.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_29.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_29.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_30.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_30.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_31.png b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_31.png rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_31.png diff --git a/assets/icons/Animations/Levelup1_128x64/frame_rate b/assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64/frame_rate rename to assets/dolphin/custom/NSFW/Icons/Animations/Levelup_128x64/frame_rate diff --git a/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png b/assets/dolphin/custom/NSFW/Icons/BLE/BLE_Pairing_128x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f60598005d41ff05a9f763f42f1a6b7900150e33 GIT binary patch literal 2610 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 Ud8X1kK>z>%07*qoM6N<$g8GWm`2YX_ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png b/assets/dolphin/custom/NSFW/Icons/Dolphin/DolphinCommon_56x48.png new file mode 100644 index 0000000000000000000000000000000000000000..e80fea5bd7f694549da1b45e9f3db056342a95cc GIT binary patch literal 3376 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/dolphin/custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.png new file mode 100644 index 0000000000000000000000000000000000000000..e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f GIT binary patch literal 4224 zcmV-`5P$E9P)pVGD$>1RCwBb+IOs0MH&WhIV+2R1w^cfy*KRG6?>00cI=8EHbRV= zXv82!#DZ5)K!}PGdso!hqu2{ZEZ7@jS40GX{e9=Tnf+wr&L3aqo-^~7=dCm6Rv&-- z@lQYfG=Kj5KmYvmgAYD<^UXI$jvU#)fBz99MqG8(RV^(oW5T9a{rA<9OD@@_O`BhT{q@Hmf1Epa?)2%?2|>6S zGiJ1J-+tkR7ykC!Z@>HQyN(?@cIwoL#LF$W+^ku%x_0gQ`|rR1_19mXw`_slcT+;r1T*H~i>U_qoYv1RquS0@3~zxd({6Y^CQwGI-@G<4|DHP>8o zqm4FdQx8-qO=AG~#oJ=0^a}NmCIr9`B9kZ9U;~>jUAlDZ)@`MgRwDY0de|E|vfZ4L zy?gh5>Zzw5ee_ZA+grtK{8A}d1M12vubeh*+D<#| z)S*KMjOoFGUfCQBfBEGXen9Va*IgHhFvE%Y@R(f>I#>@F%Pc?y%Mi4z$ck)WW^`Ju zu)+#_k!$mo%C>Ri#!a3)`LM$dW9gW{AdL;Skp@=|PdUi1v(7qTqZS_C62e;(cn~5i z{bC=VHMbz9^vareG2-0dn{U3ccdMok<6=4KKVolP?~)73q0W(HUY`p+qg?r4^JYEx9D&KPZEjSZMWUU7hhaSE!%Iu zedo@d6Pa;ddlKFx17OE`h!l9R7al}Um{^8i;4GX-V*%9dBDa-+FlZH-F!GQ}etDSG zRf%~1k8%8qn3-pD*x2v6&3J>#bHD!d3Y&PtyuXiFjabEVi>!%0&$BTs;!h3J1h)w8 zH`0_aj#yB^n6X&R6G;+6o&zRylWEFtBV4OS`XEH;B=|jyG!Z2VXM5wV4OS#kMvqJ~ zykfqjIDhgL?nZRjKscGVS-+w#-ZmC#!YqKr!50|WUPagi(6cS`62l-&BEg2}M2Q3m z-7*Pja})w+kt0$@$*hc6`E4a)k3Wi0W1xaz=n_IG(p7pWT$9$aJx`i{?ILu_MQjws z3PMGK0%t?AgjtEhLat0u^p+f)J@FjldX*eqNJr_A#xfyUR3(iksc1##Qbb#`6pfR) z<1}Ft!X--3Hn&nX8l*Xh_%*QYyI`tKlIQ;JxR-#WK;4r>C+^n7WyT( zf^+V<=We*+h8t|K0qx&<>n&g>Zxv1aso=0zE5^+dG-qpb)e$p<4msqIWtUx+X%$-~ zHeFb+OU6ot#BnGXFSc&DBg2Gk& zF?1tS+=6l_N_yLDvkjstRp2@4q?4X}^2t&f6OzF^J(ORdYG<{Gwg@6CHQMZ@>GU>? zqC?!7Oqg0(rDx02yiTh-y&kKqvI?g`6xUeSpVDB`(gGeZrydmWmT3f$SLxbp0TovT zTgiLr7fL(ZfH_E{i9t*FfP@?vyz#~x`|i6hbMg{svyna8cu*zxq*qE~6f*^wl~-O_ zM(9uv_-(e?W+a2wmRoN5#1l_gj9{WTxfeCvaO2Ft>4+a;qb`b+aD-rNB&daGj#ni1 zYLiVik} zY1$nQNh3h~DHoFl1*V4zY4+ZGZ!}+h_0=4J(%jL6=+4VkTFFFH#3Bk`+kgN45rPL2 ziN@q_T2h66#C5QBOm^PGm`iE+d(@*>uU=3jz31)f!OG+#C2Tb0ktR+Px4YwxJ1F1C zj8%G^=6iD$<9~^rj#}nltbP4Ab z(~v%mDSi0Iz<~otj2JO$)Tlmv`e4KEum^z2g%@6!ESh|i+|45?Oua@*B@`G%BWb|M ze(2tL=bh4S`qa+#P^8Gs7PKTmI*?MESPdIoQ$Ua=cWh^)oXJ6tIp!F^@UYTx`{gGP z^~8cIoKU9(#*aV#xaehIob@th3^EI&pF8n+dBhc0b-*U?>q@<#U^adMC zu{};X%9OqzBipWEcJn{%H=J?&acTZEnM{NNZ1sd58lEmkC zk{DNIwMmmEjTtjWx+5j!Sb>ofP2?yiNOI@TG|uL7BbXz-T|x{ba80I8jnk9? zEPawtXv9@I2h#CD>zJ82$jj-}6#i?S1(YUY@ct4k(WcnGKmUi=^I<`ZwQE)Is$_*K!uZEbImm@sM&*A@;6UUMvo}v$U;#W zJeOywh+LB4sHZiuQ(s8cs5n>3w*WuJZax&QwA67JCIbr)ivJQ3-W}_PH94A-OP}1VsuvuT0mRvOuhv^~lKx*K!kE z`r6#j30MIS63@W^AzUuI>@wc%l;fc|w^6}qKmrWj^!ewXlMHX-Bcy=SKm}1&r3cQ> zjZn$KuXK>HIv8@JL(neuurkIn;g=c)PLsO_zlx9QCT^0bL>cauRUDSVs0d6T5m4Z8 zTj^Lm${}9F?YQHPuuZ3u3)t|cdaiRkPsWPvau=c0^zPkT&Kx#um^Aj>bI&<%=tHD2 z(p#JhS7g~&ny$ni8>Pe$$O}yclQE4}At!_caL=ASRns7WH$oPd(-6}BjGz>~?Y7&F zJMK6wfX*beUh6)(;U+@N-mAsoNh?3qf&YJ$U^-6_rh88(utn{k6 zIL-}zB9@Bg(zKSvDW7BJxI`0p==5}9g9i_`mxm4?K77wT_vC_tQ32wo`X`Q}l*&Dv zM!_Ow*LuXtY|_9cW=7N;Trp%$ht`#AQ46S*T+GaRC}F}gbb49^rrCY>-N%g^r%S}u zVn~Q;Yvf|Rkw)eG^Us&x`__XHWX%Tv+DcT7n}%Emae^G0Xn+%q6`Hh(*f;W0ofQ+e z{pxYT2`3zM&_Mv;4Vr`r6DEKySqB{k)zc`~axt9LnM*Idlu97y58ME8s^Sto)2p{; zKQmrpVm0cZ5*p=}#gm-oNvQ#nqt%Va9(xQhsx(*Kc;k(8HlP@N?z`_kiA1BW{gZ-m z^~y>C$Yin{q4AA%B2ApHO2p>Y$s)GI7|7)UA?Y#)X5Ou~JJUG6sO>C*VlLH4Y?%T) zbm&mBvdf7lo(Om}5myj$YlL#QZr##{V4qqP*K9~rZZR>2hpszUaJjf>zDI>^?poNi zeD;B5?qJ9&g%TyVE$pZ~aT@I98{o+r;+&0gvyuC#+%J_jxk~lUUAh-^#Vp@ru#70V zFesOB39-x*gQ=kKfEgmrI@(C-fYl@j(hyf-BXLfBIfSR3jeS%8s(j7>X1P;}hsTpr z^cg7mAYE0GQ$Whm*mh9zJg^$8=`1gf-;xfWGSvL)7J2+~MG{1N;yMr{O W;b9zBraI980000T8YGT>n?^m!R}t)yw9-_rm88D%9UB~PC^WSW_^eVx+LK3NXV5BCaX zPFgnEy!7107?^2bxIzDialo;Kn;lpA`7RH(?VGP7FKSmLwewZlgfG$^#{D|1?vWB7 zbu#4x8md$l%?lD(K5N2Lc9CoAna_RKx^R>I$Y*|kN5iA+7Wdhoyx10)U&;PEoH^@& zg-k;7{Z|Z==O>CNaOu|+*s`-+6KBY3h*^4{{h#cD@<+$^i0oJ%tG1qLLMxk}V+0TX z8wL#qt_G$B2#Up#F@WJ)Q`KGWUAn9Z`Wz0t4!jH?d5|iQT98r@_*y6v(^m130UzxFS=iCP0T_Mjp%iE-l=bz7g5xd-T;*8Xm_Coz4 zp~H$-U)1Gp?q+h^m+0^>(m8o9M+o=9Wp*Miuf>e_ylmbVut)!^Y~ISH7MfEQ;pzi1W^00009a7bBm000id z000id0mpBsWB>pdmq|oHRCwBrT4!`sWx9UOKE39olST+6lt3UrLK0d42^|CkM2e0D z7{!VpV4G1;Y~vjr2Spv1dUaVY&MX}}I+md*2nYlW5KG2QZkv8|Gy(17tgZth{xl~b0`#&qtU1wi^XKODg9p?MY05~ zY%*A7oyI6@)dtz1)5{u3E$h`9*{IdYP3O+bZbz@&+u0$@iMZ_J5#%|=bEN+=%DKfQ z!%1F`M=~KkiOKO~gnvinxSZg1uJBN+RZyt_E`tA7gC~Vfr-N3j#idJ^P+MDzii!#> zUVJr5N=h(&`gD|)m7%)28Z|XFm{dIpH4|zuW7<`ys+@qui>}6~(lSh_sKSM_jnKwr zNQop2QvbPQ@gyXvKP;6D$h>$0DkLE@4nD`@e3Rd~Qs5$BF89~hB4}~qwzf9RojV^U zlMz{2nMhAhR~BQjSdg2W3%lKp^t23E`8qu<4SKzv@1-e6$>0)Xre&gbaxKPo$LHPNZcs3a=(C7>(C@ezJkP_IqsM&Mp zp^_E4*xJT4Gg-WbEU6&UC9*AX0UC{t0gDe+$^xz!xG0|pn0?J3Yxp{@tXL@)2C14;K76V_x^w5$WMo{@2fA-+}w;qhkwNWLkIEG@nh)h>BjjB=g{Nm#?j+H z!DhEYZ#7|X@nBeuMl7B^4~~{jL|s1km>lLG@oreB!g$wY4V+_4cuPjznqHfkqtX_E?HmtiDt5&Xn&*Q?S zwpO&ZG(l1&aPI7B&PgNMn$AMi*9&!f2Ub>AV|Ga?R@6*Ju;n5wdM(sUP$V3LnsKPv zPNE`L3|u_o@hBpZh_ZmbJ{QJ}8H44^m!oiC5z5QQVZwxJj2}Oq!`DBMBsoulaKMxO?Ut zEE!pbyXG%I>&eriabOVr&yW+M3lg#47Ptu5=kuYmvJzEQRZ8|#Q&W}p5q;g+*~z|l zD9=qzP4N5uN@a?Q3Xz{R07C~CAuBZ<`#$>=Uw!%+;-LVpS+o#lof;WNHIj}lY-VM) zuULx-shJp`nt_V+3`{KJ!uDx~hJzoI69QY50lsqJ{Bl=cH>&1LgHK8z5Q)Ou z;{yU}ggi<3Jz+R|JqQJ3N&)p6zVGTq&xNzNaP%U<(OYyiOT#U zEMYs%8#fkNj#9~tTt1I8XJEnba|&=dFKgi0xzfLW`DK}Gmu zvJ7mAA;vT;nK6sQeH~PMEvu6KFnz^@6Eg*B4l6FfsiQ~n@!q{ixccC~*bGyj2dZ8N z^vuShib^ah8-sGQ9iz2o3=SnQBexJ`?D1J+IU7TVBAsW;Q1Rk~x&o^W6Gx1~#ik2{ zI~gXuL3A(TL?oF^nq=TQ!JuwGhUN}LZ)cY>X9`5K5?2gdqb!iD#j~bb&3N|Vhp}$q z)!2Fcddw^=#t3g5lk}+=5m%#>0Z$q-1Y?T_VOYsPu%gHrkO6~HgY29P3?EU9kt2q| zY%w4d4I!xp`utwPY!GJ~&yyu($js`=`cp8QO$z){-w=H~N=1xnElg}Jy;k>IdR*q} zV@O0J2ywXj>Kic3V8dXG4ZgNZm{eGTQTB9H7Y;_npdwgFR{B^BIrdaoh(4-d6gG_( zA*W0Ecbd+G;R6Ptv|un1#fmA_H8^wbJi5Aj(bmyPE>Iy7j}fhcFc??`F3v)x&D5EZ z@c1AJ_LP1@t0dXw60_hVK`g1g3L6(KLrNruhM$k2E7A*_RgWo?Yhcn!FlyLX3_P9W zlMF`?4}_oB}|zdp;d79M9Gez#;1meoyWwteus zy5T_t^F~jGJ;>fq>Y$0MaOBJ>xQtr-qK={7uEhau42`LJtbg@+Y}@+=_MA9^2R?fj zo8Nm4JKlc-Z6YBS6GF^xK(B%`8iXqxPz0SiAuEe9^E#TipC}WAE-8?V9l+{IDuQt= zA~dcbfK>eKn4FjUg zvN<&c7dbbAkqwl~LjPY@r7;oDe;x@gN)oW6rN$NkUk>*2A$}l~nMq`yu#~1YAmR_g z<8s5x%*WVh=Pgk@`r#Yc{^oAH{lho-@W6Mt;rF-Uu^o@#$(@g4`~44MLd_KX*9$M= z;m01si?6+gXP81#KZ7#DAj~UjloEL6jkJg(D`Ce#i8Z* zG+H5b#n>u_0sR57w|j6gDs@6MjUvfFOPy-)4H zwr8Kf+WR(Png9@Eml41qJI-{A=xI6ptQ>gxif<*R99R*Z)o_uQekTe$I#p$S%Z{#fa{J zPqAt`wuGC6BV3Qr^?GV@Fi8sPh_>#h{N(>ZZLM%QT(#g|AItUo6)TzTc^H*9h?;_f zl1wNk5TOfbA}?acrtNs`uX`|K##IAZat445?eIcKcq>us^2IuIYPb)hoyK-CPF0kvImwf8)1_Tb z+>~?-%`d?5{XZ!5FU)Wv8Urt_&~btb6a!XHtuC?`_6NA|5VqWY7mm`(@oEf!vC^i+ zW%jGYlMsj7x;oL-<;2(f{)Meux8kK&UPj4?;Yw13m=YFEoUf&&MKLj=-$i^Pco8Fg zD1|%$Ti9SNNg+wWbko$SxSkHAicu{pD2C_96UZ$Xp!ELXum6oWCp&N05Pb6KN0>iu zBHn)PB}BUWU}p9VT#SkBb=R#MQC2(x32zvB;yi_Xe{u}0l$nT?2;uPu?!|d>!e3t8 zjlp3p#;9xvF{pnYJ=mXN0=A#J-{nsr!o|mRWL1;!2ts6W1tRNmB1$-kV&md#F@*B7 zj?%P(y7}C(pV{XOh`kjfJemkoGQU?~ONZp}BP%OQ$%>!-DUsfTULOfV#<=Q93VfI=Zr8BBm#ya~^{d!EVt*zU3sbwZ zNhIpq4HQpBIxDJ1j>HMN?{~iV2ma@~uaKXcgOMXgC~eY1_2%>VxlqzP5hpDp6WRfG zwoAOLkF00Kc-pd44fSw_yr`T~jhwPk>_rJpXU^iOosVNi-3$yHHwrp} zc0qPF@+iE7Mv;kVaYD1lM~B5~fRK8rt3%N=J-)9~AYvg+|Lr`Z%(5`$q+OGQ+xhM!Q_DMfJD*N(Tp{~R~nyAF>$wF}37 zsi!cDAgg*bYHz+4c?)V_sT>BR>v8n-as2U-hjGoE1(-Uff>l)`l`=Pj5;>lv`0{)3 z!{NgiDuz_AL+hn>^fA*xUr<3byVgjZZf2WsIL*NXj(znlMidmG;d}%3e*GD?Kl3>D ze)JE_;E)P8FM9h3Wvsz&hm*75A?3dP{g?Rsmt%N**Dk#C&O4YswH9+(`fXb_hsl7p?u*-4`aUNv_C)?Kp_2`4Prv+EeDJ{sNKI(4xU>qH zok5soHO`zorSx0eK>f_NJKNoXNwe!PVagPM2+?z~4Nea5mItB8%;zm7q117@WcBO?d}_|G()LXxCnj#0W2 zRnB&`V(0EZ5kg~VKXIDF>LGPUFnrQD%vin%e|&r=jD&^xWmQ-_yb|+=SK#W}ndosl z(eCa+fbzGSze&l=q$1Qp;&}s<#m7#az~)VNq3(tiIC#Du-yAqV(-9|BW|8elO8j1` zT8E+b44nUVKcw07=R(IxPGJ^~eD@s{Tsl%%0k7MIkl5o8j)cVG3Q-wyh`L=aR7{)% z3%_@!@d9qU^L7l#%ZFBP#L_iuux$Bq?B4SdJRJHkRb+(PTF7I2S~_ezv}6#CQIAJ< z?!e^9lMyng@zzHlV9Dy0M4yRRv;Jn>zw1$K+VK#+J$+1ZQ30~8fJwpFCi;g0vbki- zw9}00pz`@~ZPisMq4d;JpAbn#>29bUbYwYGyF%bt{60RmHOF zu2ZD1DJ=#5giweUjI6A{Et~Gf?e}lPv<35!px=y9lbq>nMOn=R1jr|Po0&<9A+8Z5 z9>Bx}bI=rYqfeWFmu(|3-bN#xpOOm6@Asms{UXvi*%tC`G~g$J7!*z1;pjnxuqIKP z_$iR=1G5pQXuFPqty;Sp3zjYh#Ra4AUuamyXA8@uwxh5an3AgQb}BTsy$IHQngGGrMH^#;KTRgx*M;@r12A= zqgnpp&><4141?I-V-m!Bp58uXUq!^@i-eR7BeB02kC5`yIjNDTf zE%XryMJ%Z}oDNpuWOFkfdip6WzHSZOOFmtTjRHuc?B0r7Ct|_kDvjsPVe6Lrl$%;h zmt6xeXkCg$v3nkJg%EVMbURkBU5mMk<|{j}L6;Yq)HuR?C)p}OVMaUwxG!}>OYNs; zVh!qk?Jl;kgF?_5qcVzf^T0Nuh@svkb@frΜe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png b/assets/dolphin/custom/NSFW/Icons/Passport/passport_okay_46x49.png new file mode 100644 index 0000000000000000000000000000000000000000..98d7e15f9c105cc36c1f6d3473fd915a9687968b GIT binary patch literal 6373 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Μe415)O~BBa>%J~wv1_Bs|XTY+nq zE{BUJ7RpcPb0I<7+uCsM#7SkrM~)m<_R#}uofs`vC=gJ#KXP(%krbAX3R1`67p$CR zPg6FH9Nj&N*AyB>_)3Gx0yD2|Al2){;PAY{%!h*heobzU`H^EcA}_^60%n64#h#Zz zfk7|7+-T6_!`604qpNYk0bFQFptmuCJ8s-UE4B%%XRpBaTQ`%0`eTIsT0d+SZyc+(BY&Q4dr!AKd>^vhYi zvHNcrn>!e3VVavjOtEkdf`GDjm|)N46y(Dn4k&jOM8<>*F)@=G3LUT44=;sIE~`>q zH5sEvk3(V6P;_>>(A3;cTo2(yLkn4R2R7V&A3pv50QP?J6>hute&x9Pp6xKDWW$zU z0<9^7*9f7b%>|<+6Fm-LjAQuu^eN1u8CP~1#C9E#OIWu3M-Cw)BSTS!q6%RSu{dHv zj~_gF6h1PajjB&5Kw+06LeL9Gr|!RLl|s$X(cYuni`3FI6G~7uk^XXI1;$LAiNX<; zFk}`Y2ph&!&4x3sM^LikOuHN3{&)uSm)}Gr+JNP&Zp957He>yr4`KTso<%reC8?Q_ znlS*~^qpEs5Qo@CqiiM7SWPLc7-Jyyi@R=JJw5PpNRyNU(y6vaIB0C!9Gyzz3&9>B z8Tk}Q(ufUfzV9ZHSkTbWfQ~K)B5?_gEp1RyE*v@4jEjyiLXrjgi~_Vcf*3Zw7Q-h@ z$Asw%QCvP5j<6c_O}(gZal#Wb!5z_|xhsU{UwsdcKl^92w6`nzB;@vrT~4?=yU^Hl zf!9eW^P$u9`%Smk1GSlpFj6XbjVdJQOooph#evY{QcJTUZj{m_V#v-g!>E?oLLp>l zWl$rxpq<%?v4Gu9FKn5)Fpp9}P_%ICSg`&YZu9!za$7snZQ(Y91=5&So{H zqj1=G3@xic_Mj2yaE7?>6VMw?%BH@N!3&G;CJYW5G8jQ#U+8bqy5bQbhI@$hCpa6D zoN9)jrqn{ZRTI+UqZVH4%3kAX&rucB2f3@k#ACooqWCemzSP7WbUg&y{p zB#FB}YO)-t;e&AXc9Auc${{{UwVG&{T}Wl7h+m2h5_(?jzKS~v;@M9N=IHE(*XcrA zS2s3pyaN_yJ=oqsOm{)A)8OS-_b9i?lgj-wm9nE1BpZiV1>p^(ZI3*FBPWg%RYF7( zv$9iUAnbH?btx?qV!zA8-m=n>Xl=fTFm;StqeGC9M@b%HcOc+%LlQ!a;(u__Kr~ZO zHPoMko07rT>p)*8y(+8H*3^R53ytVyPs`#KHxWYI`xn|>+-7XLa2^9Fcm0ICef$2! ns`R&sI4=J`0;|oYY!>}5j)ML?*}Ww%00000NkvXXu0mjfhkXNe literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..2528ebc95d79335975511a384c70c010d476a8c0 GIT binary patch literal 4862 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.png new file mode 100644 index 0000000000000000000000000000000000000000..fef503263fd962534e7e5543954612bf6c1faefd GIT binary patch literal 4882 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/dolphin/custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.png new file mode 100644 index 0000000000000000000000000000000000000000..78c4e93f8a8c4e94eb0513c7b59f6dde68748205 GIT binary patch literal 4466 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 Ef`SCamH+?% literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png b/assets/dolphin/custom/NSFW/Icons/Settings/Cry_dolph_55x52.png new file mode 100644 index 0000000000000000000000000000000000000000..d1164c59483a2242769327b5b2c9ac01acb83186 GIT binary patch literal 3798 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 M07*qoM6N<$g8AuxBLDyZ literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png b/assets/dolphin/custom/NSFW/Icons/SubGhz/Scanning_123x52.png new file mode 100644 index 0000000000000000000000000000000000000000..a48c5330e85c2135866fe99bcb6f3534d06c6d53 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connected_62x31.png new file mode 100644 index 0000000000000000000000000000000000000000..bc1010ca9da500055615c00359ea7536df4caa01 GIT binary patch literal 1874 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 Bfj=Do809goM&!5F$zl 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png b/assets/dolphin/custom/NSFW/Icons/iButton/DolphinWait_61x59.png new file mode 100644 index 0000000000000000000000000000000000000000..4beec55efbc4a6907e37e5d7422b9f7c33d3dbf1 GIT binary patch literal 5122 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 literal 0 HcmV?d00001 diff --git a/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/dolphin/custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.png new file mode 100644 index 0000000000000000000000000000000000000000..90b589ff8e9f18b28d7fd75ca525a1db24381b06 GIT binary patch literal 4719 zcmWldWmpt#6ovt*r8`8rmhMJ!=@OO>k#6bEB^D46mhO}g5CkMXLb^+&yBi5XTKL8v zGxKAvnd_Z1=iJZpys>XIm2k0MVj&?R;i@P@bbzZfa0p_e0OyTGRp5*Sa#J?)L_)&r z{dXV_x>i^tAraB5KxB3OmR6U2+Y@)6G0vwQcuM8ujE2`FBy#HY>g_Oz8998!hR- zR}W{Mc9Z$Ry>Q$nMg|53MnRnEVB_p^xs68kC&4ywoRC5pNkdZENQo7nk@T2(76rK z?AMw!mlo3^+$IWMy7Rv_7DmRChlii%f?IhfG#(5_VlMv`jt5A<-e{>p?G_9d@fi9GOPG zr2kM7eenI2|5?XP*RRS%=_kM0N0>Oi;=_x63)xt;hf~W4it9lZ`nEb(KYD^5ZdrL zU17_euQpEh*mYdzCFO!V+85gsxL^A}3fefwe1zro3hb=_f{D^ek8_O!i zRu-%EI{b-42f037#hxqA52*C{wM4Mb9e8ufWo|c|jMkqk^ES`)d(a)w0~)!a+B!bi z3r3{z4Ys;JLH7`gbmbG`@tBrv;VNOf=4ahCk~T3GPfYUcy3h~>3^bk1gP{*=+o#sd<8G%5sHVJGE^xW z^4j*_ScF3vJm&36yTl;lA#;1Wk!Tzh6xOd~pQN?vPL~?vMiE-NoYshGMa57#;?5N| zgjKefr)~R#zpM9XhIxp85lp$8R1*Mr+zmLe&3#+ZBNa>#OE*K(6ISBH&k6RY4ifaY zmb}~q*gSwWp2@czjzVaLYVLPV*o;dfX&6o||azG~|ziWq2;O{D-rC8S| z!c3>u0<`LEm4W#iQK|cbl2pEy)l8S`lf}nacI=n2DB9g|EM4{dsfbp3F_a^P!|*h7 zfJa&Q28|^mq&H}2#YAt13)d1GJN2sc0r;yu68|>@fFoI9209$;1jEsPg$hpQ7yk$Q zEc^_)L|WknhCeclcuu|^G{EfMP!w#~6X9h8ktxk~?>eux>b(Rx zf=l1+#uBr}hv9xh$Q&kr((eoj#vWKJ5Ftn_H!N;J)n6&W`as5IEK>-Kn4eXB`rXsR z8LgOy_0OQ6btaclJ<}RO0kcdkV}iaoN5D?A z(@ud>p5tx%?c?3$q}`ix)wClR22B!}z;ig6`MiEw-RPfaM;gB%PNN#n77{QI9Ehjf zj${hMi?^%7;g_LuGCj*FQel0hXo4mllll0h$!HlR5 z_Xaw;JueGBjgCX{^<6g#6{BK2Ce_sV@BwMly5DvJAa?nKIA>=Jy=xx3BlIONHD9J> z!(xIz_1Pq)b!y&$>G=}$)$=8d*zWp>wQ`?LAAhRxWDT{rZ)aMz;=eIH{#mLv)++M| z6Mc-$3`v9k8`zo8&^GU5#YMkHfQ?w8BVEqb#*IgvbXzYwlpGpAem*iY-xMm7W_}-} zep@ubmNSYLNAv>)!0V-{JluS0Ir!n)Wx6aOlN1?Ha{lxfONvd58n6qX4xSIcnr}`P zJpoVA9da2|WNP8nB`2~O+GA5zklZC=RStGmx&AhKAbL(m@UW>u&dE7fd?=(vB1&0 z2WbLNilS-cbk!s2Hq0Voi@R*fGfKXEao|;1thdRL@U3~86bicfSZ(61fI0-&2{?wS zBY;Vu8<}QyoRS%4tns}`V&X{HJOIsw?__Zpa9=o8sEkfb;Ti&*7*AnMTzZ(VfG4wP zN&ViR)KYR|{|q&5aocQiSzc;#p{j*Sv~dNA!;o7PefVPjG$4O3QA*6lR!8nbyX@a2 z`l?##0|%3V!FRXYiLB$S1NX~$D>|Ic>)-!QRt!kC!XBe?tVNd=S<4XlbSQOe9l3Gn z5?czt^_(}i1?fSIi(#lD&I?I9Dmfb2f<;dkQmkZr$@N#sJJI#-zMx3Ef?%?TA-CIB zS$eRd=sDm`&X${bE;`g0LDCHpZQAPpVHPAbuLWQ`nr;FHj?JUUR>z~n9zY$fERF}| zs`{I?l$c(%kp`q<{X!YoNE!#nl(nF`S703H!s^&9(Ry2|67B?k3=XW9 zzxRqrD)j(I3&Z|C+=OyfM)3$(8M`0VdLhKQ?>o5=7Ucf*FHSI}`z=~wGIM4lscf6} zT4U42E{^R9EXp_g$g*m25vDY~9PsK^^E8#OgYA68j5VckhOt6R_~_OebHmB;umAbz zrEiCnu$X7FHXmlb0c44gqx%Xn?7kGCJ#0b>Y~~L3-CPFyy?C!W%YHx|NoaZ_=c&pH zHCadGs2Jj57pQ@+Ru>X`qOb?!o$}#RpaQLlT(K0@e5n}NKA|>kpkeWOS|ne8HMkc$ z#Jc)zN2?Cgz1i$HhiQVOKsWxxfEtGiDd;(ogep1vJV&wdtyF@7Nt@3XWekqAc<(Ro z44|${-`M6t(2Z`bnn}I^13itw7s(D~7wlnOd=G-^bKHX@f|zL^_i-K%iP&puf`EC# zy0H85umkK_sZfg;p-eQ6E1+lj<%5w)lgwp=u{c#pgFxBgqm-}?#z8pB+c zm?ERDLVmdNn`+*5@?#q`WQ7hoj#9OZC#K50g8c;snWi){9Dd`aC*Su71f2Q@pNDf7 zPeV-3+@hl4;8jkgd`j`xdY>pP(g_lX2uqA2nd1=&o6ajqKVI17_V;>Ga@^6Ll0&5JKnq@FA`coMFFH{LleA$KEToNR!9iqRN_ z@3LzbL(~ndwYtO6Hob3FeLV*Q+_=)mfFNYj{603dWC$vSxk+|dm`YCLrDoR+8(fZT zveDfyz~JXc^4uHSd{~+J`2pJ5^(QWIGsg@P!B)yd1>ki|n@ zzwTTZg=4y0OWOVioztWOo!Q_=YG7SvrK+Sf6Rw3{gpg{)#jln(a`;Y$h7Yv_xo4`9@LUv6HONjzbl2D&s+}X`H1|m*<@#)$mMbQE zw{e4Bf(lj#f36~T9m|&Y8}&TR_wqhl>j3iMCS_&gx1$9}_~zE>n+SyLlAG>I{3|gJ zAin^m<9XZe=O@XTB8nwz2BcIx!-KL($Jxr*n{!$=-VR_^=Fsb7(fInxqNt@qf(oR9 zInk;(vBFvQM$)Q)sCztLo3YUoy0L9mV!qhtd|gnID9p791ZSt&%0}y9B9$g+xn?&~ zgkIXfuI?A(wB2h~y1s-8NR9Jiz1Sx~Oxij5ES3-Y3xGgtMH1fUN%&&DwZJn<(V4dd zJuSHjg0$S_NXj+yUY-NDn(VRIY*&45zkLivuXwnqI!$HK$o5+|nL6{-X33E>WfJ5h z9{r@s4?5%Rc>$>tvl@t}c^x1#uHBdFb2Rh0H-+B#iWq|tzn_*rnT@p&yA{-ZE<*-)cP^^G$sP=uT6gfR+>pWNYFepxj!CC;{mWdx#Or>hVAVSO|v(u4hRHBGW1z8nN zu|QSt9eyh5I;0;mR)HRra5B;r`Qe`{7z}P&%^-o{aQT)?1KkF6z5<#f)I+Iw$1Cm~ z#YneU0tjBSsMkS~SSydosr{|_GT`xmy$+D{eMYBl2FhZ>t`lk&uLX)p7lpA-Kmb|i zZL(s6^>DGBAc)zFH*E95ZG%w*XB`qv-X0=Lh6iIL5V%SQwpAqPe*#Ue|HZh7VpwM< r)>0N1a?-|MN#EE<`m`LhrSu@}&-9CHM!8ht-xiXJf+nO!?p^r*4mcZk literal 0 HcmV?d00001 diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png b/assets/dolphin/external/L1_Boxing_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_0.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png b/assets/dolphin/external/L1_Boxing_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_1.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png b/assets/dolphin/external/L1_Boxing_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_2.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png b/assets/dolphin/external/L1_Boxing_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_3.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png b/assets/dolphin/external/L1_Boxing_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_4.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png b/assets/dolphin/external/L1_Boxing_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_5.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png b/assets/dolphin/external/L1_Boxing_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/frame_6.png rename to assets/dolphin/external/L1_Boxing_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt b/assets/dolphin/external/L1_Boxing_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Boxing_128x64/meta.txt rename to assets/dolphin/external/L1_Boxing_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png b/assets/dolphin/external/L1_Cry_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_0.png rename to assets/dolphin/external/L1_Cry_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png b/assets/dolphin/external/L1_Cry_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_1.png rename to assets/dolphin/external/L1_Cry_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png b/assets/dolphin/external/L1_Cry_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_2.png rename to assets/dolphin/external/L1_Cry_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png b/assets/dolphin/external/L1_Cry_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_3.png rename to assets/dolphin/external/L1_Cry_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png b/assets/dolphin/external/L1_Cry_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_4.png rename to assets/dolphin/external/L1_Cry_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png b/assets/dolphin/external/L1_Cry_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_5.png rename to assets/dolphin/external/L1_Cry_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png b/assets/dolphin/external/L1_Cry_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_6.png rename to assets/dolphin/external/L1_Cry_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png b/assets/dolphin/external/L1_Cry_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/frame_7.png rename to assets/dolphin/external/L1_Cry_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt b/assets/dolphin/external/L1_Cry_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Cry_128x64/meta.txt rename to assets/dolphin/external/L1_Cry_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_0.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_1.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_10.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_11.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_12.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_13.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_14.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_15.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_16.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_17.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_18.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_2.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_3.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_4.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_5.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_6.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_7.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_8.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png b/assets/dolphin/external/L1_Furippa1_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/frame_9.png rename to assets/dolphin/external/L1_Furippa1_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt b/assets/dolphin/external/L1_Furippa1_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Furippa1_128x64/meta.txt rename to assets/dolphin/external/L1_Furippa1_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_0.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_1.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_10.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_11.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_12.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_2.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_3.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_4.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_5.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_6.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_7.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_8.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png b/assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/frame_9.png rename to assets/dolphin/external/L1_Happy_holidays_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt b/assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Happy_holidays_128x64/meta.txt rename to assets/dolphin/external/L1_Happy_holidays_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png b/assets/dolphin/external/L1_Laptop_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_0.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png b/assets/dolphin/external/L1_Laptop_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_1.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png b/assets/dolphin/external/L1_Laptop_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_2.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png b/assets/dolphin/external/L1_Laptop_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_3.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png b/assets/dolphin/external/L1_Laptop_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_4.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png b/assets/dolphin/external/L1_Laptop_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_5.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png b/assets/dolphin/external/L1_Laptop_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_6.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png b/assets/dolphin/external/L1_Laptop_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/frame_7.png rename to assets/dolphin/external/L1_Laptop_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt b/assets/dolphin/external/L1_Laptop_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Laptop_128x51/meta.txt rename to assets/dolphin/external/L1_Laptop_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_0.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_1.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_10.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_11.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_12.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_2.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_3.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_4.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_5.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_6.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_7.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_8.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png b/assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/frame_9.png rename to assets/dolphin/external/L1_Leaving_sad_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt b/assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Leaving_sad_128x64/meta.txt rename to assets/dolphin/external/L1_Leaving_sad_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_0.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_1.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_10.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_11.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_12.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_13.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_2.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_3.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_4.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_5.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_6.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_7.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_8.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png b/assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/frame_9.png rename to assets/dolphin/external/L1_Mad_fist_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt b/assets/dolphin/external/L1_Mad_fist_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mad_fist_128x64/meta.txt rename to assets/dolphin/external/L1_Mad_fist_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png b/assets/dolphin/external/L1_Mods_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_0.png rename to assets/dolphin/external/L1_Mods_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png b/assets/dolphin/external/L1_Mods_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_1.png rename to assets/dolphin/external/L1_Mods_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png b/assets/dolphin/external/L1_Mods_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_10.png rename to assets/dolphin/external/L1_Mods_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png b/assets/dolphin/external/L1_Mods_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_11.png rename to assets/dolphin/external/L1_Mods_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png b/assets/dolphin/external/L1_Mods_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_12.png rename to assets/dolphin/external/L1_Mods_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png b/assets/dolphin/external/L1_Mods_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_13.png rename to assets/dolphin/external/L1_Mods_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png b/assets/dolphin/external/L1_Mods_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_14.png rename to assets/dolphin/external/L1_Mods_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png b/assets/dolphin/external/L1_Mods_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_15.png rename to assets/dolphin/external/L1_Mods_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png b/assets/dolphin/external/L1_Mods_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_16.png rename to assets/dolphin/external/L1_Mods_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png b/assets/dolphin/external/L1_Mods_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_17.png rename to assets/dolphin/external/L1_Mods_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png b/assets/dolphin/external/L1_Mods_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_18.png rename to assets/dolphin/external/L1_Mods_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png b/assets/dolphin/external/L1_Mods_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_19.png rename to assets/dolphin/external/L1_Mods_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png b/assets/dolphin/external/L1_Mods_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_2.png rename to assets/dolphin/external/L1_Mods_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png b/assets/dolphin/external/L1_Mods_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_20.png rename to assets/dolphin/external/L1_Mods_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png b/assets/dolphin/external/L1_Mods_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_21.png rename to assets/dolphin/external/L1_Mods_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png b/assets/dolphin/external/L1_Mods_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_22.png rename to assets/dolphin/external/L1_Mods_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png b/assets/dolphin/external/L1_Mods_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_23.png rename to assets/dolphin/external/L1_Mods_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png b/assets/dolphin/external/L1_Mods_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_24.png rename to assets/dolphin/external/L1_Mods_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png b/assets/dolphin/external/L1_Mods_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_25.png rename to assets/dolphin/external/L1_Mods_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png b/assets/dolphin/external/L1_Mods_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_26.png rename to assets/dolphin/external/L1_Mods_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png b/assets/dolphin/external/L1_Mods_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_27.png rename to assets/dolphin/external/L1_Mods_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png b/assets/dolphin/external/L1_Mods_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_28.png rename to assets/dolphin/external/L1_Mods_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png b/assets/dolphin/external/L1_Mods_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_29.png rename to assets/dolphin/external/L1_Mods_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png b/assets/dolphin/external/L1_Mods_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_3.png rename to assets/dolphin/external/L1_Mods_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png b/assets/dolphin/external/L1_Mods_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_30.png rename to assets/dolphin/external/L1_Mods_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png b/assets/dolphin/external/L1_Mods_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_31.png rename to assets/dolphin/external/L1_Mods_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png b/assets/dolphin/external/L1_Mods_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_32.png rename to assets/dolphin/external/L1_Mods_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png b/assets/dolphin/external/L1_Mods_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_33.png rename to assets/dolphin/external/L1_Mods_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png b/assets/dolphin/external/L1_Mods_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_34.png rename to assets/dolphin/external/L1_Mods_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png b/assets/dolphin/external/L1_Mods_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_35.png rename to assets/dolphin/external/L1_Mods_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png b/assets/dolphin/external/L1_Mods_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_36.png rename to assets/dolphin/external/L1_Mods_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png b/assets/dolphin/external/L1_Mods_128x64/frame_37.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_37.png rename to assets/dolphin/external/L1_Mods_128x64/frame_37.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png b/assets/dolphin/external/L1_Mods_128x64/frame_38.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_38.png rename to assets/dolphin/external/L1_Mods_128x64/frame_38.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png b/assets/dolphin/external/L1_Mods_128x64/frame_39.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_39.png rename to assets/dolphin/external/L1_Mods_128x64/frame_39.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png b/assets/dolphin/external/L1_Mods_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_4.png rename to assets/dolphin/external/L1_Mods_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png b/assets/dolphin/external/L1_Mods_128x64/frame_40.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_40.png rename to assets/dolphin/external/L1_Mods_128x64/frame_40.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png b/assets/dolphin/external/L1_Mods_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_5.png rename to assets/dolphin/external/L1_Mods_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png b/assets/dolphin/external/L1_Mods_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_6.png rename to assets/dolphin/external/L1_Mods_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png b/assets/dolphin/external/L1_Mods_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_7.png rename to assets/dolphin/external/L1_Mods_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png b/assets/dolphin/external/L1_Mods_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_8.png rename to assets/dolphin/external/L1_Mods_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png b/assets/dolphin/external/L1_Mods_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/frame_9.png rename to assets/dolphin/external/L1_Mods_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt b/assets/dolphin/external/L1_Mods_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Mods_128x64/meta.txt rename to assets/dolphin/external/L1_Mods_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png b/assets/dolphin/external/L1_Painting_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_0.png rename to assets/dolphin/external/L1_Painting_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png b/assets/dolphin/external/L1_Painting_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_1.png rename to assets/dolphin/external/L1_Painting_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png b/assets/dolphin/external/L1_Painting_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_10.png rename to assets/dolphin/external/L1_Painting_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png b/assets/dolphin/external/L1_Painting_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_11.png rename to assets/dolphin/external/L1_Painting_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png b/assets/dolphin/external/L1_Painting_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_2.png rename to assets/dolphin/external/L1_Painting_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png b/assets/dolphin/external/L1_Painting_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_3.png rename to assets/dolphin/external/L1_Painting_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png b/assets/dolphin/external/L1_Painting_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_4.png rename to assets/dolphin/external/L1_Painting_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png b/assets/dolphin/external/L1_Painting_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_5.png rename to assets/dolphin/external/L1_Painting_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png b/assets/dolphin/external/L1_Painting_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_6.png rename to assets/dolphin/external/L1_Painting_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png b/assets/dolphin/external/L1_Painting_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_7.png rename to assets/dolphin/external/L1_Painting_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png b/assets/dolphin/external/L1_Painting_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_8.png rename to assets/dolphin/external/L1_Painting_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png b/assets/dolphin/external/L1_Painting_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/frame_9.png rename to assets/dolphin/external/L1_Painting_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt b/assets/dolphin/external/L1_Painting_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Painting_128x64/meta.txt rename to assets/dolphin/external/L1_Painting_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png b/assets/dolphin/external/L1_Read_books_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_0.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png b/assets/dolphin/external/L1_Read_books_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_1.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png b/assets/dolphin/external/L1_Read_books_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_2.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png b/assets/dolphin/external/L1_Read_books_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_3.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png b/assets/dolphin/external/L1_Read_books_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_4.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png b/assets/dolphin/external/L1_Read_books_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_5.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png b/assets/dolphin/external/L1_Read_books_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_6.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png b/assets/dolphin/external/L1_Read_books_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_7.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png b/assets/dolphin/external/L1_Read_books_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/frame_8.png rename to assets/dolphin/external/L1_Read_books_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt b/assets/dolphin/external/L1_Read_books_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Read_books_128x64/meta.txt rename to assets/dolphin/external/L1_Read_books_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png b/assets/dolphin/external/L1_Recording_128x51/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_0.png rename to assets/dolphin/external/L1_Recording_128x51/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png b/assets/dolphin/external/L1_Recording_128x51/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_1.png rename to assets/dolphin/external/L1_Recording_128x51/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png b/assets/dolphin/external/L1_Recording_128x51/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_10.png rename to assets/dolphin/external/L1_Recording_128x51/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png b/assets/dolphin/external/L1_Recording_128x51/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_11.png rename to assets/dolphin/external/L1_Recording_128x51/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png b/assets/dolphin/external/L1_Recording_128x51/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_2.png rename to assets/dolphin/external/L1_Recording_128x51/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png b/assets/dolphin/external/L1_Recording_128x51/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_3.png rename to assets/dolphin/external/L1_Recording_128x51/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png b/assets/dolphin/external/L1_Recording_128x51/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_4.png rename to assets/dolphin/external/L1_Recording_128x51/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png b/assets/dolphin/external/L1_Recording_128x51/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_5.png rename to assets/dolphin/external/L1_Recording_128x51/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png b/assets/dolphin/external/L1_Recording_128x51/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_6.png rename to assets/dolphin/external/L1_Recording_128x51/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png b/assets/dolphin/external/L1_Recording_128x51/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_7.png rename to assets/dolphin/external/L1_Recording_128x51/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png b/assets/dolphin/external/L1_Recording_128x51/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_8.png rename to assets/dolphin/external/L1_Recording_128x51/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png b/assets/dolphin/external/L1_Recording_128x51/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/frame_9.png rename to assets/dolphin/external/L1_Recording_128x51/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt b/assets/dolphin/external/L1_Recording_128x51/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Recording_128x51/meta.txt rename to assets/dolphin/external/L1_Recording_128x51/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png b/assets/dolphin/external/L1_Sleep_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png b/assets/dolphin/external/L1_Sleep_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png b/assets/dolphin/external/L1_Sleep_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png b/assets/dolphin/external/L1_Sleep_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleep_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt b/assets/dolphin/external/L1_Sleep_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleep_128x64/meta.txt rename to assets/dolphin/external/L1_Sleep_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_0.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_1.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_10.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_11.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_12.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_13.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_14.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_15.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_16.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_17.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_18.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_19.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_2.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_20.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_21.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_21.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_22.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_22.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_23.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_23.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_24.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_24.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_25.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_25.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_26.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_26.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_27.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_27.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_28.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_28.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_29.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_29.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_3.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_30.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_30.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_31.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_31.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_32.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_32.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_33.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_33.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_34.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_34.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_35.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_35.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_36.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_36.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_4.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_5.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_6.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_7.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_8.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png b/assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/frame_9.png rename to assets/dolphin/external/L1_Sleigh_ride_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt b/assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Sleigh_ride_128x64/meta.txt rename to assets/dolphin/external/L1_Sleigh_ride_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png b/assets/dolphin/external/L1_Waves_128x50/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_0.png rename to assets/dolphin/external/L1_Waves_128x50/frame_0.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png b/assets/dolphin/external/L1_Waves_128x50/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_1.png rename to assets/dolphin/external/L1_Waves_128x50/frame_1.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png b/assets/dolphin/external/L1_Waves_128x50/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_2.png rename to assets/dolphin/external/L1_Waves_128x50/frame_2.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png b/assets/dolphin/external/L1_Waves_128x50/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/frame_3.png rename to assets/dolphin/external/L1_Waves_128x50/frame_3.png diff --git a/assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt b/assets/dolphin/external/L1_Waves_128x50/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L1_Waves_128x50/meta.txt rename to assets/dolphin/external/L1_Waves_128x50/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_0.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_1.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_10.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_11.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_12.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_13.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_14.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_15.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_16.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_17.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_18.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_2.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_3.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_4.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_5.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_6.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_7.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_8.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png b/assets/dolphin/external/L2_Furippa2_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/frame_9.png rename to assets/dolphin/external/L2_Furippa2_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt b/assets/dolphin/external/L2_Furippa2_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Furippa2_128x64/meta.txt rename to assets/dolphin/external/L2_Furippa2_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_0.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_1.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_2.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_3.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png b/assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/frame_4.png rename to assets/dolphin/external/L2_Hacking_pc_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt b/assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Hacking_pc_128x64/meta.txt rename to assets/dolphin/external/L2_Hacking_pc_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png b/assets/dolphin/external/L2_Soldering_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_0.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png b/assets/dolphin/external/L2_Soldering_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_1.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png b/assets/dolphin/external/L2_Soldering_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_10.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png b/assets/dolphin/external/L2_Soldering_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_2.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png b/assets/dolphin/external/L2_Soldering_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_3.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png b/assets/dolphin/external/L2_Soldering_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_4.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png b/assets/dolphin/external/L2_Soldering_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_5.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png b/assets/dolphin/external/L2_Soldering_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_6.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png b/assets/dolphin/external/L2_Soldering_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_7.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png b/assets/dolphin/external/L2_Soldering_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_8.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png b/assets/dolphin/external/L2_Soldering_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/frame_9.png rename to assets/dolphin/external/L2_Soldering_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt b/assets/dolphin/external/L2_Soldering_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Soldering_128x64/meta.txt rename to assets/dolphin/external/L2_Soldering_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_0.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_1.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_10.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_11.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_12.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_13.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_14.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_15.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_16.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_17.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_18.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_19.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_19.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_19.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_2.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_20.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_20.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_20.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_3.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_4.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_5.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_6.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_7.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_8.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png b/assets/dolphin/external/L2_Wake_up_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/frame_9.png rename to assets/dolphin/external/L2_Wake_up_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt b/assets/dolphin/external/L2_Wake_up_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L2_Wake_up_128x64/meta.txt rename to assets/dolphin/external/L2_Wake_up_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_0.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_1.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_10.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_11.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_12.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_13.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_14.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_14.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_14.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_15.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_15.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_15.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_16.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_16.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_16.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_17.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_17.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_17.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_18.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_18.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_18.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_2.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_3.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_4.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_5.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_6.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_7.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_8.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png b/assets/dolphin/external/L3_Furippa3_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/frame_9.png rename to assets/dolphin/external/L3_Furippa3_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt b/assets/dolphin/external/L3_Furippa3_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Furippa3_128x64/meta.txt rename to assets/dolphin/external/L3_Furippa3_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_0.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_1.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_10.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_11.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_12.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_13.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_2.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_3.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_4.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_5.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_6.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_7.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_8.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png b/assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/frame_9.png rename to assets/dolphin/external/L3_Hijack_radio_128x64/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt b/assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Hijack_radio_128x64/meta.txt rename to assets/dolphin/external/L3_Hijack_radio_128x64/meta.txt diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_0.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_0.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_0.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_1.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_1.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_1.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_10.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_10.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_10.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_11.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_11.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_11.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_12.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_12.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_12.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_13.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_13.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_13.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_2.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_2.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_2.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_3.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_3.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_3.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_4.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_4.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_4.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_5.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_5.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_5.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_6.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_6.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_6.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_7.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_7.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_7.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_8.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_8.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_8.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png b/assets/dolphin/external/L3_Lab_research_128x54/frame_9.png similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/frame_9.png rename to assets/dolphin/external/L3_Lab_research_128x54/frame_9.png diff --git a/assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt b/assets/dolphin/external/L3_Lab_research_128x54/meta.txt similarity index 100% rename from assets/dolphin/external/sfw/L3_Lab_research_128x54/meta.txt rename to assets/dolphin/external/L3_Lab_research_128x54/meta.txt diff --git a/assets/dolphin/external/sfw/manifest.txt b/assets/dolphin/external/manifest.txt similarity index 100% rename from assets/dolphin/external/sfw/manifest.txt rename to assets/dolphin/external/manifest.txt diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_00.png deleted file mode 100644 index bf97f8d6ea7ee9d0df8714b06ceb37d5231d9444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1326 zcmaJ>eQ4Zd7|&g6?MkbT;lNE?$Og8M!GJD&u*4t@s!)f0n@AX1^NtUGE zwXE(eMNn}?w!u(_-xWbhL2;;HYyB7sbt+TrT-gM5)wQVqtVr4Fd%a%WA7vrQ`@CSAqj0<8t~M5`;%`?uBAdLU1l?)I_;8M>Nd*q&jT zc+nzItv)YIAhM+>vUWVaX4rIBbA_@-=YdVL6hmjT#n4u?T`vLur?IZKo9wqCoq(>@ z=V+)T>Fs4OO5e>L6%`R1=^@8JbpgGyrS)Z@>BZzQfxb|>p-VvkRRDlWY5|rdIVfl( zP}4lA1{nhYvZ6Hdfl!OWwKOwjY|L~$U}~`J z+d#fov5hyyO05nAp5-JhtGC<;9U05B+>B-898WQLeWzjSR?clG)~FP+3?1W1sPA&D zEWVOt$ykQ~;2RrxPK2b2I-L|$9Z9+xBt?M-X+h8gRz(4n@cIqOEA=TlK|$DME)v7S zbUFuXLm!ooP;(Z=wZTA}wb6nmnkNSh z&fzcZ4QS}9I`$?Nv@S0%zcus9EQa~0nB1JqpPxVZ%WYG5?eNcwM-1)m-TP;{=EwHW zU*o8MK6xZ_yPS9@x%0&Cx;@Xoeqr{kur%s_Z20uc2Oi#d_3Bpt^b>x}MeXBcF|a?fYY3 zU*Ux}E@Q7IW`dK~Dvw=1z3Fg!bc-_Hf7Um5{tMXm@6vSS7`7+(_V4dD%<|2PKXh(9 t_vj_AuwmrN!R4ChpjYcZd2uuCtHQXETaTW4F5FZ6gT<6~d90;t=s&rU%P0T< diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_01.png deleted file mode 100644 index 39c910d3a41314c985e4ce79337cbbf30d1ae79e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1597 zcmaJ>eM}Q)7{5}5%D^dpRi@)O7`W*5?s^B;R;rYKC`z%G8Y+Q}>)l)EN_*Y)Sc`6o z=qAjeQ#WD)}yh;LPt4dw`DnG9mu!RfIf~Wx+2mqNwqk+0$*bp^hBX$i?jvW&i zI-(-`jo2tDx7~r7MG2s+hQfK0BvDqc0RpG>&|Z$8hiXYJMNsf&af&r)X#=H4$1V&y zlmxHAX|avDgF7QuE6X7RK|~@EO@!8nQVl`r^?HKT5?U<|HSlmlQ0Ag|Fq{>)U;$xX z@`Yrd7(`SmTZhvAp7#_;NdvV zgY$V5g=+!BA+9YtBqdTWuzYR#wf^hH)} zk%^3Bi-TdA3-TcD+XsD*VNK7)8q5;lWKnX7VqLru9JQh>hHJ$Ts--m~I;Y$h6vRmQ z)mV*2L$(0P_ZARrk{Ccol5Frjg8)b8=hIpi;2zlNIO7qZBzSZ<%g~&cVR(k}zyL<^ z!c)l`?NfLHg9ydB7)T0u^LR2J2Y}JzJj+nHhbFbSmt$F)CMg}muowX;60yE{s*mDF zusLJmlfi(SC!+;|aGoSMIBLq(1&1!tQR1?~b$EDq|K+_`5G27^Y%#f_f823bZBb^b zgnx(k{&b_X%C@ij%eKlR1xz4AxW=oeHrzdS@wL@=Po(b|Y|8y{=6&U!_b23JK6qSl z=Ul(3U+Z>w81&36ROoa>Vw};AiR+k0+1z%t8NFY0jwL6AiTw!i~cvHBP6VAaH(t%~zvICA{Agl`I%Vxu z68zSrmC0!dz3ofM=AK;@KX*2Lw5i2p+ufM1m_OK}@82}P>xjFuZtgeW@!@ny9%#*) zwmG$JIs3-vs-E7n-E*90d}+P&5)voBGI-Sa>GrGlauUsV&W&$Ps>}&tmW&aP|S$*a?s)anPWU)!;^ zL#VILC2LNl&CYCZzS55@y|-=+x*uu2(bC40EMboCNF}Cqe)a1irm04ewgSz*9By^% z53in~QmPX@4W{I>O!-`%ZUS=a#>U^Lq$RausA>A~+>cfMi{lcGP4_BNmUXd;&QI)Z z==qd;({~>{d1mb{bt%%QGTu(SaN34cx>QKxEJgeM{Y=w%WCnXCBX!LKlhR`U;qFB diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_02.png deleted file mode 100644 index 4975adf8627b62a22d002bfc1d7be423dcc5e83b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1754 zcmaJ?c~BE)6kiEqM1gwIQV`cr5o$NPn}ZyZawR~JAjTlrk&(@2V^T;qCJO`*jYwN? zzJ0&UL^uF2iuI~ADov3n#z_MkbLz0I1`~}2fba;b3Bxl93RDvt^hOD5;Pe?5sMknX zseA>ZFvSo$eSE%|Se>7w!t*n6k%krVDHv`Q(*_I#g@IN>meC@%N>~$i#kA}^hFRc* z3Y95gO_EAeB!e-enE(ZB6v7b%0R;fhfaICZgT zhh~jdtdz!1xubUymX4xKVi?ZN&1L6u*`#>`jEY1e7~#Mi4n%7}mOLYcSs|k($YnuF zSa7r6MCnN*=(LEb$!to(qRCE1VK6BaZwwnPQ@NtE23s)`jIt5fU~q;up>3g*#D6s2 zscli^nFv@(SjcQMPM1U*c(#EsQgW$;MSo#y^ct}c zBh(m%sUe(?q7aAR;Si?Qav`-4!7-r*!?^;X%g(#-B9TB8%jHA~I5HuM%3?&aXsJvV z#^sBmWt`|J#D$d`Efi+N372m@?RyF<`d_R##!O%oX;zVBma7qxbtFYvbfgL7aM=hL zlBzdq$XrXPvqqC4O9`_+htR~DNdq{MWU>Ao1Tbz`7?&d;AT{0T5Kpb4C5>7L33yyg z%j4lZR80pkiPyZ9yvaVLPv{^_E-qpOCA54T34;iNCxUPR4~5iRgac_Yfq=_JP$7>e zV8Jv+*x5I4^^xm{ZceB8W-#c@o6#bS^gNmA!MWz%;YAOfM{=S{Mz0Qs<3MNiJpgd) zl1rmh)~^1wcgr6m%w~t9I_nxTE`~My=&O-8 z^gpbe?g8|F%k{EVr;;O0dg4dtG7da+A?1Toh{ro$cLX)S$u4NTs#)APzctwFBm`J-;zH4 zG~j+QWNYc%rB@myO~`n2pzYl30R}T3&QPu?0OthkE<)vN@N<2Uvm*Pq+pGAEmix8S=nd9m#njz_o z)mNE70C33D#l~~-VT%Zg)r&~Gj>xJv@@N1ER9KB9wTj_D4U?=h$b~)C7lfdWmJ8$j z!;mmz2$P~)kZEFKGQ(r3%vF?(7Aod~fmVVypl3J|wCYn0X2L2LzP3y7vi%qqg0EG$ zRdV4Vsrax+FoZQRATB~73PBJMmx&mf#57_pNzDc^1Vdqzzi|k~2~13&GH~b;@(xY3 zmWWoWhurZyxiE#}j06l@EEbVPEMiT`Fe;PDU<89P4B|B)bB2Kugd(LhNr`dku$_0|Wini*7Go+LQ%O-&6(UmwD^;ohvA--> zg$2(;hOwarGe;UIX4tom_dSGFz86b`m>7~{O|dMSI@}16DJ;jDQ&=O2iA4zL8>chS zti|kSuhC%0O2(v1XK1yF)q}5-Oz7T0fD{J=h%uajG<>H+5)I8uv_=Zy5;3WjNGJ)a z;R6`N({Ckjuuu6DJ_zG57Xm>Utv`hXKnx?1K@=`QA&nTpAT5dGVljeBB@$c+^Aurw z-@MgF!$*8`+Qm16!EfG-7GvP&$;1!Nn#4Z)*f~WmidFIJz`#IRsv7#R+qh3KqUKV$YLpsF+-C>Ad1 zW5>=dVf}1sI?^UO=z_-BRDvKiqR!gx~J?s>wfxY|O9R z=3<%TTyv!--`al0bHepClGHZaLthvA+^UGKm8UDa^XhJ&LO!34xwW?BwHEl^*cY-~ z<8`=PGTBGH@UQB_-Fu367GaZe+zy?sU%%opS!1ew<>HilXz7!K%hYRTwIl^?#-}*f z1@ul;6k?`QBD!{0eq6!w)V()%oo;Mv&+BX<;EJruyw*DRnNDc~;QgB4(jyyYOl#V4 zKjtX6d&H=T8#b$@NiiP<{E*-MHL}KC;E)jSZWDr^Y&Z%6BZIC-Z|b#-?HQLFTsSJS z@$Q#_w%gOBZtV~6c9!ORQ`9$FQ0rII=XiRz?nsY&dH2H2<^3{|#OCdI(@=P&b)sPW zyMOp7w6(KXWo40nJ zY|eY}PX@?#b!4yazvau+ju3P#d)l$6(`iE4%ZY-9f5|pwy$K*30`J*m9hcRstx#3CvLZO?i~j&Y CQO3gn diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_04.png deleted file mode 100644 index e6c88df92b73b89f1ad31810be1a653c3fea5d6f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1686 zcmaJ=c~BE)6we_o2w0(7EJ|I2w$PgFkwB7#KnMwk5ag0$7>>iMhk9}@h1G@VD8w4dC-Ij zQz+w2lFCpgLXotYg2V!pPa+5ciKPNcOJEwIjvyC97=ocN3SKcE72}u?N2Soz!vg`$ zS{ z2o0YMLQy_OiAX-7(Fyq)2|^MQEkO#!5~rUR;iXcsG)gGM#F$EgqN+%#N}*J#f`vg+ zg$h$dAWm$o(ZUc$l5)n@gVBBu}%3uuP66woFJ6ABP0AYE_N z(pF2L=2MhN$|uDll&=vY7+*(-#X=#1N<<gi@&~&dP#;2;lXt&#&t~K0bu{c{|l@Y17 z>-RHn7{7%);{LVo+jqBhkqhwFC4EtW3*T<$zCId!FiEzI1fFWbckNm0!wC`F3?{`-k_IuBzxtei%cnRJ}L)M0R{}2G+6F zZG*9Y7OSOh-!=B>@A8IMxp8A|xO=x=!2Y(+gJ-d;8f8QB1>^h2=QL`k^?dIQyM-j| zc4sTxD_T;{yi;1W<)2XO1@7zLRZQU{WAjPZ3s_`D;x8k!Rx(r zKja`3H`3xc5OAPI-m|UaXIjwS9Q;g z|NJ!wnLSd#RzRm}F3x!OXko;eggHLEjEeb+)zxy`<>_YMQx@?^U1w9t;}c1K@m{7o zOMYbiaeBb(0uyc-z1&v+X;R|B+QqeBmLg>`K?T>>PuurH!->W=`<=?xPu9n?my3`3 z&mOS$&p(%)uDO_#>eK7qD+r0I9N;9mtXS)}Fnc&`tj4{o`pL{(#jZ!9gTHL`??_vJ zfKzP`85l_E${B5GU~kg*br<+>+p_$N<2uSbpn2@F!~NZyb)lDw!Z{wSWiB%sx(wTa PLRqX>Rf6(}B5%!qVpw+j diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_05.png deleted file mode 100644 index e7bae4d6c8e7b60de8cb5d4751ae8d9259a39338..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1672 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 diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_07.png deleted file mode 100644 index 32e864e9827420142930083d480d331024d033f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1540 zcmaJ=eM}Q)7(ZwQL|`a!rcUQsm~or;U3zV$($W^GvxQU^s++UdySJr6@4EJ&1vM*% zeI;gS%*lpcNfsA0gc z;4`?ar4Pfwol#va%OL|nM59qW%HUGiPf&Wjo*-$0rZK32MH)nzi(z6UKk2~=B78Uy zk^_>6COmRpsZKVkA=u$8f+2_Fkz+CPuvV~YM2rg&6iyPsU?R04?TG9G|HF8qcBHZ) z1PB+1NOfTzw#1j8G=}ASdZ2_NL}Mrq2Vh^gTC2p@1%W8rtwuGx!G(Ze&~d=aahw<9 z3n>btfriI8uaCjJI+Evf0>?9~F6rk<_9djw!f16imbU3A%4X5qN~|`UmMPSi*ys{7 znY6Ww5t$Qtkc=IGvCV7=tEKhkr`j4UVZg~!xKfg8lSHVfmSibXErn2;!AW##RX`M^ zXynC2kA_pWf^eW72&G{uhz=Fm5O@Lrj?rovngy5_G9A-+1tgoGE;=1Mw!5dLIL>q&-FN=xw&*uC4Znihe$~NUAEd-L-0kus zvktd4b)`E?wVS_whgh+2EWSyhisJ_sDkr2?m?x%AIPEvRi)_AFgWT)A-;$?9Zk`oV zLVYi%ps58@;_dEc&+@jWF$=~m*faKDX(zHZXH?WP`ciFt`c{|Fvuq@?>tuWXt{Vb! z&3Wz*d$02K6q9mm8tck5QLDT1*md>GvZ{VYZ1vUYuDk-xr(((|(;@q++&ZNF1LPs~qy>5Nmh?FjI;C+jq@u}~$RyFRM)b3xp;ltj4 zUf7$r<(&U% zE4lk;thumebThty%=r6+vRkAVy);)DQv5hN>$`!&x?{7-dKcfxHhtdGbotBIkc-zp zt~M^wT)%m*{C>Ksr98cC%PMNJYx@)?@A-Qd8ZCi=E6$PI?~k0V=*Uv$A3kUNA@v~h zY7sUVMW+zEytcAL|>t;DnRKT5(UD*ylh diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_08.png deleted file mode 100644 index c692f48952c106373b56ecafd4bce7ba1295f5da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1557 zcmaJ>d2ka|7=JXS#89*jgNP!m+d9ax$sTDpS(;usXn;T{(Nd zzTbE48t1HOBQ$Sl001NG#g-DZE=Ny+T7}*n>U9pZj8Uv*%3Rr}MENiTgNH=xd5nEiLNrG%rTP_l7>HN@+n_tNGvR(k32{1IG#b@L8Lb?y)KNyGQAg4`nkJA25vh?BK1N89%%lYij0j;d zq=>SFB`oqDxmq#di0nWV!H~o8)UXs8%oWO-F2;v+l$O*5gNe}kwIfOi{GY~iwIi;Y z5Y&~xh+G{OP)U55Nia&^iyb8t5gTrHSVVQ<7g}VYItV4jZZYBLi`FZ8IRg(pJkNUw zA)BHI8tMgt_xKpXV;}|I;N=B|H6-mk4_`zY%uJ4vW@)Q|qO4}4wa8+%<}lgDA}d`~ zNG7p%DWdR_0F%B&&{G~1zyRL+ zO!5Z$6rG?TLP;(hNkLz>K;{q->WzfJ>M6p*kTl`rS(af)%AnV?xDHX&CHm%>K1v>; z=1hoB2Lo-Mjuw>AJcZHVd_T8g6B@cS=S-Ist?_t#_wU>O03c^_Er#$s`}2At6RG*kH%iwIIOcLFlAe36?42` zd`H8NV|%w=Ik>{cJos)yU)9OG6WtnTd|p~cdKc)+S^(;2&%4_4jr#TWo~>!v`$I_3 zvoG$iyP*Gc$nt#$mMrTc?^4j2(rjCMyj&wy2`SEwopXM!ZT;kwq%d6%v)^Xy1k%`}eLa8Mr}TBFU}|SM{nDF;MJcVm>CKkZ6<_sg3WvFCvsz!Sm}nk; z;m#rV%4Nqss3XP{gY-=s0@>?ZeiF@xx#8B{Yx# diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup1_128x64_sfw/frame_09.png deleted file mode 100644 index fb1c8bb9042d28b4e0ace883ea20e23cccf7ea52..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1551 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 aR#*bwkfy%NiRRUj|9VblzU`o;xcWa;e@A@) diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate deleted file mode 100644 index 0cfbf0888..000000000 --- a/assets/icons/Animations/Levelup2_128x64_sfw/frame_rate +++ /dev/null @@ -1 +0,0 @@ -2 diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png b/assets/icons/Animations/Levelup_128x64/frame_00.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_00.png rename to assets/icons/Animations/Levelup_128x64/frame_00.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png b/assets/icons/Animations/Levelup_128x64/frame_01.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_01.png rename to assets/icons/Animations/Levelup_128x64/frame_01.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png b/assets/icons/Animations/Levelup_128x64/frame_02.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_02.png rename to assets/icons/Animations/Levelup_128x64/frame_02.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png b/assets/icons/Animations/Levelup_128x64/frame_03.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_03.png rename to assets/icons/Animations/Levelup_128x64/frame_03.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png b/assets/icons/Animations/Levelup_128x64/frame_04.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_04.png rename to assets/icons/Animations/Levelup_128x64/frame_04.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png b/assets/icons/Animations/Levelup_128x64/frame_05.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_05.png rename to assets/icons/Animations/Levelup_128x64/frame_05.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png b/assets/icons/Animations/Levelup_128x64/frame_06.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_06.png rename to assets/icons/Animations/Levelup_128x64/frame_06.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png b/assets/icons/Animations/Levelup_128x64/frame_07.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_07.png rename to assets/icons/Animations/Levelup_128x64/frame_07.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png b/assets/icons/Animations/Levelup_128x64/frame_08.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_08.png rename to assets/icons/Animations/Levelup_128x64/frame_08.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png b/assets/icons/Animations/Levelup_128x64/frame_09.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_09.png rename to assets/icons/Animations/Levelup_128x64/frame_09.png diff --git a/assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png b/assets/icons/Animations/Levelup_128x64/frame_10.png similarity index 100% rename from assets/icons/Animations/Levelup2_128x64_sfw/frame_10.png rename to assets/icons/Animations/Levelup_128x64/frame_10.png diff --git a/assets/icons/Animations/Levelup1_128x64_sfw/frame_rate b/assets/icons/Animations/Levelup_128x64/frame_rate similarity index 100% rename from assets/icons/Animations/Levelup1_128x64_sfw/frame_rate rename to assets/icons/Animations/Levelup_128x64/frame_rate diff --git a/assets/icons/BLE/BLE_Pairing_128x64.png b/assets/icons/BLE/BLE_Pairing_128x64.png index f60598005d41ff05a9f763f42f1a6b7900150e33..34068c300386e0c88e45b40a8995b0a4360ed79f 100644 GIT binary patch literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG delta 2594 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 Ef}^?D!2kdN diff --git a/assets/icons/BLE/BLE_Pairing_128x64_sfw.png b/assets/icons/BLE/BLE_Pairing_128x64_sfw.png deleted file mode 100644 index 34068c300386e0c88e45b40a8995b0a4360ed79f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2307 zcmbVO4Nwzj8csqH!AcR^;-I$60s@0cc0(X!Ar(w8LGgNGFX7@NWA_5Fl{{xFJ*vX=x>q zLB{5ph;@1KiCA7HCda{LuK|%}gd;EzEDD$ndLx6F72qT!kIDckl#cziMcc(UP~}kwh1F* zws30t+O44xrHMdU%9Kb^`k9wXm{A#!NJODP;0Dr&Q#nk~GZzRI$`T6D{%S%~jQ7RKml#bMM2h3XaazGQK41?uiVM2)ro>W(>MKnf+MU5Dt zQ7J&qIUp{ zIp?wE!;2e(vR=`fGE00N(|8`_fMVLvgK}tE0)h zkVk2$NWzcKs9*Egvh>2TrrMjHq;zSzjeDiNPac?k+;Nj{%X|jg9Ay0y5tk@Htw3ATT`ta`*~H% zYe_H9zW>k1Ri}IQZ>);(2=qDpM%n&Sb0+Kq=eaeOtnjIekp77l^*k)AYTAt5>ALjg z+2T)qXky}Q!=soY-Mq&se4l^;{W38Ba{gd^uX_}D|75jW(}jfQPe!BKY)66FoASnv z75Sa(h8vC!EQE!v_vQ0D`5&k|^Q|o}ba=i}w#g-K$dE6u|Ln7SJEgVM(wZdWa7p$o zLE*JoRaK>`t~q_KYQKt&0eyc#I;-i(dpkztJ;~?sUp}dqR@R@pxF*DSw^hGn57pD~ zYLipcf$_+l9cLykomn#1*J^dXe0!kWgFkFr;M5k;)v$W+-x`jMkM_@trF1SyPre>E z9gO-$+zq2k%*zgP5c;N!u^=w_-D`mTR z#mt!ZsvlaOL|fjt6)d>!8+8;|IsB&67oP_=6uq=cyzA);t>r^|)q2}=SGuhweuIeK l-D+9gIS9B;cu3u!2$I*mZhd=eoz4E6qKS!DH7-xx_AmDMVyyrG diff --git a/assets/icons/Dolphin/DolphinCommon_56x48.png b/assets/icons/Dolphin/DolphinCommon_56x48.png index e80fea5bd7f694549da1b45e9f3db056342a95cc..089aaed83507431993a76ca25d32fdd9664c1c84 100644 GIT binary patch literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj delta 3373 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 D!U>8P diff --git a/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png b/assets/icons/Dolphin/DolphinCommon_56x48_sfw.png deleted file mode 100644 index 089aaed83507431993a76ca25d32fdd9664c1c84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1416 zcmaJ>eNYr-7(dh;KXS5&nWVIBjS_NizYg|x=Pr^vz*7zxJO|P-dw2IeZq?gec9-rD zoPZchQ_6}yP{Slc4I!!28K==nodOJ_nsCY-(wOq2uZbLx!rlYU{KIi)_Wj!D_j`WN z^FGgREXdEDF)ewT&1Re7Tj(uBvlG44lnH3;I%IzsO|z`*Vr!`uv?9QOwgs{#Ld+Ki zC9n_zxxBOkx@@+IwMwAaD)#3Ik`}gun2kLe))Crfb7e+#AgzHGCc+X$b>qJuIf`S7 z?8b}I{ghw#z>uiaLknQh@LJUrqHcVYS3v97F^OZN zCe|7^J|?QzUx0Zu17e(=CM1fYFpjtLk|a4~$g}e?hGH0!VoBOT&<=s(1ct%J9~?O} z$)jW_dkX9yTX~%W*i_IM%0{ z7EmP^_pKn`<5>E(SixgJU};7`)7Hidp&+DLnizsebUk}_-GfgbN^il9b`v)f+ z{o5Zry)d<7`fHQ^uw_;+x>mcPw0&8iW69x{k92O{Q}`yFdH=5d$pbf49w1&NS)G+vhr6y}5TMsofQirRDUmKilk5=(KGouJ{H9hW=$X zgi;)vI!jl!_4H3jD(?Jz=8By|i47I&tKA1y9{nfp;_|FxKBDNWp{hN9hJ1nU?z%J6 z?>UxyzWvO}Pgc~rCZ#5%Eq+_hNS~bBdiGlT&f%%e`hHjSySR2=JuK2^+%;$R3#Wz~ z=e_mfqW23bPa0fhe)HdE5+GelU&!jS3ckUZOQ)CC5?mo zo=tzG_4|RuvPUO|mhCwA>y)1c%SWC%a4?a-x|J*?ch~+n=R7o@>p6J2dE=$stKZmK z-xoTRwET2^Wu)&1U7!Ebw!!D?x`xwQX3pMnrRwCT?`4GHt4&?|cIiI{_^XYp-np>6 xE^lPSXzOYCC4X`6tl@OB1M5_S7jml-Y~(TPp{aTIejNKZ`m*!Atyxdk{0EAy49frj diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63.png index 93a7ad79cd95cdfe7789366edd8fadb3355e467c..46f559f65f11194c94c15bb7f195a1d72ef2a295 100644 GIT binary patch literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX literal 5390 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 diff --git a/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png b/assets/icons/Infrared/DolphinReadingSuccess_59x63_sfw.png deleted file mode 100644 index 46f559f65f11194c94c15bb7f195a1d72ef2a295..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1177 zcmeAS@N?(olHy`uVBq!ia0vp^)DSr z1<%~X^wgl##FWaylc_cg49qH-ArU1JzCKpT`MG+DAT@dwxdlMo3=B5*6$OdO*{LN8 zNvY|XdA3ULckfqH$V{%1*XSQL?vFu&J;D8jzb> zlBiITo0C^;Rbi_HHrEQs1_|pcDS(xfWZNo192Makpx~Tel&WB+XP}#GU|^lpi<;HsXMd|v6mX? zCgqow*eU^?3h_g30o>TUVrV!4LrlLSu|VHY&j92nm_lD){7Q3k;i`*Ef>IIg#cFVI zNM%8)eo$(0erZuMFy_*fK~@!5ITxiSmgEfrKz*2sj;DkpMZknD6wv4b%oJ<^ zJ|V9E|NjRvLl0f915!UdT^vIyZe6(^E!Jef<9czm0A z3tiQZTzK79dS605C-KUauQq03?HktYO@Gah6}dWL!K=tEQF6i?wpZ6>D42{4Kw06Cgbx4xeweJDDGS}F({`j)7X?o}J zyX`DecdFHnf1Yl!OLgg{#J>1HY(M3>ay__X{YpODaR^Nc-<&V5lUuWQxzBn#x3aK( p%=f=fPd;t;X_5T?htnCD8BU8cnRk73T?7mS22WQ%mvv4FO#oZ0m5TrX diff --git a/assets/icons/Interface/SmallArrowDown_4x7.png b/assets/icons/Interface/SmallArrowDown_4x7.png deleted file mode 100644 index 5c5252b167d2f9f9a1ce5e7b9f9c99123879c1b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8340 zcmeHLc{tSV*B@j}*+Z$ulu*Vj3}!5av6FpYGG+!7W5&$LlAY{(sZ@5MMY3g!NS4UH zL=+)JkrYCChkBmh^IX5@_rC9QUGMwfGuQQ<@Ap3UIiLHS&pGG*F40D3wf1owvM_TlNP3M`sZmv}VuF%3 zx_1QJEUX^*RNcKdM;LI@i?{XYD^ImhobETB&8Ve)y!}%3*#7W{*J0GnF%3yq|43O>u2&c9J{f;uvQF zm}2B$++)y^_A-fAnA##dx$ki(`%T=G^BycGXTGq=GKbG+@>L>n6HFg|)CWYcC9ZP=7w$vYM=1b>F0x>cUS2a^P&u@o^Df$dCR_7K z@`ve~bAhULUK2?~{1b_8W32@*L`t7-wK+ zMN}Nf4Gmm2w$F6vQyV_Lnw+jta?VO;Dynm3__I}EN>aL1v+a7>kpc5}DS7uol5^!# zRX?y~Fe?SR_q>)0#2M9G5x#8V+J;c5m|@`8b}`CHp25YSMCWYc5pOhz<@0<;f(Om| z6774U_b9TOv`kj=$z7J_4T_-m|nlCz7`iJb3e4m;<`|~ zuNH%R(vmR5g)+;#@H*)UnU#SseIpss|IBhaLLZeJk2f1y+{CdadbGV2MvOMJOH?is z8(fZ?PPEl|NM79TF1k2pb+@GBjfyDx>%gO+ijR!s1x#tk36txdtl!m6@|m}(Nl>xZ zgD_JEI%c>60t>&R}2jD|&U=K`&H{UWs==oe4)Kd>nSUZs}cQ8mIB3&+EiwhH_IE9 zGGMdH*TXg$%u8Xyd5n4Gp@Wwt)`cF*=3Ou)sY??XKFox>CfnxrAQa5X!unHCU-qJ< z@EoTMvFvGvBoaIwf|UT5Wq(B7Wp8$c)X5aQ2dv^8GLBuu`VW zzMp)Bn%S1N)>1&0$_%7j^|zf2eE$39j#qU#5ES8J&jNjxS26d`azKv!IK@TVZpWXq zQ?P<@B&VyrZGRWyWq+G-&v>>m-x*#cUQNmC;G0x%z|%x}TU{bM6RzS?FF7MKg| zOBn7k4F(w>4JeE(Vu>Bp;u?NuyywfrCl!K@<;19=uT?~+!J#vWPEHFP4Ibt4^h_22Hg^n2~C^sC-xrXvR;yX)lZj8eE` zuJpLMnlNhbtJ6-FJ_%#z)@?`vfaK6-WVCT|J{w$OA~;t)xdaq*iNUyJVUI9+z>~~> z=&BB`NO=6Q?^5T@@uO>jZkcu6uU(b`hH3ywTVc#k^{v>0IkT4T1rTuE))OULUa& z7JROtY*JCgg#~BX)~n(5C$M2Oorp6nr0Ei2|Fd!ciIg$v8LBDH9gIzpfQN#od;t^M&(D48 zgIK-j=Ih{U?s{P(&3n8cO8A7GX+->GzSy_j-3GI<%`c$)<>QY{!iLj?twr8a0M6Z8 z*8ILi;Zl&|umVy6N(2+@B?f}*w{E7aUAXEH9<(ToKDyqtY8jZe5AZM_Uoo-~NhAmW z>=zh}YQINzIzj+3rS)F>13Zt^_&b<9o0M`nb#H9UPLCneO%gJ^{-ag zw(&(Vr>RApS0Ka^s9BAo~u@jw?z-02|NOjILQNKzlb~4vlOtmMT_ZM zXKZiU_4Li|&>sPoRr){L4jd0kz-po4RlLSsA8u^fFvoSjGXUJ`E}BjL=eq$7Dpt7Ej2 z3)*b?Su`|YM*3IwwjUL^G?{ZX+ag;o_pULkQF@`ufcb#Ld5*6<2AXa;oxNRi0Gbp^ zI6YQc(=zL3&*8bUIi4>H8v-n0+1NN(f>4mY$O(Zm#xmiuL;VTJOTsO2nEVo}`=}w2 zg_wQKl!s34utyvhxi9iwWL8WF2s<`OBR^6&XQy;F>eMsy2^+FF*^f|1Y zPy80A1|(=F?Tmf$qbz03zL?qFzg}#W?SuBzUcSTY@LW5(=$Wz2ZL?gO+53pTa=7`3 zCejS4?psEQ{mlGMi(jfaYEu2Jag4;Kgo^~$Ec~t^-n4qaYd~|}dR}3lx=cQEj|t&+ zImtDwTv8w=CSA8&H$ykR-BUpFaQ2~Irn&cD;2W3?uMI_Wu(_dm{rrhRxxrI+iw9>4 zeG8Wdbq8JzY7V@)BQp>%&_7Ul$NSEB{~5w{Lb%)X`47Dc_m*kpck+Wk% z;fPMzW3ra6i~S-!w}$$LKKHlx-R(o=el@%K;A3|B-8%Kn@YTHi=B)-=S!L?8d0p5D zj;71WU4^(WuzVWA>${g&&DWZ+8X3P9KV!d3ejokTzOZjdeLw#F@J8K+DGQ24l!ck4 zh^2Dha)eY9f0KR0_lV;7xOk^HIUz4$Ww?dRerT7x2cq6hP0mx^+PTk>(#el)ha1Se zCi;zV%9Y9M!S2Bh!6Q2lzhIuJ?TiHX95{PGEtVzrkUnq!<+LVue1c6MI1IcOTq~7n zEo{|nODvnO+;An5(mh_g2DC;v@wuA1_G4G>6{Ue+K5AKgW%t=`y!zhkh3)_k7mwD; zQLJ}=#`&X8Pa5lA{s;@p4QtEp#Pszxzub!`_*e%%K_Ta2USJru$c-wm?TnyV};tJr((7N0j zUU0|d?yPFJi^GrusCa+zOLv!5-Bn4_v(fI_XBT@SC;8r)ex#0DcFT`c4tlqDzad>3 zmkcToO4^p&KEzYyrDXlJNKqp~Pj+p%pmsdi=G?A}@L zOQAi%Js-2zp2js@y%caXft_n_F?)~@hR>M0_epS#gHsD;D*##HCd@qC0pU`253sdH&$9| z$m&-8`Z{m3*ex+F-ri_fG&p(*nSG0;>q}qo?FzkuD)&X{MZOGH5-TZXsU0$Fc)a>V zs<^W51%o#kN@8weZ#!x?(KVimJ7wwHp=xaq|T6v@Ct%k zkVLt(6vV$;+S%SP*`oJGPRZB61>0DG7>#CmxhnU<(WqAv!wOY|#r(SlNA-Nf1oeva zPU()5W<2WGQ#&nF&jq|nDaSv2k?r1X@xtQm(8B0g2Ao;(IcgGR939k^Mq@P z*FHQp!dMzt#y-?5)w2<{8?nb5RaDFec8pQ2*-|_j{y*%4-I#}irhTD zdAm2r!PGvnj|mG$R1W%-SGL|Rn7Y`pw05*#*@9~K&S!-EX><->=GmFdTPmqu^Y7nJ z_haky{FZJ-D^rUgk4c~0dq!4f{kzlOuYEY}`{wIb$=)u(1P7e`hni{f9cn{h))edJ zQ54Hz7R5-B$Qp>E{i1M&y#Hj(W@^7>U2#V30rJPbwyos13&t-=Z?vmK>z>zso3q#` znysXIbdOn61p_LlGy3XcHgBvQ3Y7g`Vm}X5R+(2ueR6CJQ^byyh@Zx9e+hg%C;AQ3 z9NvXqIX0@*dLlp^a@t|tXG>@3a#}(2=~LTwn=htEyr1}N!Jz_*h#z)ew)%^2o-2GZ zyxq5mU!o0v`EDs(?Cfyuv2hS}0=z)nuD$NfCsVytvUKY7^}xArLSL2_3*rnj1DV#s zS2mGZqtyVwehmWslZ};uJ_<`BN@H*&C%m)|kxc)j0{|$U_90`iZg?8d3GYJiPzEj4 zJ^=v!*%wEZY}Q@^ujSU)!`5(hf1!lC4Yq5}}|Gz`#(=S zaZ&W^9Wew1+*P5uDTAyGjDYGS3LYpcEh`NLYxoc@z(Fb;KqU&!8D)aj`~^WDDT7>T zG%^YT@%HwX_Lh+*QCuJ}BoYaM!Xa=tn63e)`g+hXK41^(u^ot?7-&2dOCgYH1d<1E z2NUB&@}en&K=gUwZ~hU<1_r;wdr*I|K<5MEgCRp;(ohJI2>GK0m8Nll4)V*PzqFv5 z(Z75_Oz>2a7X^#gxPbSd9s4tc3I1O({VyQs>VE>jVSjgk>_u_k;N9^=`dU=F z+ps_RtfgaM^t;6lhb{yndDn`btv_kf2+se(vz?h;KK>X9-TZglzl>v7nXYAEfI^e7 zUOVpTpp`*%i8PLYLm_0*YBErmf|@)`6$aB#Q&oehA~ob?Rn_5g2&k<5AIx<;s5FcR z7XO<$Vb@#%CWnwif)VoaNU$s%h5%!*P#D-526e(f5zbI&1=t@T3@HS9DP!FK$`e^A z5-y98$AX1RMcI%Hw6h7>o?kNgfN6!^k-8LgBC|O%jEOq1OO`h;hL~$Q~}c z6FVfMRE>0$L2zm4za&QP7@9NPKpCV@@bL2a*Mu2?h&QESb{K@o$;rZ@2$&pH9wCqT z74|Nk1)f5sm+uZH3@R-H-!J&VNMxvOJNbbs@9SGo# zj2UPl* zW^DqMzP|6S=w}<6;yr(keh%FUyZr|Q?sgIigZ&u-6>|ZP+x3%<^>YgAit%v4)3=;o zCHkA3@E4_kgu`WU3eGq%9P0!H%R+IoU?*9;JQ#yR(sL<`#LD7+lJq+|mE=tG#!&F8 zE_5F0T+v%%mn)#eZlWar?2ET6ekTib!oW}@_}>YG{0bPdv(fw(u@dCpIr&8s8c!k6 z1Ja(yrrw*fHtx{FKpmV*17o z`F&^ql}kF0|DV5KHS+(t1Q7VoB7ckDf9U#$uD`{=-%|dky8fZ-Z!z$e!&|1-Kc z{`GE%_n<$WdDGwgw!a$r+Y^Giobch9crB6t?+DnrSiS^G==$;`2~TTgKE-+ zYxCiOV$@(bO@`NwXVS@D>;31fZpKbRj|(}GEm}9&ett=~?v8k4+^ol3X-m+1SV<$i?3^J= zV29^bZFiW$)|?7JxM3@@+*~{oFT`}%g4BC!oZ5RApKwV=*7ieJ*;W}?XwhkCzY3{z zUn#>a>rEZxft=gbj)`(x9|Io7r?cR|eDS@J+aiTqi%+_{Dr;+^JFY+YbWw1yMQ5K} z)0Ogb2N^*X56mYoewqwaYZp#nz>Rry4LPnZq`4-&sgV+KZLE%7!|t0MYKG;8HI4Da zi8Jg!k%Tpvx@HseuKi%>@zgHfY6gbbBo@JE8r8gVac$t)< zkA(_o?hipaQ}5`+h_|NQY!x;9Ro_exE_EH5UUEi&qfo)G)} zwL*r+Z9R|Ow(PLM)l9t4aL_(IDGjL@GV4#ZBUOf;35p1L+WHraeC% zLkcrc=s@*bC@;&E%TvkE*km76(B7Z14ANS-xjDYg;6^i@Oh`%SS03oO@6ULc2JcLa z6y}~(SGQ+ixYXmG_SHzdMBF7!gTgJ4RNC%3bP&-T>XK@SK#QGDE~R=HR?`I!8cqw+z(lHY?f)`n*cf$)U{uCPGY z|Ev3ENf1zLNISFkzMX4*c<%*}g2tWnKq}UIz6={5^gD^is?8FeJpufOLtSd1Mqmk%{)~xn zx5Y!%PMO*VJFaa{AXzF(xehrzs0ibCm{xahReXgt%a-UmuxQ5`c`U~krmirb;`Wea z^o7Ivd4`^DQ01+B*QN9B1Ge+z_{cuS#PbTNoMZztt9$r1VGEc3o2ex=ENgO6+{Hy( zO&PE2yQ&qQw|=}*^wmc?k;%t+$V^{Q+*u~VM?CIB`Y-v*&7Ec6q;HJ=g6~Kvado2( zS&t=SNCv>2@sPIrC(`U4#r%q;(qlv6qTb*;?kHgkI_m52Uy9b$`EEw&bzpu81$Hoh z>{hkEcOYR{Iv)@&8^AjBE>$pYsHyZyhB!0RH-V<yYh}0A+|F+wh-EEYIwrn$NYYC-q-`LlkxI;q zzotU$U=19Joh}VF5n{5noHd+?zQU>P;o^MQw?h1tCpbk!$-wLr6UjR5d#ao0_q-{_ z8_CJQ%8}qC8>4{|M_;CE0b!zgGer^-xrsS^;Uc1-1aln$&7xB&*>iQ(nk`WWp_MC@ zDoMO7Usl#q{XUP6%v+Q z7g|!U;g$wPJC4{@d4z*kJxDS~UMXWGuNsMPi#R_&%70GRk+)gLh{}9bVk|i;j}*oy zt1#sv(5c2jrB2x8zt5E2`53&u?t7$qjQy;-bW|yrRMMSZsVGx%DU9PSCwS7pmM1R3 zTT-KOsqx%XAGI&Gkf57p`rxM>_0LwWc(iKwY-yQ65BtQ7JS_81t~ru%Cqw0io8$)0 zGxET}fL@0p;89EFjFuaBid&-U@f@e$6x9S1cV3Q^u9jAR7fT7=QC{g6y3_`CTn+)$ zt)kaiX2Qb&05&*DOUp!GOY667mcF@W-AMJZFdr6->aJI=H@V6ko6>XM)oh<0N4;K> z{Bbxdx51Mu0FV;af{HawDmZ`uzYERRN-71#US=>Yo!>8v9k@W{7fIE3E)stG$Zx4L z^R3u=@cEqj?$<6$L4&n`E8F2rPmQcuLl5Rz^N&xrL?^_#II=Jt`~+Zh2fU+}mum`c z=1CI2R3RD z4bu2M zR-RX~Fj`pJ-aIlPnJ;dm~zTIT}0%^F@Wy~p`3C!?;?;96&u zJ&67V*yi@G3F_B_A9A8L#8|$29~I=;mTZWIrf6ed6c_=GQY-D+`J$Q9H6ztGaxyl! zn!eu}u1Sp<+2H9(wcdAI{IGiPQ2_Wj50Ev$S+-{>h`m*p@oQ&XZ+hB|t?sbzK~~j9 z-`fQxLlW`2SVT3iY1hY$ZQFfFAV6E4aZX)ihLP{d{)bO^ssUW@824#_qxWBMgue^p zzruh83h{?l<{nB3D{_>%!|a{QgA7-q9->{szeA!}D>X27(=wEzyS zs$5f7-c13276}+@L8EO6k2_Pj<7w1^#0a6rXP*!#G5N5nC)cQqf!vD`r<<<7ml$!^0GY-Zh_*6$T$ybtLXW<-V&>c)k`XDx8rBl)`puHrLaF2 z6SzEaGd0gLPbvSNDT~RCJ0}Lt41nF(R(Xtd&fo0p?V175&%Cpx%22JSeAU8_c;9d@#dEbRe|qtmu%Q#k6@1`i zVvacdo{sfFgr4Sc93<~*j}FSP+R#@+l6N)wR5SHvXmzBuzA9Grw5U{rl&Kz3Qt8+w zNpZ6Hg3JxjYfwEf`I(M0m#3TeAFGLr!uA-x+#|9%5JCPDdE7!$-(Vncq zzQHg3&-?E6Ve(fkG9N7D-MCk;wH2{;i}OsIv2Jd;*7U6|d?Z^_a#GhF=M{JXP0{nCY86a}jeD$LC17CjKUe$ZwIw z3GoR|@k)ohh1C$23Y@SmWe>%M^O{N*lx>K8j-H+T_~!^?h1X>N;e$%$%7*a!a1lgQ z=aCMk4|SbUkRHKPf|_y6aUw>%{mJP~?u10!K1eu(9a1NkV5JV#9xfhjRik*H{%kifr^k&AufB(e z=ZClFb>jMZpTK)d2R}2%wX)&K#2Y@EDt_pqizV_4q9*v>n=g31wdz(Lu6pV7y!#F1^4nt}6(Lu4ly*cIY#GKv zKZM>@Z+mj>Qjo^2Tqcc|8eCzWVcLyq;#57y_O&Z@%N*0}(>|BOdO~{^^46cmH>F+< z5=&&|VlQS5IfUag6y;3*__N%*;R6=G&IiriU+b zvT8jW+aJx>?A?Z+%%9AhC=}Y18>^wJHlD~sjce6%Q^l8Z&>1#4-1{aOrWUHo6WSb>@ zv3MeA!Jb{XSvYvR;S{^^XwKR(HR*aa#_LIG7cBI63Rt%gl?uyrq0QK`vxtnzRP*FzX=Tj1Ubbg8jN(``{Kfc%%$K0$$7e6u zHahg$<=T}zbfn#$e!h98=icr6{#NtF^0sOXt(4A7l@XPs^C930c{ylcjXcrebdqIn z#?8{z;CZ_^fmrPpWuit{ZUmTw{zIf{!(*L^E z{XV6z>iW@rQIb(dLozoOR+}`U0)8n;tO#=1a3Hf(=HKF*vwwqrd96=sA#wib3Ht4C zRom^t$$%G2>tg+CmR|PneTP|}#@yt~IrSkY>%=whx!KvNetf;5|58?rnpY9@5#^J6 z&+zheVE2vL^^Yh0-mGqyvUdq3o^^KkSUW|%>-8i!camjG48wecStUv|surr^uqd3Z z95@lXb*02Z$(S}yOSqT>t&t89I z8sha1GEd&AOY`AVs97pql0BIgJoEL?%IC$x_|rMTjO!80Tj<=;8UTO`OrpP|u{Jis z;3;HzoHNCVAn!}2(%<0#0IDZ_sW|+30uAUya3Oi9ftKnXgMcJwHIS8}G0d2%MQ|nQ z`Fj$~{ZCoo{m+POpsJ@c5o3nc`3XU9sexQ+G%5xP_3`nM_fe3ic)CF0 zXfzrMLqHJ-2wel><>x`e`9eIrBz7TwU|~iQr zB2)LQ=-K*g*@em>or2s)H zA`~HLWdag{!zrMhl<{ySoPyIHlrtWqL-8cz=ruqh<6H<(s)x&7$1cej4HJDe5JDdI zhs4AkM%(I_SKA4--4PcM4;?qb4W z@(PH(p50Vn=)us*#O)S09bivRXBDI6Nx;!4o)#2}yBcWMCE%{*@7>1qIp~a|;jlOw zfes2oATTgA2BBmDM`7Sd3>+p4Q^3Ifq)%}s5&iyO+PgCjsQM$#dL%FU__-t&`=^`i^#it}(G(3hN_CHkA3^f#q|Mj#ZN zQAB450`CNaAYsl(h!c{a48b|0>AWJ*c%<_Wl72_`q7Z35I8TCx3!O(gSM({d#}yE~ zm#AZZ^~J}Pu$u)sVGtM^@{=$G2KgId(EomjP6!1UkzU#mWqOPdBwPszLBkb^5EK!K zz$v3pC?^!^uTK9jg!reL_!*)qbayTNEvc%||1<4BD*;RJB++xK<4Ga!W!#HKU&k=c z|9Kfu`oAs%^rG0c|GVV>Tt*cCqy3)iKOFzlra({G4;g*4q3>PL-#4$HxuloMfAjZq z8vHkh00RFVhfPHbFatXX(7Z9MYeG2&BLDyZ diff --git a/assets/icons/NFC/ArrowC_1_36x36.png b/assets/icons/NFC/ArrowC_1_36x36.png deleted file mode 100644 index 3a0c6dd0cb2b7cb6eebb1507fa68a8dbda11b146..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3692 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 diff --git a/assets/icons/NFC/Detailed_chip_17x13.png b/assets/icons/NFC/Detailed_chip_17x13.png deleted file mode 100644 index 9aaa1c5552a2d115b7042a4bee7a0c636ed00f01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 981 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 diff --git a/assets/icons/NFC/NFC_dolphin_emulation_47x61.png b/assets/icons/NFC/NFC_dolphin_emulation_47x61.png index e85b50f26f8bfc0a66213d58247c8dd1bae0bf0f..1783531285bed514517fc0821501e718359dd765 100644 GIT binary patch literal 1541 zcmaJ>eM}Q)7{3ZaLqs>76Ex1V5{uLJ`hl&zQLyxiR4J5Flue0VuU9zXKDZvVFu$in zjZPV|k*WB>lBo=wab{s;0v4vrHgq6yL6|@s=rj(*ZALe`w+OgD#xD2qyib0=-}CW4 z_wKW%tO^gC8wNp8xH$>4fiD6cy*LQG!v-3sLy!oej7F>3XoNYQby0aF1bI7aN-wPT zSzjw@m}hD^wN~8M!%5Suc^yp$% z;LAJvZZ<7U;$}9n&swkday_9{j$6k|(n<`UWz^qKx;6aE!&&NN(Msv}w)zef+FQ1w zPdX)Tbt_@>_j_ z1g4yN$-3nhg@rScIaE?HPo@{A*oop?Lg$pk$HB2)6bR6yfWuxok8z`3y<}7u1$MxV zNZ?V3kgJ!xNGj7}g^esv!dkgfMko{tSgVthPF&?syKrI|tWv0yh!WgdnNo|Y)TmMi zk6jWFkaxOJ8D8>lEl2;>9^cDOUul8V6b|{|}|<3A9_V zkuUT8Phq#ch$9gj>1GRf0_>e@Q6LnD8hH{ISl-UEdE_v6wo9ijB}kYxrRx(fq|eo5E&zRs*rRh@+=LHR*h1V=c1idZ;b1lJeL)dauJXW z64={+?e(||3{b$F7+$cL7=MxhGtYzJp6B{075o?>)?~ZM@Am^U<4XHBa7ryUV+Omo z^NSB}*9I*Vo3i}=cBw9McHOY`cJjO|@#%;8vhG*t42yEl!M7sbnHFlhs_u%cu{BLw zhaGH~)>1i`aAdfxzb-yDYp8axDf-aUrDb=dji35@J+L(5s-gM3W`<^_YQ(SZsXOZ| z+MW^|z2V5B)@@rgjw-Cq(dw%_ar$zq^bB!0SN!Wu`o1~8yPWDctj)SpoN(-94Cmev zzx{^qT>j%Hhc_onj|mkQqEx+SO=({jC(e}(-Mu#TV*2AZM;fP!-l&6{t3IlprLTNH zu7%|!`|gA}!?~;;lRx5Kde1<}Db&?0+p;8mU68o-2TkKA6-h@we_Yd@SR^HGPk zTw<~x)L!ghDi?=TUtd5z8164@t6%c>#|vtn#`%LIpusI7pJ(MpaIQm;*_49SCT!aE E4{AO|uK)l5 literal 4224 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 E4{AO|uK)l5 diff --git a/assets/icons/NFC/Tap_reader_36x38.png b/assets/icons/NFC/Tap_reader_36x38.png deleted file mode 100644 index 4e0ba8f05921ccbfbaa4390ad07e98fd454df00a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3748 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 delta 809 zcmV+^1J?ZR1=I$RIDZ2MNkl8%vq7p(O{Y^mj-MMz&IRWW8dlklb>M}APO&ha6JbE))xS3fLx{D2mT$PcjCsp6u75dCa(gJ zg$7PRSPT#B04M+ofC8WZC;$q80-yjW00cn+PyqIVdff}UEd^jN2tW-$4FI43C;)&0 zpa1|0fC2z00DlSqpa3WUfC8WZ01AKt04M+o=ok0&h4Aeu;6Acq%a`(~0jL3}0Z;%G z@Nf=r@h+O=aF-1JajVbpY;e=0xBM_mlE#+d@(I zXy`|>Bpb)H<`DiDYv&AilG_Q`jQyv4b+;EGjywBwI@!=Wze7mT)a63NR-^sKE^VoI z)R?ikoq$ccROX3yh8r`V&u!>kr%92RWrT-uFLDyYA=T)9ZL^C4M!sP{{#ip~od{G5 z!KOYGM1SiRGrZ}8zEHd`^&M`hr8p^-6^ch+P4@%l#%Nb* z_9w1klb8`Z29rAYwoV*V_sU}`x$^;k2=>c}On+r(1iqr(p}lcf8 z`1(t)=;A?jh;Tj}4!hm%BL%xZ_zo+ZoQUJ`xZm%$+imK*R977gDhrX7tlO~0!6c@x noFc(1Z$gauzl;~WM{(gdOXY*sI7{g=00000NkvXXu0mjfG$wR{ diff --git a/assets/icons/Passport/passport_DB_sfw.png b/assets/icons/Passport/passport_DB_sfw.png deleted file mode 100644 index 69b2ac9ad4568b6f1cdc9d38f2bedb6e5d71de6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 750 zcmVpHph-kQRCwC$T+5QeAQ1GhYL)A4uO4&snE(GU*N|mHiNMSd0(qFE z`(PmjgiO;j1C|`x4u=k=;C=#a8nOrU3@w?y-76AbKix{SWoYQ^C9rJ{#u5tUV6<_8 zJPts>!W_h`3<73yZV}%EGtI$)yV!$J3>$%feGmW_v^)U_!k@UwfQ>*v=8?-Pj+ngT z2l$+2)>F$Jg~A0bNdN!^Kmh<000jV002BZ~0Z;${1$Z;YSrC92fEWNk0Z;${1wa7+ z6aWPPPyiGFY+3Yxqo6!F~AV8iy!4b zb0@qb0-yjWVBZ9MdfKp60@m_3)t?S2FL}LQ&*$@32VMB~p=(a3)A4vT1r1$TG7DPu zzlDDZjMuVQ!9IYw&TQ1H)>JZ5IzBrzzfUy~t?;a%Y`)S4`l0TW+{Qt|W>)~MXDrNG zypRUgj(IsKeM$G_sX1*$BO~ZU3vAiexK_5GZX0PFkpNZ!ZPv{xY!|v@9*>8aI-Rkk zfCvHa_OIn;7hsPj$ij>$fF+F1hpstx{T+JqADSi&i0?iJI}#SgC=5#5n;eI8=xRjqeh*H(WnV(3>rTeLEMM=cDI{+SZLBd@B8%m zJ^-dcM0Sb0$$m zB^snkxjdfo0*Ff#O9F;ra7m_2-OzzyJb-fy$I^^IvjWL53NI^yfX_b!3Ky7`QqVH< z!O%@5%2DJiG+nJ$sVYx-!2r$5vP?4^&2c2MAj7&F={3>~+nOFU7=pm|BinQF*rRTE zLy<}#s*M~RKbKo@?1uA|LN%jnx=*tdLpx5K*qn7372y9g7PStGbsy3N7)XT~bQqGwBT1-Na_yFw$KBo3U*admfv- zDOO1ZP>;Nz=y`+9G02y_$P3G!k8?c5;P>ZrV7swqecYu+(i#lxozTn#o`cVoS+N(P zkT9WP2&maIXK|z;h=7!3QzRub0WBseaJ+@mZ{W=v%Ga1vq(N!;O*RT(B}+_z1gW#o zB3W7B$)o`wsY{HQv_#1ic!r=6MLM3G8z!l#LgN`97Zw9u7FG+oXrBVKIM1~0I)IjL zX?{~NJv%##jt`&v@^K`-&T3u7+P7a#Z(MnvS+?Z!v6=oex#GtcZl8pQavRh;cARaU zI(x2X_xm5cz2&LHJxiv3;wNsK`n$zCjlKNnsh?L{|F%AHdF15UBQLg3UwCc0Z~yVj zTVH5fdGFdc@49;W%E8gFzW2Lw{-4pAPuIWm#)+-_Z|NWZ{`cYu?ckx^cXx~(*?aW$ zefPch;k7-|^$kCSoV@DCcVB((@YBcAOW#uW?F3WnX72oL_>r0=_Zu{cW z(N{Wt`KI)TYi_&#m^FIfOn>Fp&Nbr~N7jv>zjkrgRrRk`+p*18S}Gm&PbN`YfKzf6rOfXRcytG#ws-q!wyIYd^)Aa|e~?ZxbMHO( zobP<+HBWaY+c(tQQG+1JhWO@43a(!GQrCIlclUh%18}+Bjb_{~Gw0?d8z3RolmQyo zr5=z1l3E=68Uzr;Gp?mGZYI$oDyHt0$~xYHZb54V3A7e0N$CeJDuW))2x5z~pJ1q_ z2C;4~K_;v)=+!ol*r019nN~*n6+y*X??VFx5d!GImC%AdU^rqSh%MoYa93L+BC!H&ILn!WIU@>^MNnRn(Dia)OWKZ`0{_!kRoh7yEkLAzV-DF0 zEJ&`gY7CQibw_1I$VPn7)?ihnfrzOL>A-N~kstYg#DHcuBM=At{3xdkwyy^ov($CUN4u)T`SFcE4rB9&*hGA9N zhziB$IG^IfB?{zlN?;k>FDn3-c#`wyI9_EL5+fi*qTD%GbW&9W+q1k~84P$>87*MI zd9vZ)@P{U!fJ3*gvm+fXl}d2?(A=r*2(o5lJQ7M5zJ2AT_}b6V^`7a{=!f+aKwW#{ zK#*+ubL{$0C$8n$k^m}!+ z4aqGxKUFwYO9kHiYvbahk39Cg+BCCo%lQ|M%n6$g@0tAQ^Oob^{JwL>IUGgu*GwPc zUM;p;E48 fV|H=hUc`f(xyf_n$$93k^1mt`O-5$gvSa@Nx4y<4 diff --git a/assets/icons/Passport/passport_bad2_46x49_sfw.png b/assets/icons/Passport/passport_bad_46x49.png similarity index 100% rename from assets/icons/Passport/passport_bad2_46x49_sfw.png rename to assets/icons/Passport/passport_bad_46x49.png diff --git a/assets/icons/Passport/passport_happy1_46x49_sfw.png b/assets/icons/Passport/passport_happy1_46x49_sfw.png deleted file mode 100644 index 56ea000cd03bf445ff64ac2a9c01318a4ed280e3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1296 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 MT&7n)()IlKe@XVQIsgCw diff --git a/assets/icons/Passport/passport_okay3_46x49_sfw.png b/assets/icons/Passport/passport_okay3_46x49_sfw.png deleted file mode 100644 index e65da5b0e586ab706b40263b637f0fd3647ac5d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1304 zcmaJ=eQ4Zd7*FY#?Xc({wr<5?O#hgI`A#mEaJRF)-1W}xYPsFqxo)2Mc+-pRC25j+ z*P{+~I(14dBFvRBN2hbJDTpFM)qUugbz-}TilX~M+(1Ul>R)5DzSs8Z{wND1@AE!+ ze$VrJzFzO@Y;Rc8yaqv#hGarc!;^q7T~`agZ(r^0fX6yNmi0H=hF?@%fVAqi3eaRh z=>ch==!5+eAcP>bFPoXHpG|F&G`m136&s>da3C5%LXncAXuZHkRnTKvVQhN*IEI>f z7~9IHsFV{0c{5RV!RB&jMl1JfqK-v2qM?!m9Tb4Cprt~eP5w%}KSaz#yI=z@vg z8^#t%Wm8>f)OGRp)0DQsp_LBLoqZ-aU{**6eY2u#Bu1-gTZjFZb)f4 zz7!1J!dTw-9f>51#UfE;3ES-;YGU z4KZ4(U|7662dWrCHqsNW3A>{7$+k970G6MW!x;QS=%y|i6i|6y)o~Rt0?x6bhATY9 zCt(kQeA6&js6BX*w1Sa(rB7TZJgk#$$1&t%^-rp08LMsKz#7 z>?JIBN30Zef#Tb4#lx?BI(#Kl}VE zr>*Xj7oPuOCYk$s)Y#tsK*%~np8RyS?!L1%zI<}}!MWYx!_m5O^Oebw6XCJ(_ObK- zTzE2ZDn{m<55rd*dxnMr?}ze}_QjcOWc_;&d9i~N9qGpsZ_J!4w`@NTS~`yv?;ke? zhv>V?2d>v2-`~IQ&nd6F)duN|MblK>4PVZ`*vyPb!yT5yrS%35K zA7@kco=YdXx2-A%Zb=_}`+d*RzhdRBuUsVFjp#>D|NZ4t)}y$rkd*-G80G55pQ2+n{ diff --git a/assets/icons/Passport/passport_okay2_46x49_sfw.png b/assets/icons/Passport/passport_okay_46x49.png similarity index 100% rename from assets/icons/Passport/passport_okay2_46x49_sfw.png rename to assets/icons/Passport/passport_okay_46x49.png diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61.png b/assets/icons/RFID/RFIDDolphinReceive_97x61.png index 2528ebc95d79335975511a384c70c010d476a8c0..e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083 100644 GIT binary patch literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ literal 4862 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 diff --git a/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinReceive_97x61_sfw.png deleted file mode 100644 index e1f5f9f8017b49ab1ea78a9394ed8ea7684e3083..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1421 zcmaJ>eQXnD7{Agv#{x2p_yIvKhl^r%z3;AfTbW%yMuEcUiZn{X?&IxtS=&4BZnPU= zlx>i)WI>$aQvw7<5XB`bIuRj@1WD9@Lr2D(5=YPwGc*zsSTf&kEAj{7lDqeL-}m`F zpTFm}Rj;U;Sva>4L6DijCB86RMfkc4?C{(9_MQ1~dCu}jtr{(6r9=ZD9z~M?8cc|F zAPhvM>5U7Z96{_EH4?R=q2+?CB^+W_$B|Cx5RD+^6=_|R8-RsMpiWJ?vC&g!FjQ6C z*cvWGhIB8eSC=#!pr(06L~d@7c?GLjjFzVbXdnSB5ltuJNmEF>u?f2Zl(WYKhEAwh z4Q^~QsA#Af^=bw{oemP0Nz#dy@(x9mL|KwbP@1GEf@BGb#Ys|Nc!6cnsRx7Z3?(Ln zeSs-waOcMAElU>&B9%%xQj9}0>IjPGd4i+~n#Q39ZZ;(?F^wn9g*gj8V9JK7TdI~s zvlc~3YqZ=L40SSxgdPgrH=H!5Dg|psq(z;e93+uQWD}dvHmxxDKa7WJn~^3R5Mf|y zjfM;x5?h!9!{R;KQC1N~Bdj!3*cCDE)8xhkNLoRk8-q6vMO6faWjLq8D>(0>TsY@s zOL2+gT{ut5lFP+&G;q>6I}gJ}uK`3$Ga{N6&(WZ|Ub8f_Uei&p7kz1snpCuuxhUJA$%K8tP}c(` zU}y<+qQrvwF!u}>V`HR<(=n36VBt1B^`_fx&WPzU_AMY;oo7=&mIg2tu^u1f5 z)@y#lGg2HF{icooYxXeey6HJl+%===Q-Yg*f$J(< z+gbGCvVprluc__jmS6m=F>l7JjJ;Cb^sMdho~B4w{1|(u#k_H5R;4;`zs)u0gC*%S zI_>C5rsHbY>U}-r=8b&^Mh7zat>Eaqs$E;p%^t}^&M*C`d_!V*2g<#^ZLQq9;N6x= zv^)OzpYh#+OwHKfQ+kHHZreNi()*6Nw&PX5?kxF@U2EB*+}LH?toC1`{oRjksXb78 zx8u;V!Qv~6!ySjp4u16f-y8F;3}d=*b!=ao^)Gw)nS({6qa!CbyuwrWMvi?_zz4rL rb-KI#{JuTj%qEZPotyLfwj*}ruaRky;O7Gyvp>k7e}(TvWo_$!Vg&g_ diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61.png b/assets/icons/RFID/RFIDDolphinSend_97x61.png index fef503263fd962534e7e5543954612bf6c1faefd..380a970d9004cba5520560fd9aa24aa42924e2a1 100644 GIT binary patch literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ literal 4882 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 diff --git a/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png b/assets/icons/RFID/RFIDDolphinSend_97x61_sfw.png deleted file mode 100644 index 380a970d9004cba5520560fd9aa24aa42924e2a1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1418 zcmaJ>eQXnD7{6`EHeeAZ8ii;s2g4C}y=!}S>mBQswzrLLl$EYRfoyOe?`_T2-g$Sk z9pb2CPQ(a0XM$+dKhO~O0nx;1L}kUOGdh8Ve-^?LG)CD7lOfXp&bQl&{IPJ!-TS=n z`~05I-*YefH&^B@S+xW~kUZ~3J^)t%zRsL1_&wM?{Wx46Gs{C}t*V$YK?jISRz-k% zBSHfR06}hjW(brZNLC^o44EO{CQec#79pi$iAOYuMv#)SxF$$Vz(hsR5RN*rYhQeg zp<&sHZKHjpPxFAr@WwqlsNJ(UDD7#ISQ#rTMN8rwG!Ox%fW{-uQG<&+v01wulvBq9 zhR&*(O-^hssF2T(dQ=^tjD^G{l4Q_g)*=g{AcBJ%MzrGu-R~^fg7z+Q;6eHV@=uu4-82U zYi3xDqA81lsJ56+42C+FLqzlW?i!97^Ob@%BjSQaSS=(GiKG&n)i%rk_&3wK+`#f1_%uMx&~s9uHc$EgY5An6W<9p}B;4 zpogCYa)qu&(Ag4m;RW0?c3PnnQowBrN#lw@*>TY-L0(ZbMKQG9>QDeSkC*Q$-5f{Z z2~0stN5WYZfssX-#AS)L;{r{IxG2~K90(F6+7!i3SxJn5ArdLp+{2>u5u|2HygL+d zb9byj6wZ} zqrIB@aESUiV~B&zwY0sUci%;mf;cmkA+7cD0^$ih9{f{w;v_DJ`sY;R`f3( z?7BXf_vMbW zuU1_w753GAG_~{axB58aI?KM!#N|b)zyZV)ZU9QaOj9KuN$fX{&>fy=f`f8Io+CbZIMpovDCx1HL z?$&C^=R1DyispWLc%|FSKGs*ccUMOLz=7=zt7r7(!|y7;X08;c-@aJ>V5pwIR`S;) wTk7+73`}?J{<7dJ@~ diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57.png index 78c4e93f8a8c4e94eb0513c7b59f6dde68748205..34199910945376f054daa0c1738d7e64dc410421 100644 GIT binary patch literal 2681 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 Ef`SCamH+?% diff --git a/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png b/assets/icons/RFID/RFIDDolphinSuccess_108x57_sfw.png deleted file mode 100644 index 34199910945376f054daa0c1738d7e64dc410421..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2681 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 M07*qoM6N<$g8AuxBLDyZ diff --git a/assets/icons/Settings/Cry_dolph_55x52_sfw.png b/assets/icons/Settings/Cry_dolph_55x52_sfw.png deleted file mode 100644 index 86d9db1b497cd9e49bb62987cb35075b4bc7a410..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3898 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 T?I80e8v&Rb+u+JDu3`TM+ZHm< diff --git a/assets/icons/StatusBar/Attention_5x8.png b/assets/icons/StatusBar/Attention_5x8.png deleted file mode 100644 index 137d4c4d054227f27ad6a7e3e374b100d975d9af..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1690 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 diff --git a/assets/icons/SubGhz/Scanning_123x52.png b/assets/icons/SubGhz/Scanning_123x52.png index a48c5330e85c2135866fe99bcb6f3534d06c6d53..ec785948d035b717944bc41811f73d190e6fc3bd 100644 GIT binary patch literal 1690 zcmaJ?X;2eq7>)>*yJ)S*p|h?~Q6$-f1d>2NNH}5%Lt|z{KrzQc(y-YyStJQ+g^C9Q zSWv-YttjHfh@wyt@GLFhMTjB}M;LSv6%;{^;@J%X_DAW??0&~Q&+|U-`@P@n?x@Hx zJ8Nfa0)b%14d?LjF%^HQmS*_Zy;Prz4^CJ}G`0p!z*2-Nm=GjEMKHicgo!X87D}`~ zG{XJ_!fZF0AR3G2MKHxELKK=XL=B?E*#v@rphhVa%V7)_o@3{?OoMWF~y##kV3^-~Ura#~iQo~#pIF_K28B$0`bDW@qQkN5vj1er#wF+Tj+ z?|%xb1zIIc;=^h*StZ6#E@7!Dl#l0+R;G}k zDeC1D1RjscRj4tcLJV^`ED)C<%48Czw=bJb<4}SjatMt~4q?;jbe~|z8=_EsXfzI; zGR5Vf;$#F?U{hSlXD)k2uBjOiB_5drt7MyCNvH}%fQg)$vYEXwX4ISHN@n&FG$WUU zn<1G__FpGGwS~8jX*%7w_+q;CVFljrD!j4V;}c)t_r;dW2@+`9`eU1O>Hy1H=Z_x? z#)vXQ4`HsunYK6jxY4-M*|zlR`rg;$+V*St8!R`}`J(H(M(Fn4S4n;$pnW-UN$QA` z?fY?KM*pGHedF?8-QC`CtG>sHp2-iZetB?^`cj>4f5g#9o;N06J$3^rWaoX6Zp!iN6AS8ml(v%micb;q2O2KuEfM&;;GfOLnbEbQ;Xh2MB~uUb$O z=Tf34Z;Mngv^s8}xvK?|Q)X5xaeMf&yLz=D);7(;s_;xKaAkeDy_0K?%Yn2|k9C6T z^kdg4x7}!2l%F~e-2N&yKK{I*<bm{PN1M%~EqIS+ z#{g%nih7mt;>v=&E{mA(e4W(c;<>|5`bM6gWBHY@vRSt9LE+^Uhns6zS!2UzZw(cU zoUfWE)n;PLGTwze=xNtR#E5s54 z?u4@P3{ljfm#4cr?==i}&Q~nx+6o_SALMl>7g+8+b*EGr+g0b>QusCdoqSG@XTkT} zJKW>*p7M7!ScsC+I$SWe`PeH**g7LKd+2^e4BT{9(i5Fx*_t#~^L&3q*^<`Bg8SLL zJr^&8Wvz+nlWyNPFyz7qEt>zJmbmY7f19~I|Kz}R|I(kU_SSdG*|X+`TiP_=YR)O9 z%>w2`t#*GaxqGd${KiVYrAK$bi$_|DyT^Fr>F$>+;8w9Tw&Z$d+!FWSfdT)vdK%bz zZxc14Zjp1X%kW^EaVRy?Y1r_3XHB46)J>z~?!08J+0&8}hNJx?^62efuWTu{t@zFL zVZ4jin2pbcGJ}2f!HjR`wC}%p=#A$7DVOR4RjdwPz~ZWrAMHE3V&KiltB9(rl{Yu) zpLzA}yq2xqYW6IxKLj-^F^5Q^3s;lX5 N!3~Mzlm%~0{|8*@s)qmo literal 4092 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 N!3~Mzlm%~0{|8*@s)qmo diff --git a/assets/icons/U2F/Auth_62x31.png b/assets/icons/U2F/Auth_62x31.png index dd220bb65104666cb19752a96fe716cc44dffb68..40f094ac9ba758ea06838c2bb8376cf6f28f8050 100644 GIT binary patch literal 3761 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 delta 1860 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 diff --git a/assets/icons/U2F/Connect_me_62x31.png b/assets/icons/U2F/Connect_me_62x31.png index 495e8ab55c1e2dabb547151c2319f321f4af13bb..68c48c0e68142548919d6a4b02e40b48a243b04e 100644 GIT binary patch literal 3767 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 diff --git a/assets/icons/U2F/Connect_me_62x31_sfw.png b/assets/icons/U2F/Connect_me_62x31_sfw.png deleted file mode 100644 index 68c48c0e68142548919d6a4b02e40b48a243b04e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3767 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 IM6N<$g6Xe=bpQYW diff --git a/assets/icons/U2F/Connected_62x31_sfw.png b/assets/icons/U2F/Connected_62x31_sfw.png deleted file mode 100644 index eeaf660b12ea4647154c8d616b468a3098203356..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3765 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 diff --git a/assets/icons/U2F/Error_62x31_sfw.png b/assets/icons/U2F/Error_62x31_sfw.png deleted file mode 100644 index bb280e75121e12eae045d87a33f61b1713af6f85..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3751 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 literal 6876 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 W#^eyOwOvyH0000ZB8b0=g#+k z_y7La$vcsnwa$(njyxXES*27&ad(Ehf@a%szx(??vFC0MMrAy=Img9%&ES885R5X2P@K{dB9p<$p?SQ()g~i~r4cNkC3JdH&i}sg6d%yza(--p8dMv@ zh!njtmnNcfH8EIj8V2M1)j>d@3E>C~1d9SDLpsSICOLnC7va{{Z80C1fUs$Deu(uz zAWj_#gi$mBNJXF!13?Io!6J#&-(L!@03Z+o#bAI~0tqEj1oTHFGGOY%=T4*XWF$)Q z`>C_ICpkZbWsQhfoSmI5%Jvgcv`#F6VOR`8Vh9p)2qBY0vZzT&GE1fz6a<6OdLyf+ zNWjX7YNwhuAF&nut zlTM%T7{|m!I$L;81?0JCCML&7h@%LG%A_%3 zO%`|J5~~^`5=Ij!OVKeDl|G%Q$Z2^1BoRS?PpqEAscc5@GXp|_vV@$^WlbUkA?_Ok zK?n#V5acTX5fGe&s<}GAQ5OB*z!a`e&iO^CEx1S+l}^!W3g`Ur;{!N`BvZ5jW@%VH?>OF2Tk@O zPGOv@&rGEfX|lv0Cxk2gKu)ie6Af#Vr9x}>!CI+Aiv@szVry$~6u{(al2-hTBEgTzn_D^}jklllIvu1V{Q`ig6OgP|0jI zN)sVEE|=@hm?j7H6PqgYzU5==|fB0<6@J90B?N8); z?B48M`Q6&q<>QYftD|a*tJ$!0YduA;TS}(23t@i9jJ}9E&d>+O-{j}lDtd6mP7wiU?pLh0* zla-TQ!!6f>9b(>jct-Z*@vzVmEjaUp9adYyRH)W#u&{1)0G7#K8z}OOe9Z4J`?k~5 z;u#n4^?R%GdBZDjly!H8xtVMF9ud_Q|CsUp%X4BI?jMd19&&9{QqgG_a)Rz9J*BH| z$zM9cbZYA6R(n(=QYD(cO(#Aoy6CQh;hG<}_gRz&>ZIovmNuT&Z9VwM8m5pu&$kG$ zvTJ!+pA|E6E-UBtJJrv;*XaRo7|Z#x4L(qON`UQa?6`jZqnkg3XliTEuJKo%PCa~M z@WlnE3u1ZRT?c;b@m&$07PGImr1km-TQZ8*DS|rZudw{x4R!5F9=$VOt{XWj(Y>BT zd-yG`a(KJ-o0Dfs8h&U=J*C(_ z=8hNq6aC?^r7wqGy5!v`zvX@KNEDDEpXqBVXiB`Z=eNZRgGG2tG`F;x~xDn9)G1Y@4Fl28Px*E!|ivy@~-8Lx%@`DyQ}?V z4f!BGF*jl}N~1D%!=YeZY6W)9lyDw_Uq#NDJx^=CJZDD2|CF# zA7Ixt{Z7BT8@4fZgFkI{D9fJxang<$JS``+d(*81cbB@prG*c!rZ)8U4y-<__Pt)Z zZ3lJfK;Y5eZHd?A3O-!mWX3$UChhmy)r@4iKkvyz(mdTtF7?TWn4`7t4=} zZ`OLe!fHzEo3eUH7jwVD-n?Xnx$AC<-H6`;RB2iYH9UO}ROfZkPOl32mRZ%`xW#FL zD@GqK${E&#=gzidc(qkxLZ^tk7u}u0Uu|;00}}A@rq4$9xE75>Hwj!4$Nk!`)YmDg{{4HeKCy?7Z85xPzg%Peucca}QJ6#D*z!+`G0ZOj diff --git a/assets/icons/iButton/DolphinNice_96x59.png b/assets/icons/iButton/DolphinNice_96x59.png index 43cc58bd9b263ddf1ae1676f997ebd3aa5c2d74d..a299d3630239b4486e249cc501872bed5996df3b 100644 GIT binary patch literal 2459 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 Y0|Y+`m_^9JS^xk507*qoM6N<$f{I?90ssI2 diff --git a/assets/icons/iButton/DolphinNice_96x59_sfw.png b/assets/icons/iButton/DolphinNice_96x59_sfw.png deleted file mode 100644 index a299d3630239b4486e249cc501872bed5996df3b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2459 zcmbVO3s4i+8V(M(gEFORwSrA`4O0uPn|M|5y* zB*aMDxC&7(gP9JN;POOi-9khrC>Z9YJs2U!LnVcQEEC0fDtKo&ILlzb30%M}3J^;~ zv7RzcsilOs4Mq@tD*&R;!LMSk2A~{(`HK9|hQBqEX)3sQr9Je6SZU*F-^fD-p+~Hs; zHLkO%v?>ZoxEv+F#whudr%615FkA0DYR0tMEo}3OOY#xecLWe>xV?u5KtSmC^ z7)Fmj6gjfKstiEV-*Cxbbb+&rRWuI_rBJ)ybs_f1Rn&f2>q3pYwI^|J(hdn{j{0EZIm_F zpIyIWLsRUgOItR-dUbVd|6Zo=_BU_Tj4|{{jxO#=JH4o8er(5{!nZD_j4}MH&zh~9 zVLC~y(0-D6GO0ghZD8BYzP?o{>22~lT6^d@X{SwQ8vrNY-PPIMajIwC)`s14Ep72@ zeq7YOzM`?U{+W)ocXBr`eSOcpk?Rxc=ou5&)fWW|pD};-Z0mvk9}=&`Rb&y<77W~a z(>6YM;6Y5aIU~JKZ}mQZynKHiSTQ#Bczn@&jTiN^?vPJ(jhm7cXLx0oum5P$`TceG zU+wR;OO^)8CVlnM)5p$CO&e94KJt>HccCaHGusmW_b`T6m| z-R6V6Db1pErTot?^d22ojm+2>_)FbD`_+WbDGMx9f@hO27maS2`csiV(D&Fs`PS2& zvrq18du_&zXID(!KIxsU$)iuTYuZ?zmYiP&n&i@Be{IdbS-jA2c0QAlu5NXQv_0K< z3Hvs4eeu6B7yD&CNT~gIkMV&UkRU=V!iQ(+_(O&u^ah$+s{_yn(yBYeD40HeU{xGsIT6W Zfq!wOp!QmC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S literal 5122 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 diff --git a/assets/icons/iButton/DolphinWait_61x59_sfw.png b/assets/icons/iButton/DolphinWait_61x59_sfw.png deleted file mode 100644 index 423e079199b00df0d910981caf8944cbaf8ee67e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2023 zcmbVN2~ZPP7!Hc#RVu|&R6N!I%3-nxkdW1AfgnT)&=3`rikr>mC`oqNT?mIZ73u(L zrQQXsTAVP$bgCRqVL%3}R4ZPzma(V>JPRlyt(Mwx+HOKfI~`kFcXs!^eeZkUfB##O zlo0DNW!4lPkLMwelPS4T$~}uGjpN?2soqDpVKNn$%J6tor`sPlUipC;Jl=#i45}11 zMG=qUq)CWrNHrnMF;N_v$6K;2hr;j-f(6us&R~}EhnidYfI%bWuL)N`3M!h=8{+b4 zA~`QXh39495)FUZQea6A$`P0d76WojMl*xvNcj$4l$+a^K|bJsuo+T*q+KA8qDTUw zNtyseLP&r^5CVuLLRb_QCW00L2!uc&6b{0O02ZN87z&F4=f&rw(HbqPlr4A4;=ZJO zJE3n9Bn4xk2i;ixRy=n$^KLBdFw2s6uYSlET-yrfXL z;LoKsnOtawjmhRTa@zJ>G^5I;2vA8dWEPDRG1;6%zcIxqJ;{=cp8N+pT-z>dC^VWT zFqWiMBxxKARMHp=fWSfo2wY<@Ye)+dWS8PRK*%tbkn*{x!2$^3ZWV%{P&f+1AuxnO z&?r>F<$(rcvHu1pH3n_&3!xeu)snOc(Gwi$zvRUzj3KqG1*3^b z9p~;B<{ii>584ZM)DH0PCOY>1Qru&3u4CAzu2#i;xSAbd<~khBwX$#Sj?d@u##!XD zNR@tb=h}6&`}|35K||KN=gwBmj&-Xj(w;uMx-L?`(_*_ZqL8ard_B`EtMjXyZhc;> zZF)8CG-7Mu_=mE#!jz);z6%T5S~mVv@At*=X|?Ol?+v-67}*3~ z(I*yd*!+Hl{6bH0ad%YF(4l>;^=h9m2|jf9+!^TVd3?C0_k*j|7SExD#fc+65!1f++)=)#z9VKCyt!)o1FiS^CqLF68$Nlu@R-*F`KK+GldJrn z-6KW!*!*?i?t>eHLo#YCZFefNm-}Sy+HzW#(kJAQ% zu3Sm`#Ai<6U{7oT)is=0nUQ{99C7-Y$5MCXl>F$xVZhye@tt~FtDW0WISrk6aF6fm zc|x z#r8!%cqXJshqsp~m3WISwlJh}Z|BwFsa3Puno2spY&@ROyQ9E+zRBvfE9A|~$kXqv z6B}Ob_YXK*dq(uIvE9}XXM5*fnIhFsF*9M3Y+v$?f)8s}ZTskiwP8;Zmba>-dq;Q2 swOjp-{igh?qWDbj&x1Zp}@CP3WTf1pBX89+MzD8nvO|4+g31aR2}S diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52.png index 90b589ff8e9f18b28d7fd75ca525a1db24381b06..2b4bec7c6f14f53e7362da75f95b83bf387eee54 100644 GIT binary patch literal 2157 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 diff --git a/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png b/assets/icons/iButton/iButtonDolphinVerySuccess_108x52_sfw.png deleted file mode 100644 index 2b4bec7c6f14f53e7362da75f95b83bf387eee54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2157 zcmbVO2~ZPP7>*~02PorFsdimdMA+fKow8|q?(QRKr!r*R@tj{x`6Nwm6rph(oMKb4%yr|RP{ zg0_fp1D#2V?H0xj7ln_vGdPh=@<1k;MOjtg(}RaWfHJ7SsWLsHXEdaVigvJMk|REu zaAXro12}#h5N^i=0t?CGfZbxYa+qBOw(?@a+`SBgKr4jLR)K1_Kp<700BC5I1mt1_ zA`k=x6iTr~E|toWFaSkR1V&`A1cfAW43T0I1<-zhf;84(#1gep?XrX~6=>pl27_Un z%_g>u7Sn7NEKw?zFoMD;3JC~^%eV5l9kOyk9SmBMBUp;zDcTCS8SzXymsf#;rfnuz z7!R$LYj>02FxZYWutbcwO=<-i2oH|QWzDU^4FpV@NegM^IRPv2Uu!HmLMbZ1c^Z%iZLddr#Tb-4|aIAJ=QRoh9z;HW|L{! z+!3gR4i*5Fh*4nVRLW|gZCr?3O8Ws)i}R!k6rv`95LCF6Q4~XDm`oljK`;bqgX)Dm zFyK7?-@vqiGUk5}Y9KHp&0285OOyrAB4Ngw)hbP|$6~A;k6Q^cMymn^RmBu#z~oX= zAyX=h5GuSNqe6;6nNlJXl8sgv38P$rAcVBzyp|?%-4X0KZ}^|*C$W@JLAd$jc{~xq zG_;v!^|V3o@@NqFb3I0*NnmLsWfnHLMBM}+CQ>7pDCKep6-(TS-kNY&G{p%~&2KNA zBr>OcW~PAF9K&$JT?Q(UaL1oCfbGlFN4v0%)@C9F(tpW|HW)`6c^l4>>MX(CAIv*g zP#$&{Y?~eM-%V`Y`%7_mz=e+Co_bo9@Zo88q*dr}tkBAQ5}G1KqRww)wCZGg`K{GA zoW}w0;vDp8%bD|$0VhRZaP^aU-_`NZ!(iWfs5*t&z8tl<RcBM(Lo_&0F%-q_ojos_KXD@w|bUY$` z@QA0blA+dA6CC92eP`Xy9k&B+7hX;`pS<>9#gIE(Swzdl8>=K~&gP(t_vgm1`L!;8 z`^clyDwm$UhGqwx`u;g}Uw$_L==$Qd1JuOH4+&d9*ORIU%T@ z*7ixc6E>y4yb*XcZeHUkzZ`yBL~KUNLf@5?>cW;jFV8r?O6d+l_u>xJ28WJI^14{G zIlXJ*L!ZV0nydbX!MZ(922~t>VLDtDH+kjVqnRaP>g(G6Iay!^Pc3_PJ?+Kx(yxI<^BY^Q)!X-B-}(oy5N+XFBB^Sf z?vbpf%vIRpd+-af*PHbPft%M(tK2P$U2vT650vE%Yr0hNHl(P`VRLppvTRXb*|emG zvuEu8@_f_8mL2=@!urCeyHep%6}v;S`zZz&d*AsX>QmS2)wT9l(;MgHFIzgjsv_tF zzBjPum)d=fnViot)zA~qi)SfqibKE6xBu?HwuaTsysTWeNb7iayFpw4bvfdO9l@s z__)F1Bs4IC%YIQH0Rc>WAo4yCJ%`2*B+|!?rVl@O_`&A_2d#}Vq4$T(!SHS{c%BRZ zO#&yRJiMU!FAf0qZvz*)Y{BsXz(M9f_Wv8q3@|zHKR6J0U@VHp4_^cv_CWM!pd5we zfCtGv0rw0KmjEAYvj@6tMdJ_$x8NRu0gi+6!2pVb>i&Nzh(D+P2pB$v;~)4(`~dg= zz$g#|c-#T?fPx>#fDgDla1=fydxORPGhi6`AmH-;gV4Spq5>d%?e6vX1Ke&Qp=XeO z0OC&&`2*+xGo^Vzz~%l2lnxDH1dxzH@gMNP&twmo|A!9^1&}^3_8bHxAAk4`0)7i9 z5Db&X4=4aXMJ5kS06kkK02nYn1*QNLxo`pM@gRFE%pODp2bF*x!~_DA!2YBJC5(On z5C^dU$zwf$MhWcmJ_q$ICiWO50{&GKdyEeOV@JRMej>5(P+RZ-7`SMEs6+e`%m{D~ Og3&$z^CF>$-U|nYD9Y9V literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..843aed27c7df836480f795cd649be62ae08f6ad7 GIT binary patch literal 462 zcmV;<0Wtmo0LlRXAM^hM2j_qv0g(b1{{QfUgO`ENzlTmAfCsnZ-#-zY1_2(x1JnQa zpU5x);RFmIj^FtF!@x2kY<}bOkKlNqkOKjVL_Smh@bq91ZU9h)#t$9%xiAHCfP4Yv z4+w}p2f`0C(u2r+VDcXj0EDD2FnHX}OCATj#2%sY^Mlc{!Sdh((UQROC#Zfw>RuDt z1IfA|^(CAEUSRdWLFPsE2dEfebKv`bf$6}5#{v1^gO~3?8ra`UB*?AfosK+`caK2f!Rd;?FR7|AFNLCzHIu;ty~D#(+b>2gW@gg~5r^p_{(+8!2 zeqix8mI*jc?Ad%Ohh0Y E96sC7hyVZp literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_24.bm new file mode 100644 index 0000000000000000000000000000000000000000..ad87a307189f29491e693d9665e9c9780584f1ab GIT binary patch literal 487 zcmV={XPjp2dR8PL=T9&y+VlZIXEbZ<-iBL z44zYPDBT$Ej}Ouzy-{_a2$C2Pi7B@|AP+=2i*PsFpzrS0qC$N7=!Kt4?_8H z0qQw0eV0rgo&b7_Odgg7d4t8`N%fc~%pO0|0hYuHAolr?43;7Rj1SiiKrncLMi>uR z|M~(C83+C$0K)^$2minR1_dzw1qA+p@&E8lC2+h0#QuQt-~It*I8U%Vzwje){3LGn d2b!qLWdJ{zJg?x9RE-24Ux0h3MTmq0fq*|E+=BoB literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_25.bm new file mode 100644 index 0000000000000000000000000000000000000000..78de0389efdf70991b33b41f855675bea8b3248f GIT binary patch literal 483 zcmV<90UZ7T0N()sAH)9#2j&C+0|d8o{sb@o|Dqpm2R{BSIQ#;94`1h=egq%zw5Nl1 z`^VxO0P#Ss1$O<%~y{_3%OC!1`{OEX-yP zQhHzvXZ#1O5I$T0`GeekTmbzvOdkMpz(+y(V1P|Q_Jk-!DKs!{!ec`g{ZOUl37v0q-|=uK*tKF!>Y7h=BD4 z2z-I`02$B>f$@h=hycOnUN{Uqek8L8#DBquhJ)7t4@>=r01w51G|mhUhzLCc<-iB2 zB*FIiFnV|a>wgEmfgWJ-0SGv-2r`%h*YZFz*scaJFJBn|$zmW7dqc0&^`H0=&I6WuBKln$G z=wxsXF};rBq8>R^h+^+CK)`>)vBQJs|5bqAA@F1=C(s^Wa7e&m#NmIaJb^<%HH>&3 ZO7jPmd=ycL!Vv-Jj~oCzVGTnElpL}U-NOI? literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..32bb893cae287bcce9d3d5d3590bc1cd890ac96d GIT binary patch literal 488 zcmV%HfP*6h0v~`pVBx3d-@t?Z0gd<`-w!SG~&;P*%1`-&!f#u&QiD1B{cp!M5 z*T4@k@dHIRya0Lsf#>%$s64y?;s=b)P_gmygT^usf5(U(kdXWvj2uS;QYP-^ZX0iFb`n)vCn&d56B#B5Dl;X{tu)0%s^rfiTZqiV*vP%+!iA71(1FT zLqLEd%kUq6`Tarm&olf3#r`v3c_qXiqwzP4iGD!$i@VewpWt~w!{k3M06t0LA1nYq zU?TGm%s#^afdk484pY=(jldH;hC4F}W*|6#yFMeBeEtp@Uy@Oi7wz`rnfxJVwz^9PcMUFR@>OabfjRUhO21sK5naO49W30Ohx5C1Zf{{Y4- z)Bj)m3WXF@cn1(3zw@}jcpeg|cmeDz9RD01DFH{IJg}hw!|=pnzfgI2!9xJSnG7D^ e_#h}?$3+LG{sBOMK_QAm2b3Hhun0grhzAGqspMh+ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt similarity index 93% rename from assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt index 6503043bd..fb0fdb228 100644 --- a/assets/resources/dolphin/nsfw/PaxGod_TikTok_Marketing/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/PaxGod_TikTok_Marketing/meta.txt @@ -1,6 +1,5 @@ Filetype: Flipper Animation Version: 1 - Width: 128 Height: 64 Passive frames: 21 @@ -11,4 +10,4 @@ Frame rate: 5 Duration: 360 Active cooldown: 7 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_1/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_1/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_1/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_10/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_10/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_10/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_11/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_11/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_11/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_12/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_12/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_12/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_13/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_13/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_13/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_14/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_14/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_14/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_15/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_15/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_15/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_16/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_16/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_16/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_17/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_17/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_17/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_18/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_18/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_18/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_19/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_19/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_19/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_2/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_2/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_2/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_20/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_20/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_20/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_21/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_21/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_21/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_52.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_52.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_52.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_53.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_53.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_53.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_54.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_54.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_54.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_55.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_55.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_55.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_56.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_56.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_56.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_57.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_57.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_57.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_58.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_58.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_58.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_59.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_59.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_59.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_22/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_22/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_22/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_23/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_23/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_23/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_24/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_24/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_24/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_25/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_25/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_25/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_26/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_26/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_26/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_27/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_27/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_27/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_28/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_28/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_28/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_50.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_50.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_50.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_51.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_51.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_51.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_29/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_29/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_29/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_3/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_3/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_3/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_28.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_28.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_28.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_29.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_29.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_30.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_30.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_31.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_31.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_31.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_32.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_32.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_32.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_33.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_33.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_33.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_34.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_34.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_34.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_35.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_35.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_35.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_36.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_36.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_36.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_37.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_37.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_37.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_38.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_38.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_38.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_39.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_39.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_39.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_40.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_40.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_40.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_41.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_41.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_41.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_42.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_42.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_42.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_43.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_43.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_43.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_44.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_44.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_44.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_45.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_45.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_45.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_46.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_46.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_46.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_47.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_47.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_47.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_48.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_48.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_48.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_49.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_49.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_49.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_30/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_30/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_30/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_4/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_4/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_4/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_14.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_14.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_15.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_15.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_15.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_16.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_16.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_17.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_17.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_18.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_18.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_19.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_19.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_20.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_20.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_21.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_21.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_22.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_22.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_23.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_23.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_24.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_24.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_24.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_25.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_25.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_25.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_26.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_26.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_27.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_27.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_5/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_5/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_5/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_6/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_6/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_6/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_10.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_10.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_11.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_11.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_11.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_12.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_12.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_12.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_13.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_13.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_8.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_8.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_8.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/frame_9.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_7/frame_9.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/frame_9.bm diff --git a/assets/resources/dolphin/nsfw/lvl_7/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt similarity index 92% rename from assets/resources/dolphin/nsfw/lvl_7/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt index 43dd6af5a..f169de719 100644 --- a/assets/resources/dolphin/nsfw/lvl_7/meta.txt +++ b/assets/resources/dolphin_custom/NSFW/Anims/lvl_7/meta.txt @@ -11,4 +11,4 @@ Frame rate: 6 Duration: 360 Active cooldown: 0 -Bubble slots: 0 +Bubble slots: 0 \ No newline at end of file diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_8/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_8/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_8/meta.txt diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_0.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_0.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_0.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_1.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_1.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_1.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_2.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_2.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_2.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_3.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_3.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_3.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_4.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_4.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_4.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_5.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_5.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_5.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_6.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_6.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_6.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/frame_7.bm b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/frame_7.bm rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/frame_7.bm diff --git a/assets/resources/dolphin/nsfw/lvl_9/meta.txt b/assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt similarity index 100% rename from assets/resources/dolphin/nsfw/lvl_9/meta.txt rename to assets/resources/dolphin_custom/NSFW/Anims/lvl_9/meta.txt diff --git a/assets/resources/dolphin/nsfw/manifest.txt b/assets/resources/dolphin_custom/NSFW/Anims/manifest.txt similarity index 100% rename from assets/resources/dolphin/nsfw/manifest.txt rename to assets/resources/dolphin_custom/NSFW/Anims/manifest.txt diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_00.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_01.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_02.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_03.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_06.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_07.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_09.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_10.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_13.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_14.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_16.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_17.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_18.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_19.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_20.bm new file mode 100644 index 0000000000000000000000000000000000000000..d39d7c7095b350f6fe85554e7246e86cb2d6a0f5 GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j-fKtI6y2Mh!7KEMF{ z0rfuzmjeQQpm`K*ydeF9gZSJ){D6b_9AN(k(m28XkR+#~5BNS;$Pe=U1NDq!Tt}ZI6uMt0|)d3AM5?UgODFC;t-fV%qQSS)$kvI(U=49{=Wl%1LPWzAHY8- b5D(%%zrg=T&;ZBK4_5Sp)qNlM_&pQKUu1Io literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_21.bm new file mode 100644 index 0000000000000000000000000000000000000000..c3bea7837cc3042b11306dc8c2a4b2e122cc786b GIT binary patch literal 217 zcmV;~04Dzd0M!8hfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j T{1f;e;QD?D0KNeK2ly!f`G#+q literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_22.bm new file mode 100644 index 0000000000000000000000000000000000000000..83fb1ac60079bf92019ad0610c834fe7e662d02a GIT binary patch literal 219 zcmV<103`nb0M`KjfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j_yB%?r{MgD-yIAOctAg3;Nb`43?5(Le}4h}_y^Pk z9$o-{C@de~^H5kgkQfg{1mD5_4@88|!Tt|L1cUxQ5Ab??0saq&ct64Z5Ac7N{2re` ze}ncupXl>H$?^{<@IP<|A_w0B4iCHd{X78w2lzk1@&5<-Kf&qZ2luca-@tspLFV6( V`m5x>;Qt4&03YD~zyszE50LbpaIXLW literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_23.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jh0Db`f2lzk1{txhc zOVj`02lwHB>HmO49rh0|gajYZFn-_wL;eTE9G-yx2lzk1&=2r`|LoxYpM(4#&GiS$ Zyo>%1@P0bPAK?9fg9HRV0Db@j9-v9gbvyt7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_26.bm new file mode 100644 index 0000000000000000000000000000000000000000..d6ceacd02a2e9c426da3ecd7e0cb086a55343b2e GIT binary patch literal 225 zcmV<703QDV0NnupfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jV6L|1_b*+@+jGOLHh>>@wkEc0SEX$fyNK;eJ6|nJOF<@Wn5tY7uxv&{txqg1Nfp5dbisfge}keg{Ti55s!=0seo_ga`0{ b$^--Wf1m;Xf&4Cjf6v|A4=DqB!|I+;T1anm literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_27.bm new file mode 100644 index 0000000000000000000000000000000000000000..b4a87c26bb2d04806872f9dbd425a6c92a58db9e GIT binary patch literal 227 zcmV<90381T0N()rfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j?oI2mTHZx%>kG0SDZD@<98CgZd5+@P9zT z{Q(D;;2%KZ58eD8Y9U8ZKT*d3SqMLH3GD~?08`oz@PC8+06*q-1N!TZ1m^aBD9z61ndF8llt2p@L$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1jH-fh06&%%4>W~?%>iKJKvn!7;PgmL{2$=-P)I-H;Qt4s0zv){@PC8+AK?E7 zuh1XM`3IzZf6?ZDljI&zz<%H%;QPP8I1q4tfDgbA@PC8j{txhfgZv(zV1Ijo^8t70 eJzMe*@OrQ09;W&S`Mo#*`@jH*A24uez`^XjR(5Ov literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_29.bm new file mode 100644 index 0000000000000000000000000000000000000000..b73177b87d59022dd64c4bdc1dfb43378fadddcb GIT binary patch literal 222 zcmV<403rVY0NMcmfDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Q4fbuxd zG=Kt7pn7}({on`j!h!w|LKk literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/frame_30.bm new file mode 100644 index 0000000000000000000000000000000000000000..feb5ab082ec1d3388d93a6ea6290666f20dcd358 GIT binary patch literal 224 zcmV<603ZJW0NeoofDU`;hW__{Fj4>$d++vx$GQ*jeRuu9{GRtA!13^h`}z;5|GXcP z8RS35!w1j{;0RR|e{2n*~_J9Pvfbuxd zG=Kt7pn7}({on`j!h!w|L*8cZ?uu=dgz6zc1zuF%k=stJ-|M&0z-3QJ0`%nLm?|L6C z_`Ze%@BfSh0WzEIf9J~&*aN^G0PqKZJOTJ{e?$L({j2zc{Qw8-k1O;C=!g zFd6`S;As%56qNx{OcsI=LJ)lDVh~9drbG{zJrf{eFYrDx@dzzbB|rzv4h0~9!6IMK zd{yZXnq-E6z?d8dK_J9Jf%6l}K%qk+XiW|QNT5_8LHUH~U{F{tqwqhN1fmg;fqa4S z0m)#nl1hMnWV8uILV<$#2j(k*g+L`fpnOGW5=HpX1LFY#h><99C`=X;CGg+aCGQ8E zM?pZMP$(Wv_^_+Wb@k zK|s-R0pdSs`~xUy7*|NY5&cXq5TXXf*w If>Zeg7;o0wEC2ui literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta b/assets/resources/dolphin_custom/NSFW/Icons/Animations/Levelup_128x64/meta new file mode 100644 index 0000000000000000000000000000000000000000..485a421b6e4585f9e7e41e4cc628e815074fdb04 GIT binary patch literal 16 ScmZo*U|?_nVs;=_0Ac_O$N=a7 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx b/assets/resources/dolphin_custom/NSFW/Icons/BLE/BLE_Pairing_128x64.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4ab6aef41bfadab1eefbfc91a98cb0a2f4022f8a GIT binary patch literal 480 zcmV<60U!Q=0000$000010Mr2hcmu#50PqKZEB^xrWT2jv0*4hQsbACd?J@E?;1 z1<)Un2nFCDx(RqcV}lU~5=q5DCZP77w@&A|B)s zeL(S$I0b+oP$)1EW5D`{1IN(^2hRe12cQrHA76+b0DvI+*Fd-c{EZt%0tYYn5Woe1 zP6#~w5DV~uprG*=lMrwWMxg>l0#Lw734~I37#>0#1rm|Kg(<%WJXgUkpx!eEU?2Qk<{N5=r5Kr z2v8Vd5@{5I9tJ3!ifIW06hC{I0%#mLlg~<0u@(_O97=whUF!(&@kgRKk6h@3_O1aE}9J(1`hS%#>POy z-Q00TXaBSg;sx*U0@hy X-4EY3s0JCQga3)P0fzCy|Kj-|K{|=G literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Infrared/DolphinReadingSuccess_59x63.bmx new file mode 100644 index 0000000000000000000000000000000000000000..9dc1e5dcf52f7721ffe6f002905111825fcda8c9 GIT binary patch literal 513 zcmXBGT}V@50LJm>eb0N&&YXj@wdq<)XCKB~5p$#$7P-|{+sv4;XvvDx=wj|7L?{S? zj=9R3Nr^%3na+9@>m$&mErIrq%OKhit56PKw(XK;Lql==>?x%R23Ol z_~>Oh)a@+MvyHN9R@1r-y9aG3GqF>mkw6yHmn2!$Qb;t}K-<4z#w$XddxVvG8??+i zG8R$OK6zlX*iFO5K1lY0N;=~(*WLwbi^DhGz&}l-;bsSd27MfIt$Rn_pdiY!!sk?4 zw-8EvX}Pz=288kc(B?BndB!jA9ug=YMy9zAC{bl8wvbusbS zO+u7rHbK`Qf0daOyXu6wnNAE5Rh^Y-%bgg+Xb*j|$eHm*B6IgyAiU2^)^bgbM#V;+ zmxN@iXPESi8n-&)Hqts$(`S|P@IJ70Tl{0JYef#|lX>Cj!+>_YF2@$8g9MM?3xVDN zTCl#OT8z})IJvA-3uM1Cc8h8?T>rV>5l%4*BcppFTwI@Wy8S1(zaM_YqPs%8aELi> t8{fhUX>fy1fgniNxeJ&S#V{idsF0~dO*nPBe7?N;id8D}o7d+nx__kHlc)dy literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/NFC/NFC_dolphin_emulation_47x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..03c6304a2d4d334ac519d1861981fcfd0393755f GIT binary patch literal 375 zcmV--0f_!D0000z00000K&2#AN(I0uRnb%ia8*gFk_?zCl}e=wSXHT1RT->OB_&ld zR;pB~N^O=Zsj5SS@@HBmk^dq#ytRuT=U801$K+RRa+K zaBk`XfJD#@t&ac@bT?`dNCaG8ts??}|8-Bt0AK-Iwap-1KyNLN3kvvJTMfkluQs+y zf8bSZT@3)JYBg&Q33{qpo=gDDv^1*@K&q%}tqKUlS~XjZ05DW-t`0!zvs5b$60j*f Vt&RWy*=k!20Kl|ft_px;u4-Myk9+_C literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_DB.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8af359b940e9a14587bb8ae598aeff8e53b6542a GIT binary patch literal 286 zcmZo*U|?_nVnzlb#%uNaA8<5O*ng-ku`!k4;5o#|^Znq_;{W{zl;wX&{8nT5x4|L7 z;m-p%=id#Nn*Ycj`mg?t;r@Z|{|si^i{!~gt0>JR^u|M0*4X8pte z@-PmH43zU<{sYtF!*BnvZ>zC8@B^gpzg+Y5@3#B``oC=IV+w5de*eLCI8b`#gFl5I zc76PB_{U{&aMyu~V@doza((q1rsO$4e#qW&=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_happy_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Passport/passport_okay_46x49.bmx new file mode 100644 index 0000000000000000000000000000000000000000..715815f0cad52a4138cb8ff3db6a9fa15e4a2b46 GIT binary patch literal 297 zcmdO6U|=u+Vj%dz4=4^8rQbe*z^QynDYEsBnM$bw-9g zzyFIeGyM6VmCeksW9?mUHiifFTeq??TyVX*nvG#Ybm(>#h83$qR&z1jShZ?BA45*) z|0qs|D{oJ32eRY$t>a=SVVt#{i{Y7U$1X00DD{KcybP}yCq(f!WH~m}@; z!&jjr)x0Z0k1MX`eY3YUshW4yEB={Vxv$-_@TnDDYxZFBTIuapUW=+s*NRDXe$`q# zH!-DKTkp0)C8PFgJBAai+F{ZM4k&3~=Ud{yV!nzsk)u^K++Kr0&HT!{V3#J*t+)R* to-$v%?QVUO)av-{zZUD3{{FkV+xlu={M|*myZ8Uzec3GQZT|P8wgB^_eUJbE literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinReceive_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..cb92216c4e5943d4d845c979c7d8c59df040b95b GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUyG$`6BHT)5{U3WANw^bz!@%V0w)Q;`Zx7& zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H7W%wg literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSend_97x61.bmx new file mode 100644 index 0000000000000000000000000000000000000000..8932c5ed3ba499c5a9259d2d228330e5f4a5e94d GIT binary patch literal 525 zcmV+o0`mP~0000z00001009EPKqgXeMhX*8l-OV((tye0o(wz}3$3UE&H)mMcsKYD zW|pKGAP~%=d=LH`Pt>3T3I_p^O1K~GUkjyb6BHT)5{U3WAN#c`z!@%V0w)Q;`ZxA( zH6R#MF__BWx)1%zt7#Jy#sML!!*Cz@m9I(+D9i#?m5T6h`?{`x7*SXTMx|BY|LR`7 z5HO;Fk($MPTDpyhDQ~uu0SpEBCvo4 zA{iJ@Qh9)q=3)y}$ilS|iU#6|FmE7aY7v?R61fNlCZZLffk+?{h`=!-DiQ_^DhL@^ zWC92b6i9#*B3%&3;2^-oln4kiBC-G;LJABp0|rz8*#H>^d;qur8BhU_5P=4201P0^ ziLQVdA-RAO5)8IMgCZzEwt4OZ)u4rgATl=uWJ-a8YA|9zR-_Dbki{So_rU`e35bAg zp+LrvLIWcpd=N2W6+{{X7C<8qWJD+wr~}-Ajerb{8MQJ#0&szkB45M;F#v~H0Oz1J zm}Fy2!@yM~AX1zv20#WXRKBVODMSK{ra!H(pam+#1BDF&kD|X4kQB%=FlaawZ}FRj z7#LTG1PBN`45>6>f&$b74)H4oi~zP)xC$_*!@_{ZfMbe)hDyW?Ej%y=FqkkfsAp2t Pk;-Kl1{VY}U-D@H5-7Y~ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx b/assets/resources/dolphin_custom/NSFW/Icons/RFID/RFIDDolphinSuccess_108x57.bmx new file mode 100644 index 0000000000000000000000000000000000000000..3eefd86bc994996073b05eee2f627dba8b8bb9cf GIT binary patch literal 502 zcmV z&sG8a-`8rskx5wK7)>Us0pP#(ZleJPjN~>tnxu#E->Y)$fYOw}IF(JzlkvaqZm59L zn!q(3O%<2%+Ur?tK+={#D4LqLCjC;k%6NwJ4USW(=8s@H*J~&=g(F0i>akI}1lB2G z29#7bHL8bJFJPO-b|BIzjS`7K#=-O%tW1C!M1j;(FcPR7!&yw2Ez*HV7Yqg=LaI~& zsicZPB~S=J3}pb+q)`E64ImJP(n$co14jWCf(_P~1R!X_pR1rP93p`O2CV>wAkqb> zY=+_ussN<{uuL&bhVa-R0*C`%KnXG~hT{Pe2H0%?W&lvRkz@%4I0nT)8g&3`(G6!V z8{X0a4S?BdfsX>z14;}HcpX8aX?KFr3IhRwgI)=cf_y4aB`d}%kV=M}r1S}a;HwN6 zph_Sw)HbgIK>`NU0#X~Lki}qD0BlSk6KD-E)SclLKKZ5N}US)0ty8g z!LX4heZasK>JQHga$BQC_!{%pQ?BqK(GqQC;$Ke literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/Settings/Cry_dolph_55x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..22a76dd4daaa01bda8be01f522bf58c0a9e22e2c GIT binary patch literal 352 zcmV-m0iXUi0000q0000108|0&f4~O;{CmL10@edpx%UGcAJ`2@FYpYIKkykK|6myQ zf+Gh8mv8h2PuJ7`4+<8n3?S3C?LY9)xc~3I5AZbZzE3Z@uhZZ zPz|IH-D|$@AQ%W1@>|%TEd~Sqk?t@M3`7RqN1OqM5dr>prx*}vUuXA#(BdE+%j{qb zJV*!k?{NbU02)8(xETMR+sLGR5f1m>b_O75{_c1R1wsS7|IC4-*noew_dE>((E+}D z?)X>*aF{>2oEibnfL}lNd*lNi4Ih2i@q*UCKO5^mgT4(f{NG9cqrktL+dc#TmVtgh y_WcKc4on-}yc#iZpo2$&-=jf4AA9x#`V=qyv>Gt@C-d+B;D17bMIPUS{uB^jDyGK( literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/SubGhz/Scanning_123x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..75f57367cf539f9f075e06c983de83f6fb826cbd GIT binary patch literal 458 zcmV;*0X6=60000q000010KNgL>v~p-wblX;ingkga@+Z!VEF3Vy1h-Nsss;>WlLjT z+vSDxD$@V=`)#Pcbv>>9|7#Wx&5Nt|-|OO_wXk3SV@c)v1LO}`1~3LaECxm|c7+Gq z2m>6h2L%U~gW5raz`%6S3p+Y|APS-Q zK##A$oB+fgJ^=WG-rxfrlnVe4j0c1gI%d$oTd#n@lgCjD5;hNsL<$U^9wc}l9f%YX0SK{8!Osw=LDNGFxR2stpn?O& z4e&3=BFhB=4Gy_F7sql89~ci(C=ZJS76saX%cTJLz+kXFCR literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..4a569c1eb27507a1301fa34d2ec5d96d62fe43be GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..582247d43e0c1cfbed6e9d8061ec26b7ad417339 GIT binary patch literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..fae05bb57f5b883c6fd532a150899d556ce750cb GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c91714b3f1114d842e45cb614c456d3b40b164c5 GIT binary patch literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/DolphinMafia_115x62.bmx new file mode 100644 index 0000000000000000000000000000000000000000..c37cd533d7a3df21e738cf4cbffb4483f9a551ca GIT binary patch literal 684 zcmV;d0#p5S0000!000010H6YZLN^}%?5|1(7!WzTpQ}}`Euew}ngP5Hj;~c2fP$jGr)v<6eb8Gs)sl8j*#s@XScl6&=tNY&k92~!j|6}!C%F({S z=Im!~n^}Lh8}|n{qCf2YTIbN;d^{Y!$luc2+tfmT;O6p2{@$+s;u0Nyq&ubU+S+43 z;Opc1T&ewrC;o+lm+g+}cjy1GaL2*R;^lwxR{!*0y&eZI%bknDfA}9@>-@Qxxdguh zHt$7&%lhSFp*QRvm%JTsk2Vzmw?QSo;B$U?kf={G8LRhAgRRMeLZOk0*ZO`PU~tAW zGY*xyZVq0LW(g9^P&}V`@Hu-mpY+pX;Q!sljoGOGruHw-|JDv}PtX5f!E=B2vD)AP z#ix>f|Mpw>b$Pyj<-U{q@850V)#iKul=@fOKerC90~!AOL;9bN{p=jrZnvfZu;g-g zRp4^vK9Gnff+LLY|G~?N_C_Fh2_GAK{tmaJ;{kwixP9R3K|27^fI>VQ;B!Z_5HK-V zHR8sKP65mN2PXnGet`jtLErm~$U0E(#1IT!4Zr*xvG4R83}26Ub!ZUZ{*Qr+{oe;HgCGB&fs6m!4z_>( ztu6=ux*dOd0K6ane|-FK3V^VG;DP*q{h;O83ka4!;OO^3$=7KuaoHhW1k0s;3Y7vSDM*2jpjMLZBI+Z4XW{hX{Mk{Be^YK@J5q) zMh*$0wO$8l8=yrnoQmn700Rn)+GZo12Eue7;L~08>hhygRXatT! z5V%2MQ|#Y9&oc{*PGg~rLZ;cyZI~7~7?py-2L+C779>Hy$m|%5R4_(dd5!}iVzNvi zFsbB!9t$iDlR;&G(+wrSHI4(FQq1J_fU>v($AQ|K>7?1j8saoG??ro}3r+_Dp#c8y z@RePXLVLP>;p~ z1<-;%L;z7qL!HGRAvc6clAzh^h+u>z+PQZ&$1fdia9P4Fe%-@6W<@^19;PE{V0Oz>C%GhgJ z`+8s{;n_2uD_3<3AA+l6e#e32@NT8_TotcCe!)e;kJ<6k^Z%#h207tugoa(l243*s|ZbVAbYge|i#h@7nr; zp9<{WL}jZ7*6J%9UFd64^bSU=JrHKUlHPcbinq^Xl%^-b<_#RJ^*~y(=ahV36X1F# McGibf>8G#$2UW|uRR910 literal 0 HcmV?d00001 diff --git a/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx b/assets/resources/dolphin_custom/NSFW/Icons/iButton/iButtonDolphinVerySuccess_108x52.bmx new file mode 100644 index 0000000000000000000000000000000000000000..47b611a6ad2b3c2fa21cda23b155e56742d70c77 GIT binary patch literal 482 zcmV<80UiEq0000q000010M-Gj>v~p-wblX+YPPDAa@+Z!VAkr|y1h-Nsss&fWlLjT z+vSBXD$@V=`)#PD^*ycq|7#Wvy^E{&-|OP07o_t2fwBg_O97FK-Jw9&%HVKNd1y8? z&PSZtNcj%R8vp bytes: return data +def pack_anim(src: pathlib.Path, dst: pathlib.Path): + if not (src / "meta.txt").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "meta.txt": + shutil.copyfile(src / "meta.txt", dst / "meta.txt") + continue + elif frame.name.startswith("frame_"): + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + + +def pack_icon_animated(src: pathlib.Path, dst: pathlib.Path): + if not (src / "frame_rate").is_file(): + return + dst.mkdir(parents=True, exist_ok=True) + frame_count = 0 + frame_rate = None + size = None + for frame in src.iterdir(): + if not frame.is_file(): + continue + if frame.name == "frame_rate": + frame_rate = int((src / "frame_rate").read_text()) + continue + elif frame.name.startswith("frame_"): + frame_count += 1 + if not size: + size = Image.open(frame).size + (dst / frame.with_suffix(".bm").name).write_bytes(convert_bm(frame)) + (dst / "meta").write_bytes(struct.pack(" Date: Fri, 10 Feb 2023 22:53:37 +0100 Subject: [PATCH 164/231] subghz != subghz | same name, entirely different place = no good --- applications/main/subghz/SConscript | 32 - applications/main/subghz/blocks/const.c | 1 - applications/main/subghz/blocks/const.h | 20 - applications/main/subghz/blocks/decoder.c | 27 - applications/main/subghz/blocks/decoder.h | 47 - applications/main/subghz/blocks/encoder.c | 58 - applications/main/subghz/blocks/encoder.h | 67 - applications/main/subghz/blocks/generic.c | 120 -- applications/main/subghz/blocks/generic.h | 59 - applications/main/subghz/blocks/math.c | 244 ---- applications/main/subghz/blocks/math.h | 222 ---- applications/main/subghz/environment.c | 116 -- applications/main/subghz/environment.h | 115 -- .../main/subghz/protocols/alutech_at_4n.c | 455 ------- .../main/subghz/protocols/alutech_at_4n.h | 74 -- applications/main/subghz/protocols/ansonic.c | 346 ----- applications/main/subghz/protocols/ansonic.h | 107 -- applications/main/subghz/protocols/base.c | 62 - applications/main/subghz/protocols/base.h | 88 -- applications/main/subghz/protocols/bett.c | 342 ----- applications/main/subghz/protocols/bett.h | 107 -- applications/main/subghz/protocols/bin_raw.c | 1120 ----------------- applications/main/subghz/protocols/bin_raw.h | 111 -- applications/main/subghz/protocols/came.c | 347 ----- applications/main/subghz/protocols/came.h | 107 -- .../main/subghz/protocols/came_atomo.c | 598 --------- .../main/subghz/protocols/came_atomo.h | 110 -- .../main/subghz/protocols/came_twee.c | 468 ------- .../main/subghz/protocols/came_twee.h | 107 -- .../main/subghz/protocols/chamberlain_code.c | 499 -------- .../main/subghz/protocols/chamberlain_code.h | 107 -- applications/main/subghz/protocols/clemsa.c | 365 ------ applications/main/subghz/protocols/clemsa.h | 107 -- applications/main/subghz/protocols/doitrand.c | 356 ------ applications/main/subghz/protocols/doitrand.h | 107 -- applications/main/subghz/protocols/dooya.c | 447 ------- applications/main/subghz/protocols/dooya.h | 107 -- applications/main/subghz/protocols/faac_slh.c | 512 -------- applications/main/subghz/protocols/faac_slh.h | 129 -- applications/main/subghz/protocols/gate_tx.c | 334 ----- applications/main/subghz/protocols/gate_tx.h | 107 -- applications/main/subghz/protocols/holtek.c | 374 ------ applications/main/subghz/protocols/holtek.h | 107 -- .../main/subghz/protocols/holtek_ht12x.c | 400 ------ .../main/subghz/protocols/holtek_ht12x.h | 107 -- .../main/subghz/protocols/honeywell_wdb.c | 399 ------ .../main/subghz/protocols/honeywell_wdb.h | 111 -- applications/main/subghz/protocols/hormann.c | 341 ----- applications/main/subghz/protocols/hormann.h | 107 -- applications/main/subghz/protocols/ido.c | 234 ---- applications/main/subghz/protocols/ido.h | 73 -- .../main/subghz/protocols/intertechno_v3.c | 472 ------- .../main/subghz/protocols/intertechno_v3.h | 111 -- applications/main/subghz/protocols/keeloq.c | 1115 ---------------- applications/main/subghz/protocols/keeloq.h | 153 --- .../main/subghz/protocols/keeloq_common.c | 142 --- .../main/subghz/protocols/keeloq_common.h | 100 -- applications/main/subghz/protocols/kia.c | 279 ---- applications/main/subghz/protocols/kia.h | 73 -- .../subghz/protocols/kinggates_stylo_4k.c | 581 --------- .../subghz/protocols/kinggates_stylo_4k.h | 110 -- applications/main/subghz/protocols/linear.c | 352 ------ applications/main/subghz/protocols/linear.h | 107 -- .../main/subghz/protocols/linear_delta3.c | 359 ------ .../main/subghz/protocols/linear_delta3.h | 111 -- applications/main/subghz/protocols/magellan.c | 445 ------- applications/main/subghz/protocols/magellan.h | 107 -- applications/main/subghz/protocols/marantec.c | 393 ------ applications/main/subghz/protocols/marantec.h | 107 -- applications/main/subghz/protocols/megacode.c | 429 ------- applications/main/subghz/protocols/megacode.h | 107 -- .../main/subghz/protocols/nero_radio.c | 397 ------ .../main/subghz/protocols/nero_radio.h | 107 -- .../main/subghz/protocols/nero_sketch.c | 382 ------ .../main/subghz/protocols/nero_sketch.h | 107 -- applications/main/subghz/protocols/nice_flo.c | 330 ----- applications/main/subghz/protocols/nice_flo.h | 107 -- .../main/subghz/protocols/nice_flor_s.c | 694 ---------- .../main/subghz/protocols/nice_flor_s.h | 127 -- .../main/subghz/protocols/phoenix_v2.c | 339 ----- .../main/subghz/protocols/phoenix_v2.h | 107 -- .../main/subghz/protocols/power_smart.c | 394 ------ .../main/subghz/protocols/power_smart.h | 107 -- .../main/subghz/protocols/princeton.c | 374 ------ .../main/subghz/protocols/princeton.h | 115 -- .../subghz/protocols/princeton_for_testing.c | 288 ----- .../subghz/protocols/princeton_for_testing.h | 97 -- .../main/subghz/protocols/protocol_items.c | 50 - .../main/subghz/protocols/protocol_items.h | 55 - applications/main/subghz/protocols/raw.c | 374 ------ applications/main/subghz/protocols/raw.h | 148 --- .../main/subghz/protocols/scher_khan.c | 288 ----- .../main/subghz/protocols/scher_khan.h | 73 -- .../main/subghz/protocols/secplus_v1.c | 634 ---------- .../main/subghz/protocols/secplus_v1.h | 113 -- .../main/subghz/protocols/secplus_v2.c | 833 ------------ .../main/subghz/protocols/secplus_v2.h | 125 -- applications/main/subghz/protocols/smc5326.c | 387 ------ applications/main/subghz/protocols/smc5326.h | 107 -- .../main/subghz/protocols/somfy_keytis.c | 797 ------------ .../main/subghz/protocols/somfy_keytis.h | 125 -- .../main/subghz/protocols/somfy_telis.c | 663 ---------- .../main/subghz/protocols/somfy_telis.h | 125 -- .../main/subghz/protocols/star_line.c | 780 ------------ .../main/subghz/protocols/star_line.h | 131 -- applications/main/subghz/receiver.c | 124 -- applications/main/subghz/receiver.h | 73 -- applications/main/subghz/registry.c | 30 - applications/main/subghz/registry.h | 47 - .../main/subghz/subghz_file_encoder_worker.c | 244 ---- .../main/subghz/subghz_file_encoder_worker.h | 65 - applications/main/subghz/subghz_keystore.c | 613 --------- applications/main/subghz/subghz_keystore.h | 80 -- applications/main/subghz/subghz_setting.c | 480 ------- applications/main/subghz/subghz_setting.h | 58 - .../main/subghz/subghz_tx_rx_worker.c | 260 ---- .../main/subghz/subghz_tx_rx_worker.h | 89 -- applications/main/subghz/subghz_worker.c | 149 --- applications/main/subghz/subghz_worker.h | 80 -- applications/main/subghz/transmitter.c | 64 - applications/main/subghz/transmitter.h | 55 - applications/main/subghz/types.h | 104 -- lib/subghz/subghz_keystore.h | 2 +- 123 files changed, 1 insertion(+), 29256 deletions(-) delete mode 100644 applications/main/subghz/SConscript delete mode 100644 applications/main/subghz/blocks/const.c delete mode 100644 applications/main/subghz/blocks/const.h delete mode 100644 applications/main/subghz/blocks/decoder.c delete mode 100644 applications/main/subghz/blocks/decoder.h delete mode 100644 applications/main/subghz/blocks/encoder.c delete mode 100644 applications/main/subghz/blocks/encoder.h delete mode 100644 applications/main/subghz/blocks/generic.c delete mode 100644 applications/main/subghz/blocks/generic.h delete mode 100644 applications/main/subghz/blocks/math.c delete mode 100644 applications/main/subghz/blocks/math.h delete mode 100644 applications/main/subghz/environment.c delete mode 100644 applications/main/subghz/environment.h delete mode 100644 applications/main/subghz/protocols/alutech_at_4n.c delete mode 100644 applications/main/subghz/protocols/alutech_at_4n.h delete mode 100644 applications/main/subghz/protocols/ansonic.c delete mode 100644 applications/main/subghz/protocols/ansonic.h delete mode 100644 applications/main/subghz/protocols/base.c delete mode 100644 applications/main/subghz/protocols/base.h delete mode 100644 applications/main/subghz/protocols/bett.c delete mode 100644 applications/main/subghz/protocols/bett.h delete mode 100644 applications/main/subghz/protocols/bin_raw.c delete mode 100644 applications/main/subghz/protocols/bin_raw.h delete mode 100644 applications/main/subghz/protocols/came.c delete mode 100644 applications/main/subghz/protocols/came.h delete mode 100644 applications/main/subghz/protocols/came_atomo.c delete mode 100644 applications/main/subghz/protocols/came_atomo.h delete mode 100644 applications/main/subghz/protocols/came_twee.c delete mode 100644 applications/main/subghz/protocols/came_twee.h delete mode 100644 applications/main/subghz/protocols/chamberlain_code.c delete mode 100644 applications/main/subghz/protocols/chamberlain_code.h delete mode 100644 applications/main/subghz/protocols/clemsa.c delete mode 100644 applications/main/subghz/protocols/clemsa.h delete mode 100644 applications/main/subghz/protocols/doitrand.c delete mode 100644 applications/main/subghz/protocols/doitrand.h delete mode 100644 applications/main/subghz/protocols/dooya.c delete mode 100644 applications/main/subghz/protocols/dooya.h delete mode 100644 applications/main/subghz/protocols/faac_slh.c delete mode 100644 applications/main/subghz/protocols/faac_slh.h delete mode 100644 applications/main/subghz/protocols/gate_tx.c delete mode 100644 applications/main/subghz/protocols/gate_tx.h delete mode 100644 applications/main/subghz/protocols/holtek.c delete mode 100644 applications/main/subghz/protocols/holtek.h delete mode 100644 applications/main/subghz/protocols/holtek_ht12x.c delete mode 100644 applications/main/subghz/protocols/holtek_ht12x.h delete mode 100644 applications/main/subghz/protocols/honeywell_wdb.c delete mode 100644 applications/main/subghz/protocols/honeywell_wdb.h delete mode 100644 applications/main/subghz/protocols/hormann.c delete mode 100644 applications/main/subghz/protocols/hormann.h delete mode 100644 applications/main/subghz/protocols/ido.c delete mode 100644 applications/main/subghz/protocols/ido.h delete mode 100644 applications/main/subghz/protocols/intertechno_v3.c delete mode 100644 applications/main/subghz/protocols/intertechno_v3.h delete mode 100644 applications/main/subghz/protocols/keeloq.c delete mode 100644 applications/main/subghz/protocols/keeloq.h delete mode 100644 applications/main/subghz/protocols/keeloq_common.c delete mode 100644 applications/main/subghz/protocols/keeloq_common.h delete mode 100644 applications/main/subghz/protocols/kia.c delete mode 100644 applications/main/subghz/protocols/kia.h delete mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.c delete mode 100644 applications/main/subghz/protocols/kinggates_stylo_4k.h delete mode 100644 applications/main/subghz/protocols/linear.c delete mode 100644 applications/main/subghz/protocols/linear.h delete mode 100644 applications/main/subghz/protocols/linear_delta3.c delete mode 100644 applications/main/subghz/protocols/linear_delta3.h delete mode 100644 applications/main/subghz/protocols/magellan.c delete mode 100644 applications/main/subghz/protocols/magellan.h delete mode 100644 applications/main/subghz/protocols/marantec.c delete mode 100644 applications/main/subghz/protocols/marantec.h delete mode 100644 applications/main/subghz/protocols/megacode.c delete mode 100644 applications/main/subghz/protocols/megacode.h delete mode 100644 applications/main/subghz/protocols/nero_radio.c delete mode 100644 applications/main/subghz/protocols/nero_radio.h delete mode 100644 applications/main/subghz/protocols/nero_sketch.c delete mode 100644 applications/main/subghz/protocols/nero_sketch.h delete mode 100644 applications/main/subghz/protocols/nice_flo.c delete mode 100644 applications/main/subghz/protocols/nice_flo.h delete mode 100644 applications/main/subghz/protocols/nice_flor_s.c delete mode 100644 applications/main/subghz/protocols/nice_flor_s.h delete mode 100644 applications/main/subghz/protocols/phoenix_v2.c delete mode 100644 applications/main/subghz/protocols/phoenix_v2.h delete mode 100644 applications/main/subghz/protocols/power_smart.c delete mode 100644 applications/main/subghz/protocols/power_smart.h delete mode 100644 applications/main/subghz/protocols/princeton.c delete mode 100644 applications/main/subghz/protocols/princeton.h delete mode 100644 applications/main/subghz/protocols/princeton_for_testing.c delete mode 100644 applications/main/subghz/protocols/princeton_for_testing.h delete mode 100644 applications/main/subghz/protocols/protocol_items.c delete mode 100644 applications/main/subghz/protocols/protocol_items.h delete mode 100644 applications/main/subghz/protocols/raw.c delete mode 100644 applications/main/subghz/protocols/raw.h delete mode 100644 applications/main/subghz/protocols/scher_khan.c delete mode 100644 applications/main/subghz/protocols/scher_khan.h delete mode 100644 applications/main/subghz/protocols/secplus_v1.c delete mode 100644 applications/main/subghz/protocols/secplus_v1.h delete mode 100644 applications/main/subghz/protocols/secplus_v2.c delete mode 100644 applications/main/subghz/protocols/secplus_v2.h delete mode 100644 applications/main/subghz/protocols/smc5326.c delete mode 100644 applications/main/subghz/protocols/smc5326.h delete mode 100644 applications/main/subghz/protocols/somfy_keytis.c delete mode 100644 applications/main/subghz/protocols/somfy_keytis.h delete mode 100644 applications/main/subghz/protocols/somfy_telis.c delete mode 100644 applications/main/subghz/protocols/somfy_telis.h delete mode 100644 applications/main/subghz/protocols/star_line.c delete mode 100644 applications/main/subghz/protocols/star_line.h delete mode 100644 applications/main/subghz/receiver.c delete mode 100644 applications/main/subghz/receiver.h delete mode 100644 applications/main/subghz/registry.c delete mode 100644 applications/main/subghz/registry.h delete mode 100644 applications/main/subghz/subghz_file_encoder_worker.c delete mode 100644 applications/main/subghz/subghz_file_encoder_worker.h delete mode 100644 applications/main/subghz/subghz_keystore.c delete mode 100644 applications/main/subghz/subghz_keystore.h delete mode 100644 applications/main/subghz/subghz_setting.c delete mode 100644 applications/main/subghz/subghz_setting.h delete mode 100644 applications/main/subghz/subghz_tx_rx_worker.c delete mode 100644 applications/main/subghz/subghz_tx_rx_worker.h delete mode 100644 applications/main/subghz/subghz_worker.c delete mode 100644 applications/main/subghz/subghz_worker.h delete mode 100644 applications/main/subghz/transmitter.c delete mode 100644 applications/main/subghz/transmitter.h delete mode 100644 applications/main/subghz/types.h diff --git a/applications/main/subghz/SConscript b/applications/main/subghz/SConscript deleted file mode 100644 index 8fbec94ad..000000000 --- a/applications/main/subghz/SConscript +++ /dev/null @@ -1,32 +0,0 @@ -Import("env") - -env.Append( - CPPPATH=[ - "#/lib/subghz", - ], - SDK_HEADERS=[ - File("environment.h"), - File("receiver.h"), - File("subghz_worker.h"), - File("subghz_tx_rx_worker.h"), - File("transmitter.h"), - File("registry.h"), - File("protocols/protocol_items.h"), - File("protocols/raw.h"), - File("blocks/const.h"), - File("blocks/decoder.h"), - File("blocks/encoder.h"), - File("blocks/generic.h"), - File("blocks/math.h"), - File("subghz_setting.h"), - ], -) - -libenv = env.Clone(FW_LIB_NAME="subghz") -libenv.ApplyLibFlags() - -sources = libenv.GlobRecursive("*.c*") - -lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources) -libenv.Install("${LIB_DIST_DIR}", lib) -Return("lib") diff --git a/applications/main/subghz/blocks/const.c b/applications/main/subghz/blocks/const.c deleted file mode 100644 index 15719b2ac..000000000 --- a/applications/main/subghz/blocks/const.c +++ /dev/null @@ -1 +0,0 @@ -#include "const.h" diff --git a/applications/main/subghz/blocks/const.h b/applications/main/subghz/blocks/const.h deleted file mode 100644 index f32334e2f..000000000 --- a/applications/main/subghz/blocks/const.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - const uint16_t te_long; - const uint16_t te_short; - const uint16_t te_delta; - const uint8_t min_count_bit_for_found; -} SubGhzBlockConst; - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/decoder.c b/applications/main/subghz/blocks/decoder.c deleted file mode 100644 index f491c87bf..000000000 --- a/applications/main/subghz/blocks/decoder.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "decoder.h" - -#define TAG "SubGhzBlockDecoder" - -void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) { - decoder->decode_data = decoder->decode_data << 1 | bit; - decoder->decode_count_bit++; -} - -void subghz_protocol_blocks_add_to_128_bit( - SubGhzBlockDecoder* decoder, - uint8_t bit, - uint64_t* head_64_bit) { - if(++decoder->decode_count_bit > 64) { - (*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63); - } - decoder->decode_data = decoder->decode_data << 1 | bit; -} - -uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) { - uint8_t hash = 0; - uint8_t* p = (uint8_t*)&decoder->decode_data; - for(size_t i = 0; i < len; i++) { - hash ^= p[i]; - } - return hash; -} diff --git a/applications/main/subghz/blocks/decoder.h b/applications/main/subghz/blocks/decoder.h deleted file mode 100644 index a5e561e35..000000000 --- a/applications/main/subghz/blocks/decoder.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzBlockDecoder SubGhzBlockDecoder; - -struct SubGhzBlockDecoder { - uint32_t parser_step; - uint32_t te_last; - uint64_t decode_data; - uint8_t decode_count_bit; -}; - -/** - * Add data bit when decoding. - * @param decoder Pointer to a SubGhzBlockDecoder instance - * @param bit data, 1bit - */ -void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit); - -/** - * Add data to_128 bit when decoding. - * @param decoder Pointer to a SubGhzBlockDecoder instance - * @param head_64_bit Pointer to a head_64_bit - * @param bit data, 1bit - */ -void subghz_protocol_blocks_add_to_128_bit( - SubGhzBlockDecoder* decoder, - uint8_t bit, - uint64_t* head_64_bit); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param decoder Pointer to a SubGhzBlockDecoder instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/encoder.c b/applications/main/subghz/blocks/encoder.c deleted file mode 100644 index 49ec4f177..000000000 --- a/applications/main/subghz/blocks/encoder.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "encoder.h" -#include "math.h" -#include - -#include "furi.h" - -#define TAG "SubGhzBlockEncoder" - -void subghz_protocol_blocks_set_bit_array( - bool bit_value, - uint8_t data_array[], - size_t set_index_bit, - size_t max_size_array) { - furi_assert(set_index_bit < max_size_array * 8); - bit_write(data_array[set_index_bit >> 3], 7 - (set_index_bit & 0x7), bit_value); -} - -bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit) { - return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7)); -} - -size_t subghz_protocol_blocks_get_upload_from_bit_array( - uint8_t data_array[], - size_t count_bit_data_array, - LevelDuration* upload, - size_t max_size_upload, - uint32_t duration_bit, - SubGhzProtocolBlockAlignBit align_bit) { - size_t bias_bit = 0; - size_t size_upload = 0; - uint32_t duration = duration_bit; - - if(align_bit == SubGhzProtocolBlockAlignBitRight) { - if(count_bit_data_array & 0x7) { - bias_bit = 8 - (count_bit_data_array & 0x7); - } - } - size_t index_bit = bias_bit; - - bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++); - for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) { - if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) { - duration += duration_bit; - } else { - if(size_upload > max_size_upload) { - furi_crash("SubGhz: Encoder buffer overflow"); - } - upload[size_upload++] = level_duration_make( - subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); - last_bit = !last_bit; - duration = duration_bit; - } - index_bit++; - } - upload[size_upload++] = level_duration_make( - subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration); - return size_upload; -} diff --git a/applications/main/subghz/blocks/encoder.h b/applications/main/subghz/blocks/encoder.h deleted file mode 100644 index aeaa2add0..000000000 --- a/applications/main/subghz/blocks/encoder.h +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once - -#include -#include -#include - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - bool is_running; - size_t repeat; - size_t front; - size_t size_upload; - LevelDuration* upload; - -} SubGhzProtocolBlockEncoder; - -typedef enum { - SubGhzProtocolBlockAlignBitLeft, - SubGhzProtocolBlockAlignBitRight, -} SubGhzProtocolBlockAlignBit; - -/** - * Set data bit when encoding HEX array. - * @param bit_value The value of the bit to be set - * @param data_array Pointer to a HEX array - * @param set_index_bit Number set a bit in the array starting from the left - * @param max_size_array array size, check not to overflow - */ -void subghz_protocol_blocks_set_bit_array( - bool bit_value, - uint8_t data_array[], - size_t set_index_bit, - size_t max_size_array); - -/** - * Get data bit when encoding HEX array. - * @param data_array Pointer to a HEX array - * @param read_index_bit Number get a bit in the array starting from the left - * @return bool value bit - */ -bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit); - -/** - * Generating an upload from data. - * @param data_array Pointer to a HEX array - * @param count_bit_data_array How many bits in the array are processed - * @param upload Pointer to a LevelDuration - * @param max_size_upload upload size, check not to overflow - * @param duration_bit duration 1 bit - * @param align_bit alignment of useful bits in an array - */ -size_t subghz_protocol_blocks_get_upload_from_bit_array( - uint8_t data_array[], - size_t count_bit_data_array, - LevelDuration* upload, - size_t max_size_upload, - uint32_t duration_bit, - SubGhzProtocolBlockAlignBit align_bit); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/generic.c b/applications/main/subghz/blocks/generic.c deleted file mode 100644 index 3d59adc82..000000000 --- a/applications/main/subghz/blocks/generic.c +++ /dev/null @@ -1,120 +0,0 @@ -#include "generic.h" -#include -#include - -#define TAG "SubGhzBlockGeneric" - -void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) { - const char* preset_name_temp; - if(!strcmp(preset_name, "AM270")) { - preset_name_temp = "FuriHalSubGhzPresetOok270Async"; - } else if(!strcmp(preset_name, "AM650")) { - preset_name_temp = "FuriHalSubGhzPresetOok650Async"; - } else if(!strcmp(preset_name, "FM238")) { - preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async"; - } else if(!strcmp(preset_name, "FM476")) { - preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async"; - } else { - preset_name_temp = "FuriHalSubGhzPresetCustom"; - } - furi_string_set(preset_str, preset_name_temp); -} - -bool subghz_block_generic_serialize( - SubGhzBlockGeneric* instance, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(instance); - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_header_cstr( - flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - break; - } - - subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - flipper_format, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - break; - } - if(!flipper_format_write_hex( - flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - break; - } - } - if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - uint32_t temp = instance->data_count_bit; - if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { - FURI_LOG_E(TAG, "Unable to add Bit"); - break; - } - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->data >> (i * 8)) & 0xFF; - } - - if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - res = true; - } while(false); - furi_string_free(temp_str); - return res; -} - -bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) { - furi_assert(instance); - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - uint32_t temp_data = 0; - - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); - break; - } - instance->data_count_bit = (uint16_t)temp_data; - - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Key"); - break; - } - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->data = instance->data << 8 | key_data[i]; - } - - res = true; - } while(0); - - furi_string_free(temp_str); - - return res; -} diff --git a/applications/main/subghz/blocks/generic.h b/applications/main/subghz/blocks/generic.h deleted file mode 100644 index e69de8b4f..000000000 --- a/applications/main/subghz/blocks/generic.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include "../types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzBlockGeneric SubGhzBlockGeneric; - -struct SubGhzBlockGeneric { - const char* protocol_name; - uint64_t data; - uint64_t data_2; - uint32_t serial; - uint16_t data_count_bit; - uint8_t btn; - uint32_t cnt; - uint8_t cnt_2; - uint32_t seed; -}; - -/** - * Get name preset. - * @param preset_name name preset - * @param preset_str Output name preset - */ -void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str); - -/** - * Serialize data SubGhzBlockGeneric. - * @param instance Pointer to a SubGhzBlockGeneric instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_block_generic_serialize( - SubGhzBlockGeneric* instance, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzBlockGeneric. - * @param instance Pointer to a SubGhzBlockGeneric instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format); - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.c b/applications/main/subghz/blocks/math.c deleted file mode 100644 index 24202ad1c..000000000 --- a/applications/main/subghz/blocks/math.c +++ /dev/null @@ -1,244 +0,0 @@ -#include "math.h" - -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) { - uint64_t reverse_key = 0; - for(uint8_t i = 0; i < bit_count; i++) { - reverse_key = reverse_key << 1 | bit_read(key, i); - } - return reverse_key; -} - -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) { - uint8_t parity = 0; - for(uint8_t i = 0; i < bit_count; i++) { - parity += bit_read(key, i); - } - return parity & 0x01; -} - -uint8_t subghz_protocol_blocks_crc4( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = init << 4; // LSBs are unused - uint8_t poly = polynomial << 4; - uint8_t bit; - - while(size--) { - remainder ^= *message++; - for(bit = 0; bit < 8; bit++) { - if(remainder & 0x80) { - remainder = (remainder << 1) ^ poly; - } else { - remainder = (remainder << 1); - } - } - } - return remainder >> 4 & 0x0f; // discard the LSBs -} - -uint8_t subghz_protocol_blocks_crc7( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = init << 1; // LSB is unused - uint8_t poly = polynomial << 1; - - for(size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte]; - for(uint8_t bit = 0; bit < 8; ++bit) { - if(remainder & 0x80) { - remainder = (remainder << 1) ^ poly; - } else { - remainder = (remainder << 1); - } - } - } - return remainder >> 1 & 0x7f; // discard the LSB -} - -uint8_t subghz_protocol_blocks_crc8( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = init; - - for(size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte]; - for(uint8_t bit = 0; bit < 8; ++bit) { - if(remainder & 0x80) { - remainder = (remainder << 1) ^ polynomial; - } else { - remainder = (remainder << 1); - } - } - } - return remainder; -} - -uint8_t subghz_protocol_blocks_crc8le( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init) { - uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8); - polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8); - - for(size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte]; - for(uint8_t bit = 0; bit < 8; ++bit) { - if(remainder & 1) { - remainder = (remainder >> 1) ^ polynomial; - } else { - remainder = (remainder >> 1); - } - } - } - return remainder; -} - -uint16_t subghz_protocol_blocks_crc16lsb( - uint8_t const message[], - size_t size, - uint16_t polynomial, - uint16_t init) { - uint16_t remainder = init; - - for(size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte]; - for(uint8_t bit = 0; bit < 8; ++bit) { - if(remainder & 1) { - remainder = (remainder >> 1) ^ polynomial; - } else { - remainder = (remainder >> 1); - } - } - } - return remainder; -} - -uint16_t subghz_protocol_blocks_crc16( - uint8_t const message[], - size_t size, - uint16_t polynomial, - uint16_t init) { - uint16_t remainder = init; - - for(size_t byte = 0; byte < size; ++byte) { - remainder ^= message[byte] << 8; - for(uint8_t bit = 0; bit < 8; ++bit) { - if(remainder & 0x8000) { - remainder = (remainder << 1) ^ polynomial; - } else { - remainder = (remainder << 1); - } - } - } - return remainder; -} - -uint8_t subghz_protocol_blocks_lfsr_digest8( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key) { - uint8_t sum = 0; - for(size_t byte = 0; byte < size; ++byte) { - uint8_t data = message[byte]; - for(int i = 7; i >= 0; --i) { - // XOR key into sum if data bit is set - if((data >> i) & 1) sum ^= key; - - // roll the key right (actually the LSB is dropped here) - // and apply the gen (needs to include the dropped LSB as MSB) - if(key & 1) - key = (key >> 1) ^ gen; - else - key = (key >> 1); - } - } - return sum; -} - -uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key) { - uint8_t sum = 0; - // Process message from last byte to first byte (reflected) - for(int byte = size - 1; byte >= 0; --byte) { - uint8_t data = message[byte]; - // Process individual bits of each byte (reflected) - for(uint8_t i = 0; i < 8; ++i) { - // XOR key into sum if data bit is set - if((data >> i) & 1) { - sum ^= key; - } - - // roll the key left (actually the LSB is dropped here) - // and apply the gen (needs to include the dropped lsb as MSB) - if(key & 0x80) - key = (key << 1) ^ gen; - else - key = (key << 1); - } - } - return sum; -} - -uint16_t subghz_protocol_blocks_lfsr_digest16( - uint8_t const message[], - size_t size, - uint16_t gen, - uint16_t key) { - uint16_t sum = 0; - for(size_t byte = 0; byte < size; ++byte) { - uint8_t data = message[byte]; - for(int8_t i = 7; i >= 0; --i) { - // if data bit is set then xor with key - if((data >> i) & 1) sum ^= key; - - // roll the key right (actually the LSB is dropped here) - // and apply the gen (needs to include the dropped LSB as MSB) - if(key & 1) - key = (key >> 1) ^ gen; - else - key = (key >> 1); - } - } - return sum; -} - -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) { - uint32_t result = 0; - for(size_t i = 0; i < size; ++i) { - result += message[i]; - } - return (uint8_t)result; -} - -uint8_t subghz_protocol_blocks_parity8(uint8_t byte) { - byte ^= byte >> 4; - byte &= 0xf; - return (0x6996 >> byte) & 1; -} - -uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) { - uint8_t result = 0; - for(size_t i = 0; i < size; ++i) { - result ^= subghz_protocol_blocks_parity8(message[i]); - } - return result; -} - -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) { - uint8_t result = 0; - for(size_t i = 0; i < size; ++i) { - result ^= message[i]; - } - return result; -} \ No newline at end of file diff --git a/applications/main/subghz/blocks/math.h b/applications/main/subghz/blocks/math.h deleted file mode 100644 index dcea3da5f..000000000 --- a/applications/main/subghz/blocks/math.h +++ /dev/null @@ -1,222 +0,0 @@ -#pragma once - -#include -#include -#include - -#define bit_read(value, bit) (((value) >> (bit)) & 0x01) -#define bit_set(value, bit) \ - ({ \ - __typeof__(value) _one = (1); \ - (value) |= (_one << (bit)); \ - }) -#define bit_clear(value, bit) \ - ({ \ - __typeof__(value) _one = (1); \ - (value) &= ~(_one << (bit)); \ - }) -#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit)) -#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) - -#ifdef __cplusplus -extern "C" { -#endif - -/** Flip the data bitwise - * - * @param key In data - * @param bit_count number of data bits - * - * @return Reverse data - */ -uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count); - -/** Get parity the data bitwise - * - * @param key In data - * @param bit_count number of data bits - * - * @return parity - */ -uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count); - -/** CRC-4 - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * - * @return CRC value - */ -uint8_t subghz_protocol_blocks_crc4( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init); - -/** CRC-7 - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * - * @return CRC value - */ -uint8_t subghz_protocol_blocks_crc7( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init); - -/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 + - * x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal - * bit-by-bit parity XOR) - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one) - * @param init starting crc value - * - * @return CRC value - */ -uint8_t subghz_protocol_blocks_crc8( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init); - -/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are - * reflected, i.e. least significant bit is shifted in first - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * - * @return CRC value - */ -uint8_t subghz_protocol_blocks_crc8le( - uint8_t const message[], - size_t size, - uint8_t polynomial, - uint8_t init); - -/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is - * shifted in first. Note that poly and init already need to be reflected - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * - * @return CRC value - */ -uint16_t subghz_protocol_blocks_crc16lsb( - uint8_t const message[], - size_t size, - uint16_t polynomial, - uint16_t init); - -/** CRC-16 - * - * @param message array of bytes to check - * @param size number of bytes in message - * @param polynomial CRC polynomial - * @param init starting crc value - * - * @return CRC value - */ -uint16_t subghz_protocol_blocks_crc16( - uint8_t const message[], - size_t size, - uint16_t polynomial, - uint16_t init); - -/** Digest-8 by "LFSR-based Toeplitz hash" - * - * @param message bytes of message data - * @param size number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the - * LFSR is rolling - * @param key initial key - * - * @return digest value - */ -uint8_t subghz_protocol_blocks_lfsr_digest8( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key); - -/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect - * - * @param message bytes of message data - * @param size number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the - * LFSR is rolling - * @param key initial key - * - * @return digest value - */ -uint8_t subghz_protocol_blocks_lfsr_digest8_reflect( - uint8_t const message[], - size_t size, - uint8_t gen, - uint8_t key); - -/** Digest-16 by "LFSR-based Toeplitz hash" - * - * @param message bytes of message data - * @param size number of bytes to digest - * @param gen key stream generator, needs to includes the MSB if the - * LFSR is rolling - * @param key initial key - * - * @return digest value - */ -uint16_t subghz_protocol_blocks_lfsr_digest16( - uint8_t const message[], - size_t size, - uint16_t gen, - uint16_t key); - -/** Compute Addition of a number of bytes - * - * @param message bytes of message data - * @param size number of bytes to sum - * - * @return summation value - */ -uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size); - -/** Compute bit parity of a single byte (8 bits) - * - * @param byte single byte to check - * - * @return 1 odd parity, 0 even parity - */ -uint8_t subghz_protocol_blocks_parity8(uint8_t byte); - -/** Compute bit parity of a number of bytes - * - * @param message bytes of message data - * @param size number of bytes to sum - * - * @return 1 odd parity, 0 even parity - */ -uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size); - -/** Compute XOR (byte-wide parity) of a number of bytes - * - * @param message bytes of message data - * @param size number of bytes to sum - * - * @return summation value, per bit-position 1 odd parity, 0 even parity - */ -uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/environment.c b/applications/main/subghz/environment.c deleted file mode 100644 index b39b259d4..000000000 --- a/applications/main/subghz/environment.c +++ /dev/null @@ -1,116 +0,0 @@ -#include "environment.h" -#include "registry.h" - -struct SubGhzEnvironment { - SubGhzKeystore* keystore; - const SubGhzProtocolRegistry* protocol_registry; - const char* came_atomo_rainbow_table_file_name; - const char* nice_flor_s_rainbow_table_file_name; - const char* alutech_at_4n_rainbow_table_file_name; -}; - -SubGhzEnvironment* subghz_environment_alloc() { - SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment)); - - instance->keystore = subghz_keystore_alloc(); - instance->protocol_registry = NULL; - instance->came_atomo_rainbow_table_file_name = NULL; - instance->nice_flor_s_rainbow_table_file_name = NULL; - - return instance; -} - -void subghz_environment_free(SubGhzEnvironment* instance) { - furi_assert(instance); - - instance->protocol_registry = NULL; - instance->came_atomo_rainbow_table_file_name = NULL; - instance->nice_flor_s_rainbow_table_file_name = NULL; - subghz_keystore_free(instance->keystore); - - free(instance); -} - -bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename) { - furi_assert(instance); - - return subghz_keystore_load(instance->keystore, filename); -} - -SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance) { - furi_assert(instance); - - return instance->keystore; -} - -void subghz_environment_set_came_atomo_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename) { - furi_assert(instance); - - instance->came_atomo_rainbow_table_file_name = filename; -} - -const char* - subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance) { - furi_assert(instance); - - return instance->came_atomo_rainbow_table_file_name; -} - -void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename) { - furi_assert(instance); - - instance->alutech_at_4n_rainbow_table_file_name = filename; -} - -const char* - subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) { - furi_assert(instance); - - return instance->alutech_at_4n_rainbow_table_file_name; -} - -void subghz_environment_set_nice_flor_s_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename) { - furi_assert(instance); - - instance->nice_flor_s_rainbow_table_file_name = filename; -} - -const char* - subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance) { - furi_assert(instance); - - return instance->nice_flor_s_rainbow_table_file_name; -} - -void subghz_environment_set_protocol_registry( - SubGhzEnvironment* instance, - void* protocol_registry_items) { - furi_assert(instance); - const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items; - instance->protocol_registry = protocol_registry; -} - -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) { - furi_assert(instance); - furi_assert(instance->protocol_registry); - return (void*)instance->protocol_registry; -} - -const char* - subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) { - furi_assert(instance); - furi_assert(instance->protocol_registry); - const SubGhzProtocol* protocol = - subghz_protocol_registry_get_by_index(instance->protocol_registry, idx); - if(protocol != NULL) { - return protocol->name; - } else { - return NULL; - } -} \ No newline at end of file diff --git a/applications/main/subghz/environment.h b/applications/main/subghz/environment.h deleted file mode 100644 index 7bd38ba2f..000000000 --- a/applications/main/subghz/environment.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include - -#include "subghz_keystore.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzEnvironment SubGhzEnvironment; - -/** - * Allocate SubGhzEnvironment. - * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance - */ -SubGhzEnvironment* subghz_environment_alloc(); - -/** - * Free SubGhzEnvironment. - * @param instance Pointer to a SubGhzEnvironment instance - */ -void subghz_environment_free(SubGhzEnvironment* instance); - -/** - * Downloading the manufacture key file. - * @param instance Pointer to a SubGhzEnvironment instance - * @param filename Full path to the file - * @return true On success - */ -bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename); - -/** - * Get pointer to a SubGhzKeystore* instance. - * @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance - */ -SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance); - -/** - * Set filename to work with Came Atomo. - * @param instance Pointer to a SubGhzEnvironment instance - * @param filename Full path to the file - */ -void subghz_environment_set_came_atomo_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename); - -/** - * Get filename to work with Came Atomo. - * @param instance Pointer to a SubGhzEnvironment instance - * @return Full path to the file - */ -const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance); - -/** - * Set filename to work with Alutech at-4n. - * @param instance Pointer to a SubGhzEnvironment instance - * @param filename Full path to the file - */ -void subghz_environment_set_alutech_at_4n_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename); - -/** - * Get filename to work with Alutech at-4n. - * @param instance Pointer to a SubGhzEnvironment instance - * @return Full path to the file - */ -const char* - subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance); - -/** - * Set filename to work with Nice Flor-S. - * @param instance Pointer to a SubGhzEnvironment instance - * @param filename Full path to the file - */ -void subghz_environment_set_nice_flor_s_rainbow_table_file_name( - SubGhzEnvironment* instance, - const char* filename); - -/** - * Get filename to work with Nice Flor-S. - * @param instance Pointer to a SubGhzEnvironment instance - * @return Full path to the file - */ -const char* - subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance); - -/** - * Set list of protocols to work. - * @param instance Pointer to a SubGhzEnvironment instance - * @param protocol_registry_items Pointer to a SubGhzProtocolRegistry - */ -void subghz_environment_set_protocol_registry( - SubGhzEnvironment* instance, - void* protocol_registry_items); - -/** - * Get list of protocols to work. - * @param instance Pointer to a SubGhzEnvironment instance - * @return Pointer to a SubGhzProtocolRegistry - */ -void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance); - -/** - * Get list of protocols names. - * @param instance Pointer to a SubGhzEnvironment instance - * @param idx index protocols - * @return Pointer to a SubGhzProtocolRegistry - */ -const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/protocols/alutech_at_4n.c b/applications/main/subghz/protocols/alutech_at_4n.c deleted file mode 100644 index 6bcf9f25d..000000000 --- a/applications/main/subghz/protocols/alutech_at_4n.c +++ /dev/null @@ -1,455 +0,0 @@ -#include "alutech_at_4n.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocoAlutech_at_4n" - -#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF - -static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = { - .te_short = 400, - .te_long = 800, - .te_delta = 140, - .min_count_bit_for_found = 72, -}; - -struct SubGhzProtocolDecoderAlutech_at_4n { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint64_t data; - uint32_t crc; - uint16_t header_count; - - const char* alutech_at_4n_rainbow_table_file_name; -}; - -struct SubGhzProtocolEncoderAlutech_at_4n { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - Alutech_at_4nDecoderStepReset = 0, - Alutech_at_4nDecoderStepCheckPreambula, - Alutech_at_4nDecoderStepSaveDuration, - Alutech_at_4nDecoderStepCheckDuration, -} Alutech_at_4nDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = { - .alloc = subghz_protocol_decoder_alutech_at_4n_alloc, - .free = subghz_protocol_decoder_alutech_at_4n_free, - - .feed = subghz_protocol_decoder_alutech_at_4n_feed, - .reset = subghz_protocol_decoder_alutech_at_4n_reset, - - .get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data, - .serialize = subghz_protocol_decoder_alutech_at_4n_serialize, - .deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize, - .get_string = subghz_protocol_decoder_alutech_at_4n_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol subghz_protocol_alutech_at_4n = { - .name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable, - - .decoder = &subghz_protocol_alutech_at_4n_decoder, - .encoder = &subghz_protocol_alutech_at_4n_encoder, -}; - -/** - * Read bytes from rainbow table - * @param file_name Full path to rainbow table the file - * @param number_alutech_at_4n_magic_data number in the array - * @return alutech_at_4n_magic_data - */ -static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file( - const char* file_name, - uint8_t number_alutech_at_4n_magic_data) { - if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; - - uint8_t buffer[sizeof(uint32_t)] = {0}; - uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t); - uint32_t alutech_at_4n_magic_data = 0; - - if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) { - for(size_t i = 0; i < sizeof(uint32_t); i++) { - alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i]; - } - } else { - alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE; - } - return alutech_at_4n_magic_data; -} - -static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) { - uint8_t* p = (uint8_t*)&data; - uint8_t crc = 0xff; - for(uint8_t y = 0; y < 8; y++) { - crc = crc ^ p[y]; - for(uint8_t i = 0; i < 8; i++) { - if((crc & 0x80) != 0) { - crc <<= 1; - crc ^= 0x31; - } else { - crc <<= 1; - } - } - } - return crc; -} - -static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) { - uint8_t crc = data; - for(uint8_t i = 0; i < 8; i++) { - if((crc & 0x80) != 0) { - crc <<= 1; - crc ^= 0x31; - } else { - crc <<= 1; - } - } - return ~crc; -} - -static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) { - uint8_t* p = (uint8_t*)&data; - uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; - uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; - uint32_t data3 = 0; - uint32_t magic_data[] = { - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0), - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3), - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), - subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)}; - - uint32_t i = magic_data[0]; - do { - data2 = data2 - - ((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i))); - data3 = data2 + i; - i += magic_data[3]; - data1 = - data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3)); - } while(i != 0); - - p[0] = (uint8_t)(data1 >> 24); - p[1] = (uint8_t)(data1 >> 16); - p[3] = (uint8_t)data1; - p[4] = (uint8_t)(data2 >> 24); - p[5] = (uint8_t)(data2 >> 16); - p[2] = (uint8_t)(data1 >> 8); - p[6] = (uint8_t)(data2 >> 8); - p[7] = (uint8_t)data2; - - return data; -} - -// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) { -// uint8_t* p = (uint8_t*)&data; -// uint32_t data1 = 0; -// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3]; -// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7]; -// uint32_t magic_data[] = { -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2), -// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)}; - -// do { -// data1 = data1 + magic_data[0]; -// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^ -// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3))); -// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^ -// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2))); -// } while(data1 != magic_data[5]); -// p[0] = (uint8_t)(data2 >> 24); -// p[1] = (uint8_t)(data2 >> 16); -// p[3] = (uint8_t)data2; -// p[4] = (uint8_t)(data3 >> 24); -// p[5] = (uint8_t)(data3 >> 16); -// p[2] = (uint8_t)(data2 >> 8); -// p[6] = (uint8_t)(data3 >> 8); -// p[7] = (uint8_t)data3; - -// return data; -// } - -void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolDecoderAlutech_at_4n* instance = - malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n)); - instance->base.protocol = &subghz_protocol_alutech_at_4n; - instance->generic.protocol_name = instance->base.protocol->name; - instance->alutech_at_4n_rainbow_table_file_name = - subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment); - if(instance->alutech_at_4n_rainbow_table_file_name) { - FURI_LOG_I( - TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name); - } - return instance; -} - -void subghz_protocol_decoder_alutech_at_4n_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - instance->alutech_at_4n_rainbow_table_file_name = NULL; - free(instance); -} - -void subghz_protocol_decoder_alutech_at_4n_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; -} - -void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - - switch(instance->decoder.parser_step) { - case Alutech_at_4nDecoderStepReset: - if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < - subghz_protocol_alutech_at_4n_const.te_delta) { - instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula; - instance->header_count++; - } - break; - case Alutech_at_4nDecoderStepCheckPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < - subghz_protocol_alutech_at_4n_const.te_delta)) { - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; - break; - } - if((instance->header_count > 2) && - (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) < - subghz_protocol_alutech_at_4n_const.te_delta * 10)) { - // Found header - instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; - instance->header_count = 0; - } - break; - case Alutech_at_4nDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration; - } - break; - case Alutech_at_4nDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 + - subghz_protocol_alutech_at_4n_const.te_delta)) { - //add last bit - if(DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < - subghz_protocol_alutech_at_4n_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < - subghz_protocol_alutech_at_4n_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } - - // Found end TX - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { - if(instance->generic.data != instance->data) { - instance->generic.data = instance->data; - - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - instance->crc = instance->decoder.decode_data; - - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - } - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) < - subghz_protocol_alutech_at_4n_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) < - subghz_protocol_alutech_at_4n_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - if(instance->decoder.decode_count_bit == 64) { - instance->data = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } - instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) < - subghz_protocol_alutech_at_4n_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) < - subghz_protocol_alutech_at_4n_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - if(instance->decoder.decode_count_bit == 64) { - instance->data = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } - instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; - instance->header_count = 0; - } - } else { - instance->decoder.parser_step = Alutech_at_4nDecoderStepReset; - instance->header_count = 0; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param file_name Full path to rainbow table the file - */ -static void subghz_protocol_alutech_at_4n_remote_controller( - SubGhzBlockGeneric* instance, - uint8_t crc, - const char* file_name) { - /** - * Message format 72bit LSB first - * data crc - * XXXXXXXXXXXXXXXX CC - * - * For analysis, you need to turn the package MSB - * in decoded messages format - * - * crc1 serial cnt key - * cc SSSSSSSS XXxx BB - * - * crc1 is calculated from the lower part of cnt - * key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44 - * - */ - - bool status = false; - uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64); - crc = subghz_protocol_blocks_reverse_key(crc, 8); - - if(crc == subghz_protocol_alutech_at_4n_crc(data)) { - data = subghz_protocol_alutech_at_4n_decrypt(data, file_name); - status = true; - } - - if(status && ((uint8_t)(data >> 56) == - subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) { - instance->btn = (uint8_t)data & 0xFF; - instance->cnt = (uint16_t)(data >> 8) & 0xFFFF; - instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF; - } - - if(!status) { - instance->btn = 0; - instance->cnt = 0; - instance->serial = 0; - } -} - -uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - return (uint8_t)instance->crc; -} - -bool subghz_protocol_decoder_alutech_at_4n_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) { - FURI_LOG_E(TAG, "Unable to add CRC"); - res = false; - } - return res; - - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_alutech_at_4n_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) { - FURI_LOG_E(TAG, "Missing CRC"); - break; - } - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderAlutech_at_4n* instance = context; - subghz_protocol_alutech_at_4n_remote_controller( - &instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %d\r\n" - "Key:0x%08lX%08lX%02X\r\n" - "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%03lX\r\n", - - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - (uint8_t)instance->crc, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); -} diff --git a/applications/main/subghz/protocols/alutech_at_4n.h b/applications/main/subghz/protocols/alutech_at_4n.h deleted file mode 100644 index 38bac3ea6..000000000 --- a/applications/main/subghz/protocols/alutech_at_4n.h +++ /dev/null @@ -1,74 +0,0 @@ -#pragma once -#include "base.h" - -#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n" - -typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n; -typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n; - -extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder; -extern const SubGhzProtocol subghz_protocol_alutech_at_4n; - -/** - * Allocate SubGhzProtocolDecoderAlutech_at_4n. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - */ -void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderAlutech_at_4n. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - */ -void subghz_protocol_decoder_alutech_at_4n_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderAlutech_at_4n. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - */ -void subghz_protocol_decoder_alutech_at_4n_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderAlutech_at_4n. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_alutech_at_4n_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderAlutech_at_4n. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_alutech_at_4n_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance - * @param output Resulting text - */ -void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ansonic.c b/applications/main/subghz/protocols/ansonic.c deleted file mode 100644 index 81b196e36..000000000 --- a/applications/main/subghz/protocols/ansonic.c +++ /dev/null @@ -1,346 +0,0 @@ -#include "ansonic.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolAnsonic" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" -#define CNT_TO_DIP(dip) \ - (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ - (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ - (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ - (dip & 0x0008 ? '1' : '0') - -static const SubGhzBlockConst subghz_protocol_ansonic_const = { - .te_short = 555, - .te_long = 1111, - .te_delta = 120, - .min_count_bit_for_found = 12, -}; - -struct SubGhzProtocolDecoderAnsonic { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderAnsonic { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - AnsonicDecoderStepReset = 0, - AnsonicDecoderStepFoundStartBit, - AnsonicDecoderStepSaveDuration, - AnsonicDecoderStepCheckDuration, -} AnsonicDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = { - .alloc = subghz_protocol_decoder_ansonic_alloc, - .free = subghz_protocol_decoder_ansonic_free, - - .feed = subghz_protocol_decoder_ansonic_feed, - .reset = subghz_protocol_decoder_ansonic_reset, - - .get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data, - .serialize = subghz_protocol_decoder_ansonic_serialize, - .deserialize = subghz_protocol_decoder_ansonic_deserialize, - .get_string = subghz_protocol_decoder_ansonic_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = { - .alloc = subghz_protocol_encoder_ansonic_alloc, - .free = subghz_protocol_encoder_ansonic_free, - - .deserialize = subghz_protocol_encoder_ansonic_deserialize, - .stop = subghz_protocol_encoder_ansonic_stop, - .yield = subghz_protocol_encoder_ansonic_yield, -}; - -const SubGhzProtocol subghz_protocol_ansonic = { - .name = SUBGHZ_PROTOCOL_ANSONIC_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_ansonic_decoder, - .encoder = &subghz_protocol_ansonic_encoder, -}; - -void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic)); - - instance->base.protocol = &subghz_protocol_ansonic; - 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_ansonic_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderAnsonic* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance - * @return true On success - */ -static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short); - } - } - return true; -} - -bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderAnsonic* 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_ansonic_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); - - if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_ansonic_stop(void* context) { - SubGhzProtocolEncoderAnsonic* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) { - SubGhzProtocolEncoderAnsonic* 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_ansonic_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic)); - instance->base.protocol = &subghz_protocol_ansonic; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_ansonic_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - free(instance); -} - -void subghz_protocol_decoder_ansonic_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - instance->decoder.parser_step = AnsonicDecoderStepReset; -} - -void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - - switch(instance->decoder.parser_step) { - case AnsonicDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) < - subghz_protocol_ansonic_const.te_delta * 35)) { - //Found header Ansonic - instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; - } - break; - case AnsonicDecoderStepFoundStartBit: - if(!level) { - break; - } else if( - DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < - subghz_protocol_ansonic_const.te_delta) { - //Found start bit Ansonic - instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = AnsonicDecoderStepReset; - } - break; - case AnsonicDecoderStepSaveDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) { - instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_ansonic_const.min_count_bit_for_found) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - - 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); - } - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = AnsonicDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = AnsonicDecoderStepReset; - } - break; - case AnsonicDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) < - subghz_protocol_ansonic_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) < - subghz_protocol_ansonic_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) < - subghz_protocol_ansonic_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) < - subghz_protocol_ansonic_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = AnsonicDecoderStepSaveDuration; - } else - instance->decoder.parser_step = AnsonicDecoderStepReset; - } else { - instance->decoder.parser_step = AnsonicDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) { - /* - * 12345678(10) k 9 - * AAA => 10101010 1 01 0 - * - * 1...10 - DIP - * k- KEY - */ - instance->cnt = instance->data & 0xFFF; - instance->btn = ((instance->data >> 1) & 0x3); -} - -uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_ansonic_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_ansonic_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_ansonic_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderAnsonic* instance = context; - subghz_protocol_ansonic_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%03lX\r\n" - "Btn:%X\r\n" - "DIP:" DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.btn, - CNT_TO_DIP(instance->generic.cnt)); -} diff --git a/applications/main/subghz/protocols/ansonic.h b/applications/main/subghz/protocols/ansonic.h deleted file mode 100644 index 0170a6048..000000000 --- a/applications/main/subghz/protocols/ansonic.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic" - -typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic; -typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic; - -extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder; -extern const SubGhzProtocol subghz_protocol_ansonic; - -/** - * Allocate SubGhzProtocolEncoderAnsonic. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance - */ -void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderAnsonic. - * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance - */ -void subghz_protocol_encoder_ansonic_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance - */ -void subghz_protocol_encoder_ansonic_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderAnsonic instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_ansonic_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderAnsonic. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance - */ -void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderAnsonic. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - */ -void subghz_protocol_decoder_ansonic_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderAnsonic. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - */ -void subghz_protocol_decoder_ansonic_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderAnsonic. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_ansonic_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderAnsonic. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderAnsonic instance - * @param output Resulting text - */ -void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/base.c b/applications/main/subghz/protocols/base.c deleted file mode 100644 index 36f33b9a5..000000000 --- a/applications/main/subghz/protocols/base.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "base.h" -#include "registry.h" - -void subghz_protocol_decoder_base_set_decoder_callback( - SubGhzProtocolDecoderBase* decoder_base, - SubGhzProtocolDecoderBaseRxCallback callback, - void* context) { - decoder_base->callback = callback; - decoder_base->context = context; -} - -bool subghz_protocol_decoder_base_get_string( - SubGhzProtocolDecoderBase* decoder_base, - FuriString* output) { - bool status = false; - - if(decoder_base->protocol && decoder_base->protocol->decoder && - decoder_base->protocol->decoder->get_string) { - decoder_base->protocol->decoder->get_string(decoder_base, output); - status = true; - } - - return status; -} - -bool subghz_protocol_decoder_base_serialize( - SubGhzProtocolDecoderBase* decoder_base, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - bool status = false; - - if(decoder_base->protocol && decoder_base->protocol->decoder && - decoder_base->protocol->decoder->serialize) { - status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset); - } - - return status; -} - -bool subghz_protocol_decoder_base_deserialize( - SubGhzProtocolDecoderBase* decoder_base, - FlipperFormat* flipper_format) { - bool status = false; - - if(decoder_base->protocol && decoder_base->protocol->decoder && - decoder_base->protocol->decoder->deserialize) { - status = decoder_base->protocol->decoder->deserialize(decoder_base, flipper_format); - } - - return status; -} - -uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) { - uint8_t hash = 0; - - if(decoder_base->protocol && decoder_base->protocol->decoder && - decoder_base->protocol->decoder->get_hash_data) { - hash = decoder_base->protocol->decoder->get_hash_data(decoder_base); - } - - return hash; -} diff --git a/applications/main/subghz/protocols/base.h b/applications/main/subghz/protocols/base.h deleted file mode 100644 index 1f3d3e1be..000000000 --- a/applications/main/subghz/protocols/base.h +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include "../types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase; - -typedef void ( - *SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context); - -typedef void (*SubGhzProtocolDecoderBaseSerialize)( - SubGhzProtocolDecoderBase* decoder_base, - FuriString* output); - -struct SubGhzProtocolDecoderBase { - // Decoder general section - const SubGhzProtocol* protocol; - - // Callback section - SubGhzProtocolDecoderBaseRxCallback callback; - void* context; -}; - -/** - * Set a callback upon completion of successful decoding of one of the protocols. - * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance - * @param callback Callback, SubGhzProtocolDecoderBaseRxCallback - * @param context Context - */ -void subghz_protocol_decoder_base_set_decoder_callback( - SubGhzProtocolDecoderBase* decoder_base, - SubGhzProtocolDecoderBaseRxCallback callback, - void* context); - -/** - * Getting a textual representation of the received data. - * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance - * @param output Resulting text - */ -bool subghz_protocol_decoder_base_get_string( - SubGhzProtocolDecoderBase* decoder_base, - FuriString* output); - -/** - * Serialize data SubGhzProtocolDecoderBase. - * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_base_serialize( - SubGhzProtocolDecoderBase* decoder_base, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderBase. - * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_base_deserialize( - SubGhzProtocolDecoderBase* decoder_base, - FlipperFormat* flipper_format); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base); - -// Encoder Base -typedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase; - -struct SubGhzProtocolEncoderBase { - // Decoder general section - const SubGhzProtocol* protocol; - - // Callback section -}; - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/protocols/bett.c b/applications/main/subghz/protocols/bett.c deleted file mode 100644 index 644d80fd8..000000000 --- a/applications/main/subghz/protocols/bett.c +++ /dev/null @@ -1,342 +0,0 @@ -#include "bett.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 "SubGhzProtocolBETT" - -#define DIP_P 0b11 //(+) -#define DIP_O 0b10 //(0) -#define DIP_N 0b00 //(-) - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c" -#define SHOW_DIP_P(dip, check_dip) \ - ((((dip >> 0x8) >> 0x8) == 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_bett_const = { - .te_short = 340, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 18, -}; - -struct SubGhzProtocolDecoderBETT { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderBETT { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - BETTDecoderStepReset = 0, - BETTDecoderStepSaveDuration, - BETTDecoderStepCheckDuration, -} BETTDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_bett_decoder = { - .alloc = subghz_protocol_decoder_bett_alloc, - .free = subghz_protocol_decoder_bett_free, - - .feed = subghz_protocol_decoder_bett_feed, - .reset = subghz_protocol_decoder_bett_reset, - - .get_hash_data = subghz_protocol_decoder_bett_get_hash_data, - .serialize = subghz_protocol_decoder_bett_serialize, - .deserialize = subghz_protocol_decoder_bett_deserialize, - .get_string = subghz_protocol_decoder_bett_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_bett_encoder = { - .alloc = subghz_protocol_encoder_bett_alloc, - .free = subghz_protocol_encoder_bett_free, - - .deserialize = subghz_protocol_encoder_bett_deserialize, - .stop = subghz_protocol_encoder_bett_stop, - .yield = subghz_protocol_encoder_bett_yield, -}; - -const SubGhzProtocol subghz_protocol_bett = { - .name = SUBGHZ_PROTOCOL_BETT_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_bett_decoder, - .encoder = &subghz_protocol_bett_encoder, -}; - -void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT)); - - instance->base.protocol = &subghz_protocol_bett; - 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_bett_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderBETT* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderBETT instance - * @return true On success - */ -static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* 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_bett_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_bett_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_bett_const.te_long); - instance->encoder.upload[index++] = level_duration_make( - false, - (uint32_t)subghz_protocol_bett_const.te_short + - subghz_protocol_bett_const.te_long * 7); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, - (uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7); - } - return true; -} - -bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderBETT* 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_bett_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); - - if(!subghz_protocol_encoder_bett_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_bett_stop(void* context) { - SubGhzProtocolEncoderBETT* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_bett_yield(void* context) { - SubGhzProtocolEncoderBETT* 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_bett_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT)); - instance->base.protocol = &subghz_protocol_bett; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_bett_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - free(instance); -} - -void subghz_protocol_decoder_bett_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - instance->decoder.parser_step = BETTDecoderStepReset; -} - -void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - - switch(instance->decoder.parser_step) { - case BETTDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < - (subghz_protocol_bett_const.te_delta * 15))) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = BETTDecoderStepCheckDuration; - } - break; - case BETTDecoderStepSaveDuration: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) < - (subghz_protocol_bett_const.te_delta * 15)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_bett_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); - } else { - instance->decoder.parser_step = BETTDecoderStepReset; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < - subghz_protocol_bett_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < - subghz_protocol_bett_const.te_delta * 3)) { - instance->decoder.parser_step = BETTDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = BETTDecoderStepReset; - } - } - } - break; - case BETTDecoderStepCheckDuration: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) < - subghz_protocol_bett_const.te_delta * 3) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = BETTDecoderStepSaveDuration; - } else if( - DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) < - subghz_protocol_bett_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = BETTDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = BETTDecoderStepReset; - } - } else { - instance->decoder.parser_step = BETTDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_bett_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_bett_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_bett_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderBETT* instance = context; - uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%05lX\r\n" - " +: " DIP_PATTERN "\r\n" - " o: " DIP_PATTERN "\r\n" - " -: " DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - data, - SHOW_DIP_P(data, DIP_P), - SHOW_DIP_P(data, DIP_O), - SHOW_DIP_P(data, DIP_N)); -} diff --git a/applications/main/subghz/protocols/bett.h b/applications/main/subghz/protocols/bett.h deleted file mode 100644 index c0ce0b7f4..000000000 --- a/applications/main/subghz/protocols/bett.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_BETT_NAME "BETT" - -typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT; -typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT; - -extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder; -extern const SubGhzProtocol subghz_protocol_bett; - -/** - * Allocate SubGhzProtocolEncoderBETT. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance - */ -void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderBETT. - * @param context Pointer to a SubGhzProtocolEncoderBETT instance - */ -void subghz_protocol_encoder_bett_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderBETT instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderBETT instance - */ -void subghz_protocol_encoder_bett_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderBETT instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_bett_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderBETT. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance - */ -void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderBETT. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - */ -void subghz_protocol_decoder_bett_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderBETT. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - */ -void subghz_protocol_decoder_bett_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderBETT. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_bett_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderBETT. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderBETT instance - * @param output Resulting text - */ -void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/bin_raw.c b/applications/main/subghz/protocols/bin_raw.c deleted file mode 100644 index 67e0467ee..000000000 --- a/applications/main/subghz/protocols/bin_raw.c +++ /dev/null @@ -1,1120 +0,0 @@ -#include "bin_raw.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" -#include -#include -#include - -#define TAG "SubGhzProtocolBinRAW" - -//change very carefully, RAM ends at the most inopportune moment -#define BIN_RAW_BUF_RAW_SIZE 2048 -#define BIN_RAW_BUF_DATA_SIZE 512 - -#define BIN_RAW_THRESHOLD_RSSI -85.0f -#define BIN_RAW_DELTA_RSSI 7.0f -#define BIN_RAW_SEARCH_CLASSES 20 -#define BIN_RAW_TE_MIN_COUNT 40 -#define BIN_RAW_BUF_MIN_DATA_COUNT 128 -#define BIN_RAW_MAX_MARKUP_COUNT 20 - -//#define BIN_RAW_DEBUG - -#ifdef BIN_RAW_DEBUG -#define bin_raw_debug(...) FURI_LOG_RAW_D(__VA_ARGS__) -#define bin_raw_debug_tag(tag, ...) \ - FURI_LOG_RAW_D("\033[0;32m[" tag "]\033[0m "); \ - FURI_LOG_RAW_D(__VA_ARGS__) -#else -#define bin_raw_debug(...) -#define bin_raw_debug_tag(...) -#endif - -static const SubGhzBlockConst subghz_protocol_bin_raw_const = { - .te_short = 30, - .te_long = 65000, - .te_delta = 0, - .min_count_bit_for_found = 0, -}; - -typedef enum { - BinRAWDecoderStepReset = 0, - BinRAWDecoderStepWrite, - BinRAWDecoderStepBufFull, - BinRAWDecoderStepNoParse, -} BinRAWDecoderStep; - -typedef enum { - BinRAWTypeUnknown = 0, - BinRAWTypeNoGap, - BinRAWTypeGap, - BinRAWTypeGapRecurring, - BinRAWTypeGapRolling, - BinRAWTypeGapUnknown, -} BinRAWType; - -struct BinRAW_Markup { - uint16_t byte_bias; - uint16_t bit_count; -}; -typedef struct BinRAW_Markup BinRAW_Markup; - -struct SubGhzProtocolDecoderBinRAW { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - int32_t* data_raw; - uint8_t* data; - BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; - size_t data_raw_ind; - uint32_t te; - float adaptive_threshold_rssi; -}; - -struct SubGhzProtocolEncoderBinRAW { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint8_t* data; - BinRAW_Markup data_markup[BIN_RAW_MAX_MARKUP_COUNT]; - uint32_t te; -}; - -const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder = { - .alloc = subghz_protocol_decoder_bin_raw_alloc, - .free = subghz_protocol_decoder_bin_raw_free, - - .feed = subghz_protocol_decoder_bin_raw_feed, - .reset = subghz_protocol_decoder_bin_raw_reset, - - .get_hash_data = subghz_protocol_decoder_bin_raw_get_hash_data, - .serialize = subghz_protocol_decoder_bin_raw_serialize, - .deserialize = subghz_protocol_decoder_bin_raw_deserialize, - .get_string = subghz_protocol_decoder_bin_raw_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder = { - .alloc = subghz_protocol_encoder_bin_raw_alloc, - .free = subghz_protocol_encoder_bin_raw_free, - - .deserialize = subghz_protocol_encoder_bin_raw_deserialize, - .stop = subghz_protocol_encoder_bin_raw_stop, - .yield = subghz_protocol_encoder_bin_raw_yield, -}; - -const SubGhzProtocol subghz_protocol_bin_raw = { - .name = SUBGHZ_PROTOCOL_BIN_RAW_NAME, - .type = SubGhzProtocolTypeBinRAW, -#ifdef BIN_RAW_DEBUG - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, -#else - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_BinRAW | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, -#endif - .decoder = &subghz_protocol_bin_raw_decoder, - .encoder = &subghz_protocol_bin_raw_encoder, -}; - -static uint16_t subghz_protocol_bin_raw_get_full_byte(uint16_t bit_count) { - if(bit_count & 0x7) { - return (bit_count >> 3) + 1; - } else { - return (bit_count >> 3); - } -} - -void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderBinRAW* instance = malloc(sizeof(SubGhzProtocolEncoderBinRAW)); - - instance->base.protocol = &subghz_protocol_bin_raw; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = BIN_RAW_BUF_DATA_SIZE * 5; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->data = malloc(instance->encoder.size_upload * sizeof(uint8_t)); - memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_bin_raw_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderBinRAW* instance = context; - free(instance->encoder.upload); - free(instance->data); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderBinRAW instance - * @return true On success - */ -static bool subghz_protocol_encoder_bin_raw_get_upload(SubGhzProtocolEncoderBinRAW* instance) { - furi_assert(instance); - - //we glue all the pieces of the package into 1 long sequence with left alignment, - //in the uploaded data we have right alignment. - - bin_raw_debug_tag(TAG, "Recovery of offset bits in sequences\r\n"); - uint16_t i = 0; - uint16_t ind = 0; - bin_raw_debug("\tind byte_bias\tbit_count\tbit_bias\r\n"); - while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { - uint8_t bit_bias = - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - - instance->data_markup[i].bit_count; - bin_raw_debug( - "\t%d\t%d\t%d :\t\t%d\r\n", - i, - instance->data_markup[i].byte_bias, - instance->data_markup[i].bit_count, - bit_bias); - for(uint16_t y = instance->data_markup[i].byte_bias * 8; - y < instance->data_markup[i].byte_bias * 8 + - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count) * 8 - - bit_bias; - y++) { - subghz_protocol_blocks_set_bit_array( - subghz_protocol_blocks_get_bit_array(instance->data, y + bit_bias), - instance->data, - ind++, - BIN_RAW_BUF_DATA_SIZE); - } - i++; - } - bin_raw_debug("\r\n"); -#ifdef BIN_RAW_DEBUG - bin_raw_debug_tag(TAG, "Restored Sequence left aligned\r\n"); - for(uint16_t y = 0; y < subghz_protocol_bin_raw_get_full_byte(ind); y++) { - bin_raw_debug("%02X ", instance->data[y]); - } - bin_raw_debug("\r\n\tbin_count_result= %d\r\n\r\n", ind); - - bin_raw_debug_tag( - TAG, "Maximum levels encoded in upload %zu\r\n", instance->encoder.size_upload); -#endif - instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( - instance->data, - ind, - instance->encoder.upload, - instance->encoder.size_upload, - instance->te, - SubGhzProtocolBlockAlignBitLeft); - - bin_raw_debug_tag(TAG, "The result %zu is levels\r\n", instance->encoder.size_upload); - bin_raw_debug_tag(TAG, "Remaining free memory %zu\r\n", memmgr_get_free_heap()); - return true; -} - -bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderBinRAW* instance = context; - - bool res = false; - uint32_t temp_data = 0; - - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); - break; - } - - instance->generic.data_count_bit = (uint16_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - - temp_data = 0; - uint16_t ind = 0; - uint16_t byte_bias = 0; - uint16_t byte_count = 0; - memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { - if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { - FURI_LOG_E(TAG, "Markup overflow"); - break; - } - byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); - if(byte_count > BIN_RAW_BUF_DATA_SIZE) { - FURI_LOG_E(TAG, "Receive buffer overflow"); - break; - } - - instance->data_markup[ind].bit_count = temp_data; - instance->data_markup[ind].byte_bias = byte_bias; - byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); - - if(!flipper_format_read_hex( - flipper_format, - "Data_RAW", - instance->data + instance->data_markup[ind].byte_bias, - subghz_protocol_bin_raw_get_full_byte(temp_data))) { - instance->data_markup[ind].bit_count = 0; - FURI_LOG_E(TAG, "Missing Data_RAW"); - break; - } - ind++; - } - -#ifdef BIN_RAW_DEBUG - uint16_t i = 0; - bin_raw_debug_tag(TAG, "Download data to encoder\r\n"); - bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); - while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { - bin_raw_debug( - "\r\n\t%d\t%d\t%d :\t", - i, - instance->data_markup[i].byte_bias, - instance->data_markup[i].bit_count); - for(uint16_t y = instance->data_markup[i].byte_bias; - y < instance->data_markup[i].byte_bias + - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); - y++) { - bin_raw_debug("%02X ", instance->data[y]); - } - i++; - } - bin_raw_debug("\r\n\r\n"); -#endif - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - if(!subghz_protocol_encoder_bin_raw_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(0); - - return res; -} - -void subghz_protocol_encoder_bin_raw_stop(void* context) { - SubGhzProtocolEncoderBinRAW* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context) { - SubGhzProtocolEncoderBinRAW* 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_bin_raw_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderBinRAW* instance = malloc(sizeof(SubGhzProtocolDecoderBinRAW)); - instance->base.protocol = &subghz_protocol_bin_raw; - instance->generic.protocol_name = instance->base.protocol->name; - instance->data_raw_ind = 0; - instance->data_raw = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); - instance->data = malloc(BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); - memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - instance->adaptive_threshold_rssi = BIN_RAW_THRESHOLD_RSSI; - return instance; -} - -void subghz_protocol_decoder_bin_raw_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - free(instance->data_raw); - free(instance->data); - free(instance); -} - -void subghz_protocol_decoder_bin_raw_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; -#ifdef BIN_RAW_DEBUG - UNUSED(instance); -#else - instance->decoder.parser_step = BinRAWDecoderStepNoParse; - instance->data_raw_ind = 0; -#endif -} - -void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - - if(instance->decoder.parser_step == BinRAWDecoderStepWrite) { - if(instance->data_raw_ind == BIN_RAW_BUF_RAW_SIZE) { - instance->decoder.parser_step = BinRAWDecoderStepBufFull; - } else { - instance->data_raw[instance->data_raw_ind++] = (level ? duration : -duration); - } - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzProtocolDecoderBinRAW* instance - */ -static bool - subghz_protocol_bin_raw_check_remote_controller(SubGhzProtocolDecoderBinRAW* instance) { - struct { - float data; - uint16_t count; - } classes[BIN_RAW_SEARCH_CLASSES]; - - size_t ind = 0; - - memset(classes, 0x00, sizeof(classes)); - - uint16_t data_markup_ind = 0; - memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - - if(instance->data_raw_ind < 512) { - ind = - instance->data_raw_ind - - 100; //there is usually garbage at the end of the record, we exclude it from the classification - } else { - ind = 512; - } - - //sort the durations to find the shortest correlated interval - for(size_t i = 0; i < ind; i++) { - for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { - if(classes[k].count == 0) { - classes[k].data = (float)(abs(instance->data_raw[i])); - classes[k].count++; - break; - } else if( - DURATION_DIFF((float)(abs(instance->data_raw[i])), (classes[k].data)) < - (classes[k].data / 4)) { //if the test value does not differ by more than 25% - classes[k].data += ((float)(abs(instance->data_raw[i])) - classes[k].data) * - 0.05f; //running average k=0.05 - classes[k].count++; - break; - } - } - } - - // if(classes[BIN_RAW_SEARCH_CLASSES - 1].count != 0) { - // //filling the classifier, it means that they received an unclean signal - // return false; - // } - - //looking for the minimum te with an occurrence greater than BIN_RAW_TE_MIN_COUNT - instance->te = subghz_protocol_bin_raw_const.te_long * 2; - - bool te_ok = false; - uint16_t gap_ind = 0; - uint16_t gap_delta = 0; - uint32_t gap = 0; - int data_temp = 0; - BinRAWType bin_raw_type = BinRAWTypeUnknown; - - //sort by number of occurrences - bool swap = true; - while(swap) { - swap = false; - for(size_t i = 1; i < BIN_RAW_SEARCH_CLASSES; i++) { - if(classes[i].count > classes[i - 1].count) { - uint32_t data = classes[i - 1].data; - uint32_t count = classes[i - 1].count; - classes[i - 1].data = classes[i].data; - classes[i - 1].count = classes[i].count; - classes[i].data = data; - classes[i].count = count; - swap = true; - } - } - } -#ifdef BIN_RAW_DEBUG - bin_raw_debug_tag(TAG, "Sorted durations\r\n"); - bin_raw_debug("\t\tind\tcount\tus\r\n"); - for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { - bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); - } - bin_raw_debug("\r\n"); -#endif - if((classes[0].count > BIN_RAW_TE_MIN_COUNT) && (classes[1].count == 0)) { - //adopted only the preamble - instance->te = (uint32_t)classes[0].data; - te_ok = true; - gap = 0; //gap no - } else { - //take the 2 most common durations - //check that there are enough - if((classes[0].count < BIN_RAW_TE_MIN_COUNT) || - (classes[1].count < (BIN_RAW_TE_MIN_COUNT >> 1))) - return false; - //arrange the first 2 date values in ascending order - if(classes[0].data > classes[1].data) { - uint32_t data = classes[1].data; - classes[0].data = classes[1].data; - classes[1].data = data; - } - - //determine the value to be corrected - for(uint8_t k = 1; k < 5; k++) { - float delta = (classes[1].data / (classes[0].data / k)); - bin_raw_debug_tag(TAG, "K_div= %f\r\n", (double)(delta)); - delta -= (uint32_t)delta; - - if((delta < 0.20f) || (delta > 0.80f)) { - instance->te = (uint32_t)classes[0].data / k; - bin_raw_debug_tag(TAG, "K= %d\r\n", k); - te_ok = true; //found a correlated duration - break; - } - } - if(!te_ok) { - //did not find the minimum TE satisfying the condition - return false; - } - bin_raw_debug_tag(TAG, "TE= %lu\r\n\r\n", instance->te); - - //looking for a gap - for(size_t k = 2; k < BIN_RAW_SEARCH_CLASSES; k++) { - if((classes[k].count > 2) && (classes[k].data > gap)) { - gap = (uint32_t)classes[k].data; - gap_delta = gap / 5; //calculate 20% deviation from ideal value - } - } - - if((gap / instance->te) < - 10) { //make an assumption, the longest gap should be more than 10 TE - gap = 0; //check that our signal has a gap greater than 10*TE - bin_raw_type = BinRAWTypeNoGap; - } else { - bin_raw_type = BinRAWTypeGap; - //looking for the last occurrence of gap - ind = instance->data_raw_ind - 1; - while((ind > 0) && (DURATION_DIFF(abs(instance->data_raw[ind]), gap) > gap_delta)) { - ind--; - } - gap_ind = ind; - } - } - - //if we consider that there is a gap, then we divide the signal with respect to this gap - //processing input data from the end - if(bin_raw_type == BinRAWTypeGap) { - bin_raw_debug_tag(TAG, "Tinted sequence\r\n"); - ind = (BIN_RAW_BUF_DATA_SIZE * 8); - uint16_t bit_count = 0; - do { - gap_ind--; - data_temp = (int)(round((float)(instance->data_raw[gap_ind]) / instance->te)); - bin_raw_debug("%d ", data_temp); - if(data_temp == 0) bit_count++; //there is noise in the package - for(size_t i = 0; i < abs(data_temp); i++) { - bit_count++; - if(ind) { - ind--; - } else { - break; - } - if(data_temp > 0) { - subghz_protocol_blocks_set_bit_array( - true, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); - } else { - subghz_protocol_blocks_set_bit_array( - false, instance->data, ind, BIN_RAW_BUF_DATA_SIZE); - } - } - //split into full bytes if gap is caught - if(DURATION_DIFF(abs(instance->data_raw[gap_ind]), gap) < gap_delta) { - instance->data_markup[data_markup_ind].byte_bias = ind >> 3; - instance->data_markup[data_markup_ind++].bit_count = bit_count; - bit_count = 0; - - if(data_markup_ind == BIN_RAW_MAX_MARKUP_COUNT) break; - ind &= 0xFFFFFFF8; //jump to the pre whole byte - } - } while(gap_ind != 0); - if((data_markup_ind != BIN_RAW_MAX_MARKUP_COUNT) && (ind != 0)) { - instance->data_markup[data_markup_ind].byte_bias = ind >> 3; - instance->data_markup[data_markup_ind++].bit_count = bit_count; - } - - bin_raw_debug("\r\n\t count bit= %zu\r\n\r\n", (BIN_RAW_BUF_DATA_SIZE * 8) - ind); - - //reset the classifier and classify the received data - memset(classes, 0x00, sizeof(classes)); - - bin_raw_debug_tag(TAG, "Sort the found pieces by the number of bits in them\r\n"); - for(size_t i = 0; i < data_markup_ind; i++) { - for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { - if(classes[k].count == 0) { - classes[k].data = instance->data_markup[i].bit_count; - classes[k].count++; - break; - } else if(instance->data_markup[i].bit_count == (uint16_t)classes[k].data) { - classes[k].count++; - break; - } - } - } - -#ifdef BIN_RAW_DEBUG - bin_raw_debug("\t\tind\tcount\tus\r\n"); - for(size_t k = 0; k < BIN_RAW_SEARCH_CLASSES; k++) { - bin_raw_debug("\t\t%zu\t%u\t%lu\r\n", k, classes[k].count, (uint32_t)classes[k].data); - } - bin_raw_debug("\r\n"); -#endif - - //choose the value with the maximum repetition - data_temp = 0; - for(size_t i = 0; i < BIN_RAW_SEARCH_CLASSES; i++) { - if((classes[i].count > 1) && (data_temp < classes[i].count)) - data_temp = (int)classes[i].data; - } - - //if(data_markup_ind == 0) return false; - -#ifdef BIN_RAW_DEBUG - //output in reverse order - bin_raw_debug_tag(TAG, "Found sequences\r\n"); - bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); - uint16_t data_markup_ind_temp = data_markup_ind; - if(data_markup_ind) { - data_markup_ind_temp--; - for(size_t i = (ind / 8); i < BIN_RAW_BUF_DATA_SIZE; i++) { - if(instance->data_markup[data_markup_ind_temp].byte_bias == i) { - bin_raw_debug( - "\r\n\t%d\t%d\t%d :\t", - data_markup_ind_temp, - instance->data_markup[data_markup_ind_temp].byte_bias, - instance->data_markup[data_markup_ind_temp].bit_count); - if(data_markup_ind_temp != 0) data_markup_ind_temp--; - } - bin_raw_debug("%02X ", instance->data[i]); - } - bin_raw_debug("\r\n\r\n"); - } - //compare data in chunks with the same number of bits - bin_raw_debug_tag(TAG, "Analyze sequences of long %d bit\r\n\r\n", data_temp); -#endif - - //if(data_temp == 0) data_temp = (int)classes[0].data; - - if(data_temp != 0) { - //check that data in transmission is repeated every packet - for(uint16_t i = 0; i < data_markup_ind - 1; i++) { - if((instance->data_markup[i].bit_count == data_temp) && - (instance->data_markup[i + 1].bit_count == data_temp)) { - //if the number of bits in adjacent parcels is the same, compare the data - bin_raw_debug_tag( - TAG, - "Comparison of neighboring sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", - i, - i + 1, - instance->data[instance->data_markup[i].byte_bias], - instance->data[instance->data_markup[i + 1].byte_bias], - instance->data - [instance->data_markup[i].byte_bias + - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[i].bit_count) - - 1], - instance->data - [instance->data_markup[i + 1].byte_bias + - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[i + 1].bit_count) - - 1]); - - uint16_t byte_count = - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); - if(memcmp( - instance->data + instance->data_markup[i].byte_bias, - instance->data + instance->data_markup[i + 1].byte_bias, - byte_count - 1) == 0) { - bin_raw_debug_tag( - TAG, "Match found bin_raw_type=BinRAWTypeGapRecurring\r\n\r\n"); - - //place in 1 element the offset to valid data - instance->data_markup[0].bit_count = instance->data_markup[i].bit_count; - instance->data_markup[0].byte_bias = instance->data_markup[i].byte_bias; - //markup end sign - instance->data_markup[1].bit_count = 0; - instance->data_markup[1].byte_bias = 0; - - bin_raw_type = BinRAWTypeGapRecurring; - i = data_markup_ind; - break; - } - } - } - } - - if(bin_raw_type == BinRAWTypeGap) { - // check that retry occurs every n packets - for(uint16_t i = 0; i < data_markup_ind - 2; i++) { - uint16_t byte_count = - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count); - for(uint16_t y = i + 1; y < data_markup_ind - 1; y++) { - bin_raw_debug_tag( - TAG, - "Comparison every N sequences ind_1=%d ind_2=%d %02X=%02X .... %02X=%02X\r\n", - i, - y, - instance->data[instance->data_markup[i].byte_bias], - instance->data[instance->data_markup[y].byte_bias], - instance->data - [instance->data_markup[i].byte_bias + - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[i].bit_count) - - 1], - instance->data - [instance->data_markup[y].byte_bias + - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[y].bit_count) - - 1]); - - if(byte_count == - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[y].bit_count)) { //if the length in bytes matches - - if((memcmp( - instance->data + instance->data_markup[i].byte_bias, - instance->data + instance->data_markup[y].byte_bias, - byte_count - 1) == 0) && - (memcmp( - instance->data + instance->data_markup[i + 1].byte_bias, - instance->data + instance->data_markup[y + 1].byte_bias, - byte_count - 1) == 0)) { - uint8_t index = 0; -#ifdef BIN_RAW_DEBUG - bin_raw_debug_tag( - TAG, "Match found bin_raw_type=BinRAWTypeGapRolling\r\n\r\n"); - //output in reverse order - bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data\r\n"); - index = y - 1; - for(size_t z = instance->data_markup[y].byte_bias + byte_count; - z < instance->data_markup[i].byte_bias + byte_count; - z++) { - if(instance->data_markup[index].byte_bias == z) { - bin_raw_debug( - "\r\n\t%d\t%d\t%d :\t", - index, - instance->data_markup[index].byte_bias, - instance->data_markup[index].bit_count); - if(index != 0) index--; - } - bin_raw_debug("%02X ", instance->data[z]); - } - - bin_raw_debug("\r\n\r\n"); -#endif - //todo can be optimized - BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; - memcpy( - markup_temp, - instance->data_markup, - BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - memset( - instance->data_markup, - 0x00, - BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - - for(index = i; index < y; index++) { - instance->data_markup[index - i].bit_count = - markup_temp[y - index - 1].bit_count; - instance->data_markup[index - i].byte_bias = - markup_temp[y - index - 1].byte_bias; - } - - bin_raw_type = BinRAWTypeGapRolling; - i = data_markup_ind; - break; - } - } - } - } - } - //todo can be optimized - if(bin_raw_type == BinRAWTypeGap) { - if(data_temp != 0) { //there are sequences with the same number of bits - - BinRAW_Markup markup_temp[BIN_RAW_MAX_MARKUP_COUNT]; - memcpy( - markup_temp, - instance->data_markup, - BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - memset( - instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(data_temp); - uint16_t index = 0; - uint16_t it = BIN_RAW_MAX_MARKUP_COUNT; - do { - it--; - if(subghz_protocol_bin_raw_get_full_byte(markup_temp[it].bit_count) == - byte_count) { - instance->data_markup[index].bit_count = markup_temp[it].bit_count; - instance->data_markup[index].byte_bias = markup_temp[it].byte_bias; - index++; - bin_raw_type = BinRAWTypeGapUnknown; - } - } while(it != 0); - } - } - - if(bin_raw_type != BinRAWTypeGap) - return true; - else - return false; - - } else { - // if bin_raw_type == BinRAWTypeGap - bin_raw_debug_tag(TAG, "Sequence analysis without gap\r\n"); - ind = 0; - for(size_t i = 0; i < instance->data_raw_ind; i++) { - int data_temp = (int)(round((float)(instance->data_raw[i]) / instance->te)); - if(data_temp == 0) break; //found an interval 2 times shorter than TE, this is noise - bin_raw_debug("%d ", data_temp); - - for(size_t k = 0; k < abs(data_temp); k++) { - if(data_temp > 0) { - subghz_protocol_blocks_set_bit_array( - true, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); - } else { - subghz_protocol_blocks_set_bit_array( - false, instance->data, ind++, BIN_RAW_BUF_DATA_SIZE); - } - if(ind == BIN_RAW_BUF_DATA_SIZE * 8) { - i = instance->data_raw_ind; - break; - } - } - } - - if(ind != 0) { - bin_raw_type = BinRAWTypeNoGap; - //right alignment - uint8_t bit_bias = (subghz_protocol_bin_raw_get_full_byte(ind) << 3) - ind; -#ifdef BIN_RAW_DEBUG - bin_raw_debug( - "\r\n\t count bit= %zu\tcount full byte= %d\tbias bit= %d\r\n\r\n", - ind, - subghz_protocol_bin_raw_get_full_byte(ind), - bit_bias); - - for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { - bin_raw_debug("%02X ", instance->data[i]); - } - bin_raw_debug("\r\n\r\n"); -#endif - //checking that the received sequence contains useful data - bool data_check = false; - for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { - if(instance->data[i] != 0) { - data_check = true; - break; - } - } - - if(data_check) { - for(size_t i = subghz_protocol_bin_raw_get_full_byte(ind) - 1; i > 0; i--) { - instance->data[i] = (instance->data[i - 1] << (8 - bit_bias)) | - (instance->data[i] >> bit_bias); - } - instance->data[0] = (instance->data[0] >> bit_bias); - -#ifdef BIN_RAW_DEBUG - bin_raw_debug_tag(TAG, "Data right alignment\r\n"); - for(size_t i = 0; i < subghz_protocol_bin_raw_get_full_byte(ind); i++) { - bin_raw_debug("%02X ", instance->data[i]); - } - bin_raw_debug("\r\n\r\n"); -#endif - instance->data_markup[0].bit_count = ind; - instance->data_markup[0].byte_bias = 0; - - return true; - } else { - return false; - } - } else { - return false; - } - } - return false; -} - -void subghz_protocol_decoder_bin_raw_data_input_rssi( - SubGhzProtocolDecoderBinRAW* instance, - float rssi) { - furi_assert(instance); - switch(instance->decoder.parser_step) { - case BinRAWDecoderStepReset: - - bin_raw_debug("%ld %ld :", (int32_t)rssi, (int32_t)instance->adaptive_threshold_rssi); - if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { - instance->data_raw_ind = 0; - memset(instance->data_raw, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(int32_t)); - memset(instance->data, 0x00, BIN_RAW_BUF_RAW_SIZE * sizeof(uint8_t)); - instance->decoder.parser_step = BinRAWDecoderStepWrite; - bin_raw_debug_tag(TAG, "RSSI\r\n"); - } else { - //adaptive noise level adjustment - instance->adaptive_threshold_rssi += (rssi - instance->adaptive_threshold_rssi) * 0.2f; - } - break; - - case BinRAWDecoderStepBufFull: - case BinRAWDecoderStepWrite: -#ifdef BIN_RAW_DEBUG - if(rssi > (instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI)) { - bin_raw_debug("\033[0;32m%ld \033[0m ", (int32_t)rssi); - } else { - bin_raw_debug("%ld ", (int32_t)rssi); - } -#endif - if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { -#ifdef BIN_RAW_DEBUG - bin_raw_debug("\r\n\r\n"); - bin_raw_debug_tag(TAG, "Data for analysis, positive high, negative low, us\r\n"); - for(size_t i = 0; i < instance->data_raw_ind; i++) { - bin_raw_debug("%ld ", instance->data_raw[i]); - } - bin_raw_debug("\r\n\t count data= %zu\r\n\r\n", instance->data_raw_ind); -#endif - instance->decoder.parser_step = BinRAWDecoderStepReset; - instance->generic.data_count_bit = 0; - if(instance->data_raw_ind >= BIN_RAW_BUF_MIN_DATA_COUNT) { - if(subghz_protocol_bin_raw_check_remote_controller(instance)) { - bin_raw_debug_tag(TAG, "Sequence found\r\n"); - bin_raw_debug("\tind byte_bias\tbit_count\t\tbin_data"); - uint16_t i = 0; - while((i < BIN_RAW_MAX_MARKUP_COUNT) && - (instance->data_markup[i].bit_count != 0)) { - instance->generic.data_count_bit += instance->data_markup[i].bit_count; -#ifdef BIN_RAW_DEBUG - bin_raw_debug( - "\r\n\t%d\t%d\t%d :\t", - i, - instance->data_markup[i].byte_bias, - instance->data_markup[i].bit_count); - for(uint16_t y = instance->data_markup[i].byte_bias; - y < instance->data_markup[i].byte_bias + - subghz_protocol_bin_raw_get_full_byte( - instance->data_markup[i].bit_count); - y++) { - bin_raw_debug("%02X ", instance->data[y]); - } -#endif - i++; - } - bin_raw_debug("\r\n"); - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - } - } - break; - - default: - //if instance->decoder.parser_step == BinRAWDecoderStepNoParse or others, restore the initial state - if(rssi < instance->adaptive_threshold_rssi + BIN_RAW_DELTA_RSSI) { - instance->decoder.parser_step = BinRAWDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - return subghz_protocol_blocks_add_bytes( - instance->data + instance->data_markup[0].byte_bias, - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[0].bit_count)); -} - -bool subghz_protocol_decoder_bin_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_header_cstr( - flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - - if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - break; - } - - subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - flipper_format, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - flipper_format, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - break; - } - if(!flipper_format_write_hex( - flipper_format, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - break; - } - } - if(!flipper_format_write_string_cstr( - flipper_format, "Protocol", instance->generic.protocol_name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - - uint32_t temp = instance->generic.data_count_bit; - if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) { - FURI_LOG_E(TAG, "Unable to add Bit"); - break; - } - - if(!flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { - FURI_LOG_E(TAG, "Unable to add TE"); - break; - } - - uint16_t i = 0; - while((i < BIN_RAW_MAX_MARKUP_COUNT) && (instance->data_markup[i].bit_count != 0)) { - temp = instance->data_markup[i].bit_count; - if(!flipper_format_write_uint32(flipper_format, "Bit_RAW", &temp, 1)) { - FURI_LOG_E(TAG, "Bit_RAW"); - break; - } - if(!flipper_format_write_hex( - flipper_format, - "Data_RAW", - instance->data + instance->data_markup[i].byte_bias, - subghz_protocol_bin_raw_get_full_byte(instance->data_markup[i].bit_count))) { - FURI_LOG_E(TAG, "Unable to add Data_RAW"); - break; - } - i++; - } - - res = true; - } while(false); - furi_string_free(temp_str); - return res; -} - -bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - - bool res = false; - uint32_t temp_data = 0; - - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) { - FURI_LOG_E(TAG, "Missing Bit"); - break; - } - - instance->generic.data_count_bit = (uint16_t)temp_data; - - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - - temp_data = 0; - uint16_t ind = 0; - uint16_t byte_bias = 0; - uint16_t byte_count = 0; - memset(instance->data_markup, 0x00, BIN_RAW_MAX_MARKUP_COUNT * sizeof(BinRAW_Markup)); - while(flipper_format_read_uint32(flipper_format, "Bit_RAW", (uint32_t*)&temp_data, 1)) { - if(ind >= BIN_RAW_MAX_MARKUP_COUNT) { - FURI_LOG_E(TAG, "Markup overflow"); - break; - } - byte_count += subghz_protocol_bin_raw_get_full_byte(temp_data); - if(byte_count > BIN_RAW_BUF_DATA_SIZE) { - FURI_LOG_E(TAG, "Receive buffer overflow"); - break; - } - - instance->data_markup[ind].bit_count = temp_data; - instance->data_markup[ind].byte_bias = byte_bias; - byte_bias += subghz_protocol_bin_raw_get_full_byte(temp_data); - - if(!flipper_format_read_hex( - flipper_format, - "Data_RAW", - instance->data + instance->data_markup[ind].byte_bias, - subghz_protocol_bin_raw_get_full_byte(temp_data))) { - instance->data_markup[ind].bit_count = 0; - FURI_LOG_E(TAG, "Missing Data_RAW"); - break; - } - ind++; - } - - res = true; - } while(0); - - return res; -} - -void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderBinRAW* instance = context; - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:", - instance->generic.protocol_name, - instance->generic.data_count_bit); - - uint16_t byte_count = subghz_protocol_bin_raw_get_full_byte(instance->generic.data_count_bit); - for(size_t i = 0; (byte_count < 36 ? i < byte_count : i < 36); i++) { - furi_string_cat_printf(output, "%02X", instance->data[i]); - } - - furi_string_cat_printf(output, "\r\nTe:%luus\r\n", instance->te); -} diff --git a/applications/main/subghz/protocols/bin_raw.h b/applications/main/subghz/protocols/bin_raw.h deleted file mode 100644 index c63f86ce6..000000000 --- a/applications/main/subghz/protocols/bin_raw.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW" - -typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW; -typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW; - -extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder; -extern const SubGhzProtocol subghz_protocol_bin_raw; - -/** - * Allocate SubGhzProtocolEncoderBinRAW. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance - */ -void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderBinRAW. - * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance - */ -void subghz_protocol_encoder_bin_raw_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance - */ -void subghz_protocol_encoder_bin_raw_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderBinRAW instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderBinRAW. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance - */ -void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderBinRAW. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - */ -void subghz_protocol_decoder_bin_raw_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderBinRAW. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - */ -void subghz_protocol_decoder_bin_raw_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context); - -void subghz_protocol_decoder_bin_raw_data_input_rssi( - SubGhzProtocolDecoderBinRAW* instance, - float rssi); - -/** - * Serialize data SubGhzProtocolDecoderBinRAW. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_bin_raw_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderBinRAW. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderBinRAW instance - * @param output Resulting text - */ -void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came.c b/applications/main/subghz/protocols/came.c deleted file mode 100644 index bed26d7d2..000000000 --- a/applications/main/subghz/protocols/came.c +++ /dev/null @@ -1,347 +0,0 @@ -#include "came.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://phreakerclub.com/447 - * - */ - -#define TAG "SubGhzProtocolCAME" -#define CAME_24_COUNT_BIT 24 -#define PRASTEL_COUNT_BIT 25 -#define PRASTEL_NAME "Prastel" -#define AIRFORCE_COUNT_BIT 18 -#define AIRFORCE_NAME "Airforce" - -static const SubGhzBlockConst subghz_protocol_came_const = { - .te_short = 320, - .te_long = 640, - .te_delta = 150, - .min_count_bit_for_found = 12, -}; - -struct SubGhzProtocolDecoderCame { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderCame { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - CameDecoderStepReset = 0, - CameDecoderStepFoundStartBit, - CameDecoderStepSaveDuration, - CameDecoderStepCheckDuration, -} CameDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_came_decoder = { - .alloc = subghz_protocol_decoder_came_alloc, - .free = subghz_protocol_decoder_came_free, - - .feed = subghz_protocol_decoder_came_feed, - .reset = subghz_protocol_decoder_came_reset, - - .get_hash_data = subghz_protocol_decoder_came_get_hash_data, - .serialize = subghz_protocol_decoder_came_serialize, - .deserialize = subghz_protocol_decoder_came_deserialize, - .get_string = subghz_protocol_decoder_came_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_came_encoder = { - .alloc = subghz_protocol_encoder_came_alloc, - .free = subghz_protocol_encoder_came_free, - - .deserialize = subghz_protocol_encoder_came_deserialize, - .stop = subghz_protocol_encoder_came_stop, - .yield = subghz_protocol_encoder_came_yield, -}; - -const SubGhzProtocol subghz_protocol_came = { - .name = SUBGHZ_PROTOCOL_CAME_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_came_decoder, - .encoder = &subghz_protocol_came_encoder, -}; - -void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderCame* instance = malloc(sizeof(SubGhzProtocolEncoderCame)); - - instance->base.protocol = &subghz_protocol_came; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_came_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderCame* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderCame instance - * @return true On success - */ -static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = level_duration_make( - false, - (((instance->generic.data_count_bit == CAME_24_COUNT_BIT) || - (instance->generic.data_count_bit == - subghz_protocol_came_const.min_count_bit_for_found)) ? - (uint32_t)subghz_protocol_came_const.te_short * 76 : - (uint32_t)subghz_protocol_came_const.te_short * 39)); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderCame* 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 > PRASTEL_COUNT_BIT)) { - 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); - - if(!subghz_protocol_encoder_came_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_came_stop(void* context) { - SubGhzProtocolEncoderCame* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_came_yield(void* context) { - SubGhzProtocolEncoderCame* 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_came_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderCame* instance = malloc(sizeof(SubGhzProtocolDecoderCame)); - instance->base.protocol = &subghz_protocol_came; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_came_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - free(instance); -} - -void subghz_protocol_decoder_came_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - instance->decoder.parser_step = CameDecoderStepReset; -} - -void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - switch(instance->decoder.parser_step) { - case CameDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) < - subghz_protocol_came_const.te_delta * 47)) { - //Found header CAME - instance->decoder.parser_step = CameDecoderStepFoundStartBit; - } - break; - case CameDecoderStepFoundStartBit: - if(!level) { - break; - } else if( - DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < - subghz_protocol_came_const.te_delta) { - //Found start bit CAME - instance->decoder.parser_step = CameDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = CameDecoderStepReset; - } - break; - case CameDecoderStepSaveDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_came_const.te_short * 4)) { - instance->decoder.parser_step = CameDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_came_const.min_count_bit_for_found) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - - 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); - } - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = CameDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = CameDecoderStepReset; - } - break; - case CameDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_short) < - subghz_protocol_came_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_came_const.te_long) < - subghz_protocol_came_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = CameDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_long) < - subghz_protocol_came_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_came_const.te_short) < - subghz_protocol_came_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = CameDecoderStepSaveDuration; - } else - instance->decoder.parser_step = CameDecoderStepReset; - } else { - instance->decoder.parser_step = CameDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_came_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderCame* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Yek:0x%08lX\r\n", - (instance->generic.data_count_bit == PRASTEL_COUNT_BIT ? - PRASTEL_NAME : - (instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ? - AIRFORCE_NAME : - instance->generic.protocol_name)), - instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo); -} diff --git a/applications/main/subghz/protocols/came.h b/applications/main/subghz/protocols/came.h deleted file mode 100644 index 253c93aae..000000000 --- a/applications/main/subghz/protocols/came.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_CAME_NAME "CAME" - -typedef struct SubGhzProtocolDecoderCame SubGhzProtocolDecoderCame; -typedef struct SubGhzProtocolEncoderCame SubGhzProtocolEncoderCame; - -extern const SubGhzProtocolDecoder subghz_protocol_came_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_came_encoder; -extern const SubGhzProtocol subghz_protocol_came; - -/** - * Allocate SubGhzProtocolEncoderCame. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderCame* pointer to a SubGhzProtocolEncoderCame instance - */ -void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderCame. - * @param context Pointer to a SubGhzProtocolEncoderCame instance - */ -void subghz_protocol_encoder_came_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderCame instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderCame instance - */ -void subghz_protocol_encoder_came_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderCame instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_came_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderCame. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderCame* pointer to a SubGhzProtocolDecoderCame instance - */ -void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderCame. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - */ -void subghz_protocol_decoder_came_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderCame. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - */ -void subghz_protocol_decoder_came_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_came_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderCame. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_came_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderCame. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderCame instance - * @param output Resulting text - */ -void subghz_protocol_decoder_came_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_atomo.c b/applications/main/subghz/protocols/came_atomo.c deleted file mode 100644 index d12e5976c..000000000 --- a/applications/main/subghz/protocols/came_atomo.c +++ /dev/null @@ -1,598 +0,0 @@ -#include "came_atomo.h" -#include -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocoCameAtomo" - -static const SubGhzBlockConst subghz_protocol_came_atomo_const = { - .te_short = 600, - .te_long = 1200, - .te_delta = 250, - .min_count_bit_for_found = 62, -}; - -struct SubGhzProtocolDecoderCameAtomo { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - ManchesterState manchester_saved_state; -}; - -struct SubGhzProtocolEncoderCameAtomo { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - CameAtomoDecoderStepReset = 0, - CameAtomoDecoderStepDecoderData, -} CameAtomoDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder = { - .alloc = subghz_protocol_decoder_came_atomo_alloc, - .free = subghz_protocol_decoder_came_atomo_free, - - .feed = subghz_protocol_decoder_came_atomo_feed, - .reset = subghz_protocol_decoder_came_atomo_reset, - - .get_hash_data = subghz_protocol_decoder_came_atomo_get_hash_data, - .serialize = subghz_protocol_decoder_came_atomo_serialize, - .deserialize = subghz_protocol_decoder_came_atomo_deserialize, - .get_string = subghz_protocol_decoder_came_atomo_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder = { - .alloc = subghz_protocol_encoder_came_atomo_alloc, - .free = subghz_protocol_encoder_came_atomo_free, - - .deserialize = subghz_protocol_encoder_came_atomo_deserialize, - .stop = subghz_protocol_encoder_came_atomo_stop, - .yield = subghz_protocol_encoder_came_atomo_yield, -}; - -const SubGhzProtocol subghz_protocol_came_atomo = { - .name = SUBGHZ_PROTOCOL_CAME_ATOMO_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_came_atomo_decoder, - .encoder = &subghz_protocol_came_atomo_encoder, -}; - -static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance); - -void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolEncoderCameAtomo)); - - instance->base.protocol = &subghz_protocol_came_atomo; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 900; //actual size 766+ - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_came_atomo_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderCameAtomo* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static LevelDuration - subghz_protocol_encoder_came_atomo_add_duration_to_upload(ManchesterEncoderResult result) { - LevelDuration data = {.duration = 0, .level = 0}; - switch(result) { - case ManchesterEncoderResultShortLow: - data.duration = subghz_protocol_came_atomo_const.te_short; - data.level = false; - break; - case ManchesterEncoderResultLongLow: - data.duration = subghz_protocol_came_atomo_const.te_long; - data.level = false; - break; - case ManchesterEncoderResultLongHigh: - data.duration = subghz_protocol_came_atomo_const.te_long; - data.level = true; - break; - case ManchesterEncoderResultShortHigh: - data.duration = subghz_protocol_came_atomo_const.te_short; - data.level = true; - break; - - default: - FURI_LOG_E(TAG, "SubGhz: ManchesterEncoderResult is incorrect."); - break; - } - return level_duration_make(data.level, data.duration); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderCameAtomo instance - */ -static void - subghz_protocol_encoder_came_atomo_get_upload(SubGhzProtocolEncoderCameAtomo* instance) { - furi_assert(instance); - size_t index = 0; - - ManchesterEncoderState enc_state; - manchester_encoder_reset(&enc_state); - ManchesterEncoderResult result; - - uint8_t pack[8] = {}; - - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - - //Send header - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long * 15); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_long * 60); - - for(uint8_t i = 0; i < 8; i++) { - pack[0] = (instance->generic.data_2 >> 56); - pack[1] = (instance->generic.cnt >> 8); - pack[2] = (instance->generic.cnt & 0xFF); - pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); - pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); - pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); - pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); - pack[7] = (instance->generic.data_2 & 0xFF); - - if(pack[0] == 0x7F) { - pack[0] = 0; - } else { - pack[0] += (i + 1); - } - - atomo_encrypt(pack); - uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; - uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; - instance->generic.data = (uint64_t)hi << 32 | lo; - - instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; - instance->generic.data >>= 4; - instance->generic.data &= 0xFFFFFFFFFFFFFFF; - - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_short); - - for(uint8_t i = (instance->generic.data_count_bit - 2); i > 0; i--) { - if(!manchester_encoder_advance( - &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, !bit_read(instance->generic.data, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_came_atomo_add_duration_to_upload(result); - } - instance->encoder.upload[index] = - subghz_protocol_encoder_came_atomo_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - //Send pause - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_delta * 272); - } - instance->encoder.size_upload = index; - instance->generic.cnt_2++; - pack[0] = (instance->generic.cnt_2); - pack[1] = (instance->generic.cnt >> 8); - pack[2] = (instance->generic.cnt & 0xFF); - pack[3] = ((instance->generic.data_2 >> 32) & 0xFF); - pack[4] = ((instance->generic.data_2 >> 24) & 0xFF); - pack[5] = ((instance->generic.data_2 >> 16) & 0xFF); - pack[6] = ((instance->generic.data_2 >> 8) & 0xFF); - pack[7] = (instance->generic.data_2 & 0xFF); - - atomo_encrypt(pack); - uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; - uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; - instance->generic.data = (uint64_t)hi << 32 | lo; - - instance->generic.data ^= 0xFFFFFFFFFFFFFFFF; - instance->generic.data >>= 4; - instance->generic.data &= 0xFFFFFFFFFFFFFFF; -} - -bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderCameAtomo* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_came_atomo_remote_controller(&instance->generic); - subghz_protocol_encoder_came_atomo_get_upload(instance); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_came_atomo_stop(void* context) { - SubGhzProtocolEncoderCameAtomo* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context) { - SubGhzProtocolEncoderCameAtomo* 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_came_atomo_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolDecoderCameAtomo)); - instance->base.protocol = &subghz_protocol_came_atomo; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_came_atomo_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - free(instance); -} - -void subghz_protocol_decoder_came_atomo_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - instance->decoder.parser_step = CameAtomoDecoderStepReset; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case CameAtomoDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) < - subghz_protocol_came_atomo_const.te_delta * 40)) { - //Found header CAME - instance->decoder.parser_step = CameAtomoDecoderStepDecoderData; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } - break; - case CameAtomoDecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < - subghz_protocol_came_atomo_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < - subghz_protocol_came_atomo_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= ((uint32_t)subghz_protocol_came_atomo_const.te_long * 2 + - subghz_protocol_came_atomo_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_came_atomo_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.decode_data = 0; - instance->decoder.decode_count_bit = 1; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } else { - instance->decoder.parser_step = CameAtomoDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) < - subghz_protocol_came_atomo_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) < - subghz_protocol_came_atomo_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = CameAtomoDecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param file_name Full path to rainbow table the file - */ -static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance) { - /* - * ***SkorP ver.*** - * 0x1fafef3ed0f7d9ef - * 0x185fcc1531ee86e7 - * 0x184fa96912c567ff - * 0x187f8a42f3dc38f7 - * 0x186f63915492a5cd - * 0x181f40bab58bfac5 - * 0x180f25c696a01bdd - * 0x183f06ed77b944d5 - * 0x182ef661d83d21a9 - * 0x18ded54a39247ea1 - * 0x18ceb0361a0f9fb9 - * 0x18fe931dfb16c0b1 - * 0x18ee7ace5c585d8b - * ........ - * transmission consists of 99 parcels with increasing counter while holding down the button - * with each new press, the counter in the encrypted part increases - * - * 0x1FAFF13ED0F7D9EF - * 0x1FAFF11ED0F7D9EF - * 0x1FAFF10ED0F7D9EF - * 0x1FAFF0FED0F7D9EF - * 0x1FAFF0EED0F7D9EF - * 0x1FAFF0DED0F7D9EF - * 0x1FAFF0CED0F7D9EF - * 0x1FAFF0BED0F7D9EF - * 0x1FAFF0AED0F7D9EF - * - * where 0x1FAF - parcel counter, 0хF0A - button press counter, - * 0xED0F7D9E - serial number, 0хF - key - * 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F = 0x07F0 - * 0x185f ^ 0x185F = 0x0000 - * 0x184f ^ 0x185F = 0x0010 - * 0x187f ^ 0x185F = 0x0020 - * ..... - * 0x182e ^ 0x185F = 0x0071 - * 0x18de ^ 0x185F = 0x0081 - * ..... - * 0x1e43 ^ 0x185F = 0x061C - * where the last nibble is incremented every 8 samples - * - * Decode - * - * 0x1cf6931dfb16c0b1 => 0x1cf6 - * 0x1cf6 ^ 0x185F = 0x04A9 - * 0x04A9 => 0x04A = 74 (dec) - * 74+1 % 32(atomo_magic_xor) = 11 - * GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX - * 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX = 0xEF3ED0F7D9EF - * 0xEF3 ED0F7D9E F => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key - * - * ***Eng1n33r ver. (actual)*** - * 0x1FF08D9924984115 - received data - * 0x00F7266DB67BEEA0 - inverted data - * 0x0501FD0000A08300 - decrypted data, - * where: 0x05 - Button hold-cycle counter (8-bit, from 0 to 0x7F) - * 0x01FD - Parcel counter (normal 16-bit counter) - * 0x0000A083 - Serial number (32-bit) - * 0x0 - Button code (4-bit, 0x0 - #1 left-up; 0x2 - #2 right-up; 0x4 - #3 left-down; 0x6 - #4 right-down) - * 0x0 - Last zero nibble - * */ - - instance->data ^= 0xFFFFFFFFFFFFFFFF; - instance->data <<= 4; - - uint8_t pack[8] = {}; - pack[0] = (instance->data >> 56); - pack[1] = ((instance->data >> 48) & 0xFF); - pack[2] = ((instance->data >> 40) & 0xFF); - pack[3] = ((instance->data >> 32) & 0xFF); - pack[4] = ((instance->data >> 24) & 0xFF); - pack[5] = ((instance->data >> 16) & 0xFF); - pack[6] = ((instance->data >> 8) & 0xFF); - pack[7] = (instance->data & 0xFF); - - atomo_decrypt(pack); - - instance->cnt_2 = pack[0]; - instance->cnt = (uint16_t)pack[1] << 8 | pack[2]; - instance->serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6]; - - uint8_t btn_decode = (pack[7] >> 4); - if(btn_decode == 0x0) { - instance->btn = 0x1; - } - if(btn_decode == 0x2) { - instance->btn = 0x2; - } - if(btn_decode == 0x4) { - instance->btn = 0x3; - } - if(btn_decode == 0x6) { - instance->btn = 0x4; - } - - uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3]; - uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7]; - instance->data_2 = (uint64_t)hi << 32 | lo; -} - -void atomo_encrypt(uint8_t* buff) { - uint8_t tmpB = (~buff[0] + 1) & 0x7F; - - uint8_t bitCnt = 8; - while(bitCnt < 59) { - if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { - tmpB = ((tmpB << 1) & 0xFF) | 1; - } else { - tmpB = (tmpB << 1) & 0xFF; - } - - if(tmpB & 0x80) { - buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); - } - - bitCnt++; - } - - buff[0] = (buff[0] ^ 5) & 0x7F; -} - -void atomo_decrypt(uint8_t* buff) { - buff[0] = (buff[0] ^ 5) & 0x7F; - uint8_t tmpB = (-buff[0]) & 0x7F; - - uint8_t bitCnt = 8; - while(bitCnt < 59) { - if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) { - tmpB = ((tmpB << 1) & 0xFF) | 1; - } else { - tmpB = (tmpB << 1) & 0xFF; - } - - if(tmpB & 0x80) { - buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7)); - } - - bitCnt++; - } -} - -uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_came_atomo_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_came_atomo_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_came_atomo_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderCameAtomo* instance = context; - subghz_protocol_came_atomo_remote_controller(&instance->generic); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%08lX%08lX\r\n" - "Sn:0x%08lX Btn:0x%01X\r\n" - "Pcl_Cnt:0x%04lX\r\n" - "Btn_Cnt:0x%02X", - - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt, - instance->generic.cnt_2); -} diff --git a/applications/main/subghz/protocols/came_atomo.h b/applications/main/subghz/protocols/came_atomo.h deleted file mode 100644 index 736aee850..000000000 --- a/applications/main/subghz/protocols/came_atomo.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once -#include "base.h" - -#define SUBGHZ_PROTOCOL_CAME_ATOMO_NAME "CAME Atomo" - -typedef struct SubGhzProtocolDecoderCameAtomo SubGhzProtocolDecoderCameAtomo; -typedef struct SubGhzProtocolEncoderCameAtomo SubGhzProtocolEncoderCameAtomo; - -extern const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder; -extern const SubGhzProtocol subghz_protocol_came_atomo; - -void atomo_decrypt(uint8_t* buff); - -void atomo_encrypt(uint8_t* buff); - -/** - * Allocate SubGhzProtocolEncoderCameAtomo. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderCameAtomo* pointer to a SubGhzProtocolEncoderCameAtomo instance - */ -void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderCameAtomo. - * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance - */ -void subghz_protocol_encoder_came_atomo_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance - */ -void subghz_protocol_encoder_came_atomo_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderCameAtomo. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderCameAtomo* pointer to a SubGhzProtocolDecoderCameAtomo instance - */ -void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderCameAtomo. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - */ -void subghz_protocol_decoder_came_atomo_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderCameAtomo. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - */ -void subghz_protocol_decoder_came_atomo_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderCameAtomo. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_came_atomo_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderCameAtomo. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance - * @param output Resulting text - */ -void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/came_twee.c b/applications/main/subghz/protocols/came_twee.c deleted file mode 100644 index e7eb33c42..000000000 --- a/applications/main/subghz/protocols/came_twee.c +++ /dev/null @@ -1,468 +0,0 @@ -#include "came_twee.h" -#include -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin - * - */ - -#define TAG "SubGhzProtocolCAME_Twee" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" -#define CNT_TO_DIP(dip) \ - (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ - (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ - (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ - (dip & 0x0001 ? '1' : '0') - -/** - * Rainbow table Came Twee. - */ -static const uint32_t came_twee_magic_numbers_xor[15] = { - 0x0E0E0E00, - 0x1D1D1D11, - 0x2C2C2C22, - 0x3B3B3B33, - 0x4A4A4A44, - 0x59595955, - 0x68686866, - 0x77777777, - 0x86868688, - 0x95959599, - 0xA4A4A4AA, - 0xB3B3B3BB, - 0xC2C2C2CC, - 0xD1D1D1DD, - 0xE0E0E0EE, -}; - -static const SubGhzBlockConst subghz_protocol_came_twee_const = { - .te_short = 500, - .te_long = 1000, - .te_delta = 250, - .min_count_bit_for_found = 54, -}; - -struct SubGhzProtocolDecoderCameTwee { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - ManchesterState manchester_saved_state; -}; - -struct SubGhzProtocolEncoderCameTwee { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - CameTweeDecoderStepReset = 0, - CameTweeDecoderStepDecoderData, -} CameTweeDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = { - .alloc = subghz_protocol_decoder_came_twee_alloc, - .free = subghz_protocol_decoder_came_twee_free, - - .feed = subghz_protocol_decoder_came_twee_feed, - .reset = subghz_protocol_decoder_came_twee_reset, - - .get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data, - .serialize = subghz_protocol_decoder_came_twee_serialize, - .deserialize = subghz_protocol_decoder_came_twee_deserialize, - .get_string = subghz_protocol_decoder_came_twee_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = { - .alloc = subghz_protocol_encoder_came_twee_alloc, - .free = subghz_protocol_encoder_came_twee_free, - - .deserialize = subghz_protocol_encoder_came_twee_deserialize, - .stop = subghz_protocol_encoder_came_twee_stop, - .yield = subghz_protocol_encoder_came_twee_yield, -}; - -const SubGhzProtocol subghz_protocol_came_twee = { - .name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_came_twee_decoder, - .encoder = &subghz_protocol_came_twee_encoder, -}; - -void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee)); - - instance->base.protocol = &subghz_protocol_came_twee; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!! - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_came_twee_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderCameTwee* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static LevelDuration - subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) { - LevelDuration data = {.duration = 0, .level = 0}; - switch(result) { - case ManchesterEncoderResultShortLow: - data.duration = subghz_protocol_came_twee_const.te_short; - data.level = false; - break; - case ManchesterEncoderResultLongLow: - data.duration = subghz_protocol_came_twee_const.te_long; - data.level = false; - break; - case ManchesterEncoderResultLongHigh: - data.duration = subghz_protocol_came_twee_const.te_long; - data.level = true; - break; - case ManchesterEncoderResultShortHigh: - data.duration = subghz_protocol_came_twee_const.te_short; - data.level = true; - break; - - default: - furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); - break; - } - return level_duration_make(data.level, data.duration); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance - */ -static void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) { - furi_assert(instance); - size_t index = 0; - - ManchesterEncoderState enc_state; - manchester_encoder_reset(&enc_state); - ManchesterEncoderResult result; - - uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask - - for(int i = 14; i >= 0; i--) { - temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) | - (instance->generic.serial ^ came_twee_magic_numbers_xor[i]); - - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_came_twee_add_duration_to_upload(result); - manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_came_twee_add_duration_to_upload(result); - } - instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51); - } - instance->encoder.size_upload = index; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) { - /* Came Twee 54 bit, rolling code 15 parcels with - * a decreasing counter from 0xE to 0x0 - * with originally coded dip switches on the console 10 bit code - * - * 0x003FFF72E04A6FEE - * 0x003FFF72D17B5EDD - * 0x003FFF72C2684DCC - * 0x003FFF72B3193CBB - * 0x003FFF72A40E2BAA - * 0x003FFF72953F1A99 - * 0x003FFF72862C0988 - * 0x003FFF7277DDF877 - * 0x003FFF7268C2E766 - * 0x003FFF7259F3D655 - * 0x003FFF724AE0C544 - * 0x003FFF723B91B433 - * 0x003FFF722C86A322 - * 0x003FFF721DB79211 - * 0x003FFF720EA48100 - * - * decryption - * the last 32 bits, do XOR by the desired number, divide the result by 4, - * convert the first 16 bits of the resulting 32-bit number to bin and do - * bit-by-bit mirroring, adding up to 10 bits - * - * Example - * Step 1. 0x003FFF721DB79211 => 0x1DB79211 - * Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00 - * Step 4. 0x00AA8F00 / 4 => 0x002AA3C0 - * Step 5. 0x002AA3C0 => 0x002A - * Step 6. 0x002A bin => b101010 - * Step 7. b101010 => b0101010000 - * Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off - */ - - uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF); - uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF); - - data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]); - instance->serial = data; - data /= 4; - instance->btn = (data >> 4) & 0x0F; - data >>= 16; - data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16); - instance->cnt = data >> 6; -} - -bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderCameTwee* 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_came_twee_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_came_twee_remote_controller(&instance->generic); - subghz_protocol_encoder_came_twee_get_upload(instance); - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_came_twee_stop(void* context) { - SubGhzProtocolEncoderCameTwee* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_came_twee_yield(void* context) { - SubGhzProtocolEncoderCameTwee* 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_came_twee_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee)); - instance->base.protocol = &subghz_protocol_came_twee; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_came_twee_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - free(instance); -} - -void subghz_protocol_decoder_came_twee_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - instance->decoder.parser_step = CameTweeDecoderStepReset; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case CameTweeDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) < - subghz_protocol_came_twee_const.te_delta * 20)) { - //Found header CAME - instance->decoder.parser_step = CameTweeDecoderStepDecoderData; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongLow, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } - break; - case CameTweeDecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < - subghz_protocol_came_twee_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < - subghz_protocol_came_twee_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 + - subghz_protocol_came_twee_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_came_twee_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongLow, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } else { - instance->decoder.parser_step = CameTweeDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) < - subghz_protocol_came_twee_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) < - subghz_protocol_came_twee_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = CameTweeDecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_came_twee_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_came_twee_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_came_twee_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderCameTwee* instance = context; - subghz_protocol_came_twee_remote_controller(&instance->generic); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "Btn:%X\r\n" - "DIP:" DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.btn, - CNT_TO_DIP(instance->generic.cnt)); -} diff --git a/applications/main/subghz/protocols/came_twee.h b/applications/main/subghz/protocols/came_twee.h deleted file mode 100644 index 359b964da..000000000 --- a/applications/main/subghz/protocols/came_twee.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_CAME_TWEE_NAME "CAME TWEE" - -typedef struct SubGhzProtocolDecoderCameTwee SubGhzProtocolDecoderCameTwee; -typedef struct SubGhzProtocolEncoderCameTwee SubGhzProtocolEncoderCameTwee; - -extern const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder; -extern const SubGhzProtocol subghz_protocol_came_twee; - -/** - * Allocate SubGhzProtocolEncoderCameTwee. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderCameTwee* pointer to a SubGhzProtocolEncoderCameTwee instance - */ -void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderCameTwee. - * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance - */ -void subghz_protocol_encoder_came_twee_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance - */ -void subghz_protocol_encoder_came_twee_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderCameTwee instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_came_twee_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderCameTwee. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderCameTwee* pointer to a SubGhzProtocolDecoderCameTwee instance - */ -void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderCameTwee. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - */ -void subghz_protocol_decoder_came_twee_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderCameTwee. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - */ -void subghz_protocol_decoder_came_twee_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderCameTwee. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_came_twee_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderCameTwee. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderCameTwee instance - * @param output Resulting text - */ -void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/chamberlain_code.c b/applications/main/subghz/protocols/chamberlain_code.c deleted file mode 100644 index 9c8e5ee4a..000000000 --- a/applications/main/subghz/protocols/chamberlain_code.c +++ /dev/null @@ -1,499 +0,0 @@ -#include "chamberlain_code.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolChamb_Code" - -#define CHAMBERLAIN_CODE_BIT_STOP 0b0001 -#define CHAMBERLAIN_CODE_BIT_1 0b0011 -#define CHAMBERLAIN_CODE_BIT_0 0b0111 - -#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F -#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F -#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F - -#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101 -#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001 -#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001 - -#define CHAMBERLAIN_7_CODE_DIP_PATTERN "%c%c%c%c%c%c%c" -#define CHAMBERLAIN_7_CODE_DATA_TO_DIP(dip) \ - (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ - (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ - (dip & 0x0001 ? '1' : '0') - -#define CHAMBERLAIN_8_CODE_DIP_PATTERN "%c%c%c%c%cx%c%c" -#define CHAMBERLAIN_8_CODE_DATA_TO_DIP(dip) \ - (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ - (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \ - (dip & 0x0002 ? '1' : '0') - -#define CHAMBERLAIN_9_CODE_DIP_PATTERN "%c%c%c%c%c%c%c%c%c" -#define CHAMBERLAIN_9_CODE_DATA_TO_DIP(dip) \ - (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \ - (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), \ - (dip & 0x0001 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), (dip & 0x0004 ? '1' : '0') - -static const SubGhzBlockConst subghz_protocol_chamb_code_const = { - .te_short = 1000, - .te_long = 3000, - .te_delta = 200, - .min_count_bit_for_found = 10, -}; - -struct SubGhzProtocolDecoderChamb_Code { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderChamb_Code { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - Chamb_CodeDecoderStepReset = 0, - Chamb_CodeDecoderStepFoundStartBit, - Chamb_CodeDecoderStepSaveDuration, - Chamb_CodeDecoderStepCheckDuration, -} Chamb_CodeDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder = { - .alloc = subghz_protocol_decoder_chamb_code_alloc, - .free = subghz_protocol_decoder_chamb_code_free, - - .feed = subghz_protocol_decoder_chamb_code_feed, - .reset = subghz_protocol_decoder_chamb_code_reset, - - .get_hash_data = subghz_protocol_decoder_chamb_code_get_hash_data, - .serialize = subghz_protocol_decoder_chamb_code_serialize, - .deserialize = subghz_protocol_decoder_chamb_code_deserialize, - .get_string = subghz_protocol_decoder_chamb_code_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder = { - .alloc = subghz_protocol_encoder_chamb_code_alloc, - .free = subghz_protocol_encoder_chamb_code_free, - - .deserialize = subghz_protocol_encoder_chamb_code_deserialize, - .stop = subghz_protocol_encoder_chamb_code_stop, - .yield = subghz_protocol_encoder_chamb_code_yield, -}; - -const SubGhzProtocol subghz_protocol_chamb_code = { - .name = SUBGHZ_PROTOCOL_CHAMB_CODE_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_chamb_code_decoder, - .encoder = &subghz_protocol_chamb_code_encoder, -}; - -void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolEncoderChamb_Code)); - - instance->base.protocol = &subghz_protocol_chamb_code; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 24; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_chamb_code_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderChamb_Code* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static uint64_t subghz_protocol_chamb_bit_to_code(uint64_t data, uint8_t size) { - uint64_t data_res = 0; - for(uint8_t i = 0; i < size; i++) { - if(!(bit_read(data, size - i - 1))) { - data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_0; - } else { - data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_1; - } - } - return data_res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderChamb_Code instance - * @return true On success - */ -static bool - subghz_protocol_encoder_chamb_code_get_upload(SubGhzProtocolEncoderChamb_Code* instance) { - furi_assert(instance); - - uint64_t data = subghz_protocol_chamb_bit_to_code( - instance->generic.data, instance->generic.data_count_bit); - - switch(instance->generic.data_count_bit) { - case 7: - data = ((data >> 4) << 16) | (data & 0xF) << 4 | CHAMBERLAIN_7_CODE_MASK_CHECK; - break; - case 8: - data = ((data >> 12) << 16) | (data & 0xFF) << 4 | CHAMBERLAIN_8_CODE_MASK_CHECK; - break; - case 9: - data = (data << 4) | CHAMBERLAIN_9_CODE_MASK_CHECK; - break; - - default: - FURI_LOG_E(TAG, "Invalid bits count"); - return false; - break; - } -#define UPLOAD_HEX_DATA_SIZE 10 - uint8_t upload_hex_data[UPLOAD_HEX_DATA_SIZE] = {0}; - size_t upload_hex_count_bit = 0; - - //insert guard time - for(uint8_t i = 0; i < 36; i++) { - subghz_protocol_blocks_set_bit_array( - 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); - } - - //insert data - switch(instance->generic.data_count_bit) { - case 7: - case 9: - for(uint8_t i = 44; i > 0; i--) { - if(!bit_read(data, i - 1)) { - subghz_protocol_blocks_set_bit_array( - 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); - } else { - subghz_protocol_blocks_set_bit_array( - 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); - } - } - break; - case 8: - for(uint8_t i = 40; i > 0; i--) { - if(!bit_read(data, i - 1)) { - subghz_protocol_blocks_set_bit_array( - 0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); - } else { - subghz_protocol_blocks_set_bit_array( - 1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE); - } - } - break; - } - - instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array( - upload_hex_data, - upload_hex_count_bit, - instance->encoder.upload, - instance->encoder.size_upload, - subghz_protocol_chamb_code_const.te_short, - SubGhzProtocolBlockAlignBitLeft); - - return true; -} - -bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderChamb_Code* 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_chamb_code_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); - - if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_chamb_code_stop(void* context) { - SubGhzProtocolEncoderChamb_Code* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context) { - SubGhzProtocolEncoderChamb_Code* 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_chamb_code_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolDecoderChamb_Code)); - instance->base.protocol = &subghz_protocol_chamb_code; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_chamb_code_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - free(instance); -} - -void subghz_protocol_decoder_chamb_code_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - instance->decoder.parser_step = Chamb_CodeDecoderStepReset; -} - -static bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) { - uint64_t data_tmp = data[0]; - uint64_t data_res = 0; - for(uint8_t i = 0; i < size; i++) { - if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) { - bit_write(data_res, i, 0); - } else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) { - bit_write(data_res, i, 1); - } else { - return false; - } - data_tmp >>= 4; - } - data[0] = data_res; - return true; -} - -static bool subghz_protocol_decoder_chamb_code_check_mask_and_parse( - SubGhzProtocolDecoderChamb_Code* instance) { - furi_assert(instance); - if(instance->decoder.decode_count_bit > - subghz_protocol_chamb_code_const.min_count_bit_for_found + 1) - return false; - - if((instance->decoder.decode_data & CHAMBERLAIN_7_CODE_MASK) == - CHAMBERLAIN_7_CODE_MASK_CHECK) { - instance->decoder.decode_count_bit = 7; - instance->decoder.decode_data &= ~CHAMBERLAIN_7_CODE_MASK; - instance->decoder.decode_data = (instance->decoder.decode_data >> 12) | - ((instance->decoder.decode_data >> 4) & 0xF); - } else if( - (instance->decoder.decode_data & CHAMBERLAIN_8_CODE_MASK) == - CHAMBERLAIN_8_CODE_MASK_CHECK) { - instance->decoder.decode_count_bit = 8; - instance->decoder.decode_data &= ~CHAMBERLAIN_8_CODE_MASK; - instance->decoder.decode_data = instance->decoder.decode_data >> 4 | - CHAMBERLAIN_CODE_BIT_0 << 8; //DIP 6 no use - } else if( - (instance->decoder.decode_data & CHAMBERLAIN_9_CODE_MASK) == - CHAMBERLAIN_9_CODE_MASK_CHECK) { - instance->decoder.decode_count_bit = 9; - instance->decoder.decode_data &= ~CHAMBERLAIN_9_CODE_MASK; - instance->decoder.decode_data >>= 4; - } else { - return false; - } - return subghz_protocol_chamb_code_to_bit( - &instance->decoder.decode_data, instance->decoder.decode_count_bit); -} - -void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - switch(instance->decoder.parser_step) { - case Chamb_CodeDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 39) < - subghz_protocol_chamb_code_const.te_delta * 20)) { - //Found header Chamb_Code - instance->decoder.parser_step = Chamb_CodeDecoderStepFoundStartBit; - } - break; - case Chamb_CodeDecoderStepFoundStartBit: - if((level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < - subghz_protocol_chamb_code_const.te_delta)) { - //Found start bit Chamb_Code - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = instance->decoder.decode_data << 4 | - CHAMBERLAIN_CODE_BIT_STOP; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Chamb_CodeDecoderStepReset; - } - break; - case Chamb_CodeDecoderStepSaveDuration: - if(!level) { //save interval - if(duration > subghz_protocol_chamb_code_const.te_short * 5) { - if(instance->decoder.decode_count_bit >= - subghz_protocol_chamb_code_const.min_count_bit_for_found) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - if(subghz_protocol_decoder_chamb_code_check_mask_and_parse(instance)) { - 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 = Chamb_CodeDecoderStepReset; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Chamb_CodeDecoderStepCheckDuration; - } - } else { - instance->decoder.parser_step = Chamb_CodeDecoderStepReset; - } - break; - case Chamb_CodeDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF( //Found stop bit Chamb_Code - instance->decoder.te_last, - subghz_protocol_chamb_code_const.te_short * 3) < - subghz_protocol_chamb_code_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) < - subghz_protocol_chamb_code_const.te_delta)) { - instance->decoder.decode_data = instance->decoder.decode_data << 4 | - CHAMBERLAIN_CODE_BIT_STOP; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short * 2) < - subghz_protocol_chamb_code_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 2) < - subghz_protocol_chamb_code_const.te_delta)) { - instance->decoder.decode_data = instance->decoder.decode_data << 4 | - CHAMBERLAIN_CODE_BIT_1; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short) < - subghz_protocol_chamb_code_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 3) < - subghz_protocol_chamb_code_const.te_delta)) { - instance->decoder.decode_data = instance->decoder.decode_data << 4 | - CHAMBERLAIN_CODE_BIT_0; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Chamb_CodeDecoderStepReset; - } - - } else { - instance->decoder.parser_step = Chamb_CodeDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_chamb_code_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit > - subghz_protocol_chamb_code_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_chamb_code_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderChamb_Code* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%03lX\r\n" - "Yek:0x%03lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo); - - switch(instance->generic.data_count_bit) { - case 7: - furi_string_cat_printf( - output, - "DIP:" CHAMBERLAIN_7_CODE_DIP_PATTERN "\r\n", - CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo)); - break; - case 8: - furi_string_cat_printf( - output, - "DIP:" CHAMBERLAIN_8_CODE_DIP_PATTERN "\r\n", - CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo)); - break; - case 9: - furi_string_cat_printf( - output, - "DIP:" CHAMBERLAIN_9_CODE_DIP_PATTERN "\r\n", - CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo)); - break; - - default: - break; - } -} diff --git a/applications/main/subghz/protocols/chamberlain_code.h b/applications/main/subghz/protocols/chamberlain_code.h deleted file mode 100644 index f87b64d90..000000000 --- a/applications/main/subghz/protocols/chamberlain_code.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_CHAMB_CODE_NAME "Cham_Code" - -typedef struct SubGhzProtocolDecoderChamb_Code SubGhzProtocolDecoderChamb_Code; -typedef struct SubGhzProtocolEncoderChamb_Code SubGhzProtocolEncoderChamb_Code; - -extern const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder; -extern const SubGhzProtocol subghz_protocol_chamb_code; - -/** - * Allocate SubGhzProtocolEncoderChamb_Code. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderChamb_Code* pointer to a SubGhzProtocolEncoderChamb_Code instance - */ -void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderChamb_Code. - * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance - */ -void subghz_protocol_encoder_chamb_code_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance - */ -void subghz_protocol_encoder_chamb_code_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderChamb_Code. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderChamb_Code* pointer to a SubGhzProtocolDecoderChamb_Code instance - */ -void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderChamb_Code. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - */ -void subghz_protocol_decoder_chamb_code_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderChamb_Code. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - */ -void subghz_protocol_decoder_chamb_code_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderChamb_Code. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_chamb_code_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderChamb_Code. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance - * @param output Resulting text - */ -void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/clemsa.c b/applications/main/subghz/protocols/clemsa.c deleted file mode 100644 index a2cb7a18b..000000000 --- a/applications/main/subghz/protocols/clemsa.c +++ /dev/null @@ -1,365 +0,0 @@ -#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); - - if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break; - 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, - SubGhzRadioPreset* 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, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderClemsa* instance = context; - subghz_protocol_clemsa_check_remote_controller(&instance->generic); - //uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF); - furi_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/applications/main/subghz/protocols/clemsa.h b/applications/main/subghz/protocols/clemsa.h deleted file mode 100644 index 8858c1a3b..000000000 --- a/applications/main/subghz/protocols/clemsa.h +++ /dev/null @@ -1,107 +0,0 @@ -#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, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_clemsa_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* 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, FuriString* output); diff --git a/applications/main/subghz/protocols/doitrand.c b/applications/main/subghz/protocols/doitrand.c deleted file mode 100644 index 6b31d4f27..000000000 --- a/applications/main/subghz/protocols/doitrand.c +++ /dev/null @@ -1,356 +0,0 @@ -#include "doitrand.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolDoitrand" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" -#define CNT_TO_DIP(dip) \ - (dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ - (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \ - (dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \ - (dip & 0x0002 ? '1' : '0') - -static const SubGhzBlockConst subghz_protocol_doitrand_const = { - .te_short = 400, - .te_long = 1100, - .te_delta = 150, - .min_count_bit_for_found = 37, -}; - -struct SubGhzProtocolDecoderDoitrand { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderDoitrand { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - DoitrandDecoderStepReset = 0, - DoitrandDecoderStepFoundStartBit, - DoitrandDecoderStepSaveDuration, - DoitrandDecoderStepCheckDuration, -} DoitrandDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = { - .alloc = subghz_protocol_decoder_doitrand_alloc, - .free = subghz_protocol_decoder_doitrand_free, - - .feed = subghz_protocol_decoder_doitrand_feed, - .reset = subghz_protocol_decoder_doitrand_reset, - - .get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data, - .serialize = subghz_protocol_decoder_doitrand_serialize, - .deserialize = subghz_protocol_decoder_doitrand_deserialize, - .get_string = subghz_protocol_decoder_doitrand_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = { - .alloc = subghz_protocol_encoder_doitrand_alloc, - .free = subghz_protocol_encoder_doitrand_free, - - .deserialize = subghz_protocol_encoder_doitrand_deserialize, - .stop = subghz_protocol_encoder_doitrand_stop, - .yield = subghz_protocol_encoder_doitrand_yield, -}; - -const SubGhzProtocol subghz_protocol_doitrand = { - .name = SUBGHZ_PROTOCOL_DOITRAND_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_doitrand_decoder, - .encoder = &subghz_protocol_doitrand_encoder, -}; - -void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand)); - - instance->base.protocol = &subghz_protocol_doitrand; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_doitrand_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderDoitrand* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance - * @return true On success - */ -static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderDoitrand* 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_doitrand_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); - - if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_doitrand_stop(void* context) { - SubGhzProtocolEncoderDoitrand* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) { - SubGhzProtocolEncoderDoitrand* 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_doitrand_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand)); - instance->base.protocol = &subghz_protocol_doitrand; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_doitrand_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - free(instance); -} - -void subghz_protocol_decoder_doitrand_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - instance->decoder.parser_step = DoitrandDecoderStepReset; -} - -void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - - switch(instance->decoder.parser_step) { - case DoitrandDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) < - subghz_protocol_doitrand_const.te_delta * 30)) { - //Found Preambula - instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; - } - break; - case DoitrandDecoderStepFoundStartBit: - if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) < - subghz_protocol_doitrand_const.te_delta * 3))) { - //Found start bit - instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = DoitrandDecoderStepReset; - } - break; - case DoitrandDecoderStepSaveDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 + - subghz_protocol_doitrand_const.te_delta)) { - instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit == - subghz_protocol_doitrand_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = DoitrandDecoderStepCheckDuration; - } - } - break; - case DoitrandDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) < - subghz_protocol_doitrand_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) < - subghz_protocol_doitrand_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) < - subghz_protocol_doitrand_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) < - subghz_protocol_doitrand_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = DoitrandDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = DoitrandDecoderStepReset; - } - } else { - instance->decoder.parser_step = DoitrandDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) { - /* -* 67892345 0 k 1 -* 0000082F5F => 00000000000000000 10 000010111101011111 -* 0002082F5F => 00000000000100000 10 000010111101011111 -* 0200082F5F => 00010000000000000 10 000010111101011111 -* 0400082F5F => 00100000000000000 10 000010111101011111 -* 0800082F5F => 01000000000000000 10 000010111101011111 -* 1000082F5F => 10000000000000000 10 000010111101011111 -* 0020082F5F => 00000001000000000 10 000010111101011111 -* 0040082F5F => 00000010000000000 10 000010111101011111 -* 0080082F5F => 00000100000000000 10 000010111101011111 -* 0100082F5F => 00001000000000000 10 000010111101011111 -* 000008AF5F => 00000000000000000 10 001010111101011111 -* 1FE208AF5F => 11111111000100000 10 001010111101011111 -* -* 0...9 - DIP -* k- KEY -*/ - instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1); - instance->btn = ((instance->data >> 18) & 0x3); -} - -uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_doitrand_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_doitrand_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_doitrand_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderDoitrand* instance = context; - subghz_protocol_doitrand_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%02lX%08lX\r\n" - "Btn:%X\r\n" - "DIP:" DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.btn, - CNT_TO_DIP(instance->generic.cnt)); -} diff --git a/applications/main/subghz/protocols/doitrand.h b/applications/main/subghz/protocols/doitrand.h deleted file mode 100644 index 30f1fffd0..000000000 --- a/applications/main/subghz/protocols/doitrand.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand" - -typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand; -typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand; - -extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder; -extern const SubGhzProtocol subghz_protocol_doitrand; - -/** - * Allocate SubGhzProtocolEncoderDoitrand. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance - */ -void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderDoitrand. - * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance - */ -void subghz_protocol_encoder_doitrand_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance - */ -void subghz_protocol_encoder_doitrand_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderDoitrand instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_doitrand_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderDoitrand. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance - */ -void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderDoitrand. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - */ -void subghz_protocol_decoder_doitrand_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderDoitrand. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - */ -void subghz_protocol_decoder_doitrand_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderDoitrand. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_doitrand_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderDoitrand. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderDoitrand instance - * @param output Resulting text - */ -void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/dooya.c b/applications/main/subghz/protocols/dooya.c deleted file mode 100644 index c70b6d54e..000000000 --- a/applications/main/subghz/protocols/dooya.c +++ /dev/null @@ -1,447 +0,0 @@ -#include "dooya.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolDooya" - -#define DOYA_SINGLE_CHANNEL 0xFF - -static const SubGhzBlockConst subghz_protocol_dooya_const = { - .te_short = 366, - .te_long = 733, - .te_delta = 120, - .min_count_bit_for_found = 40, -}; - -struct SubGhzProtocolDecoderDooya { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderDooya { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - DooyaDecoderStepReset = 0, - DooyaDecoderStepFoundStartBit, - DooyaDecoderStepSaveDuration, - DooyaDecoderStepCheckDuration, -} DooyaDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = { - .alloc = subghz_protocol_decoder_dooya_alloc, - .free = subghz_protocol_decoder_dooya_free, - - .feed = subghz_protocol_decoder_dooya_feed, - .reset = subghz_protocol_decoder_dooya_reset, - - .get_hash_data = subghz_protocol_decoder_dooya_get_hash_data, - .serialize = subghz_protocol_decoder_dooya_serialize, - .deserialize = subghz_protocol_decoder_dooya_deserialize, - .get_string = subghz_protocol_decoder_dooya_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = { - .alloc = subghz_protocol_encoder_dooya_alloc, - .free = subghz_protocol_encoder_dooya_free, - - .deserialize = subghz_protocol_encoder_dooya_deserialize, - .stop = subghz_protocol_encoder_dooya_stop, - .yield = subghz_protocol_encoder_dooya_yield, -}; - -const SubGhzProtocol subghz_protocol_dooya = { - .name = SUBGHZ_PROTOCOL_DOOYA_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_dooya_decoder, - .encoder = &subghz_protocol_dooya_encoder, -}; - -void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya)); - - instance->base.protocol = &subghz_protocol_dooya; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_dooya_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderDooya* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderDooya instance - * @return true On success - */ -static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - - //Send header - if(bit_read(instance->generic.data, 0)) { - instance->encoder.upload[index++] = level_duration_make( - false, - (uint32_t)subghz_protocol_dooya_const.te_long * 12 + - subghz_protocol_dooya_const.te_long); - } else { - instance->encoder.upload[index++] = level_duration_make( - false, - (uint32_t)subghz_protocol_dooya_const.te_long * 12 + - subghz_protocol_dooya_const.te_short); - } - - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderDooya* 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_dooya_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); - - if(!subghz_protocol_encoder_dooya_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_dooya_stop(void* context) { - SubGhzProtocolEncoderDooya* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_dooya_yield(void* context) { - SubGhzProtocolEncoderDooya* 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_dooya_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya)); - instance->base.protocol = &subghz_protocol_dooya; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_dooya_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - free(instance); -} - -void subghz_protocol_decoder_dooya_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - instance->decoder.parser_step = DooyaDecoderStepReset; -} - -void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - - switch(instance->decoder.parser_step) { - case DooyaDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) < - subghz_protocol_dooya_const.te_delta * 20)) { - instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; - } - break; - - case DooyaDecoderStepFoundStartBit: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) < - subghz_protocol_dooya_const.te_delta * 3) { - instance->decoder.parser_step = DooyaDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - } - } else if( - DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) < - subghz_protocol_dooya_const.te_delta * 5) { - break; - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - } - break; - - case DooyaDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = DooyaDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - } - break; - - case DooyaDecoderStepCheckDuration: - if(!level) { - if(duration >= (subghz_protocol_dooya_const.te_long * 4)) { - //add last bit - if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < - subghz_protocol_dooya_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else if( - DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < - subghz_protocol_dooya_const.te_delta * 2) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - break; - } - instance->decoder.parser_step = DooyaDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit == - subghz_protocol_dooya_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); - } - break; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) < - subghz_protocol_dooya_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) < - subghz_protocol_dooya_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = DooyaDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) < - subghz_protocol_dooya_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) < - subghz_protocol_dooya_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = DooyaDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - } - } else { - instance->decoder.parser_step = DooyaDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { - /* - * serial s/m ch key - * long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 - * - * short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011 - * 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100 - * - * press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101 - * - * long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 - * - * short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001 - * 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110 - * - * serial: 3 byte serial number - * s/m: single (b0000) / multi (b0001) channel console - * ch: channel if single (always b0101) or multi - * key: 0b00010001 - long press up - * 0b00011110 - short press up - * 0b00110011 - long press down - * 0b00111100 - short press down - * 0b01010101 - press stop - * 0b01111001 - press up + down - * 0b10000000 - press up + stop - * 0b10000001 - press down + stop - * 0b11001100 - press P2 - * -*/ - - instance->serial = (instance->data >> 16); - if((instance->data >> 12) & 0x0F) { - instance->cnt = (instance->data >> 8) & 0x0F; - } else { - instance->cnt = DOYA_SINGLE_CHANNEL; - } - instance->btn = instance->data & 0xFF; -} - -uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_dooya_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_dooya_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; -} - -/** - * Get button name. - * @param btn Button number, 8 bit - */ -static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) { - const char* btn_name; - switch(btn) { - case 0b00010001: - btn_name = "Up_Long"; - break; - case 0b00011110: - btn_name = "Up_Short"; - break; - case 0b00110011: - btn_name = "Down_Long"; - break; - case 0b00111100: - btn_name = "Down_Short"; - break; - case 0b01010101: - btn_name = "Stop"; - break; - case 0b01111001: - btn_name = "Up+Down"; - break; - case 0b10000000: - btn_name = "Up+Stop"; - break; - case 0b10000001: - btn_name = "Down+Stop"; - break; - case 0b11001100: - btn_name = "P2"; - break; - default: - btn_name = "Unknown"; - break; - } - return btn_name; -} - -void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderDooya* instance = context; - - subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%010llX\r\n" - "Sn:0x%08lX\r\n" - "Btn:%s\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - instance->generic.data, - instance->generic.serial, - subghz_protocol_dooya_get_name_button(instance->generic.btn)); - if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) { - furi_string_cat_printf(output, "Ch:Single\r\n"); - } else { - furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt); - } -} diff --git a/applications/main/subghz/protocols/dooya.h b/applications/main/subghz/protocols/dooya.h deleted file mode 100644 index f0cf843c0..000000000 --- a/applications/main/subghz/protocols/dooya.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya" - -typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya; -typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya; - -extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder; -extern const SubGhzProtocol subghz_protocol_dooya; - -/** - * Allocate SubGhzProtocolEncoderDooya. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance - */ -void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderDooya. - * @param context Pointer to a SubGhzProtocolEncoderDooya instance - */ -void subghz_protocol_encoder_dooya_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderDooya instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderDooya instance - */ -void subghz_protocol_encoder_dooya_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderDooya instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_dooya_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderDooya. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance - */ -void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderDooya. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - */ -void subghz_protocol_decoder_dooya_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderDooya. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - */ -void subghz_protocol_decoder_dooya_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderDooya. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_dooya_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderDooya. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderDooya instance - * @param output Resulting text - */ -void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/faac_slh.c b/applications/main/subghz/protocols/faac_slh.c deleted file mode 100644 index 7572bd8ab..000000000 --- a/applications/main/subghz/protocols/faac_slh.c +++ /dev/null @@ -1,512 +0,0 @@ -#include "faac_slh.h" -#include "../subghz_keystore.h" -#include -#include "keeloq_common.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolFaacSLH" - -static const SubGhzBlockConst subghz_protocol_faac_slh_const = { - .te_short = 255, - .te_long = 595, - .te_delta = 100, - .min_count_bit_for_found = 64, -}; - -struct SubGhzProtocolDecoderFaacSLH { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - SubGhzKeystore* keystore; - const char* manufacture_name; -}; - -struct SubGhzProtocolEncoderFaacSLH { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - SubGhzKeystore* keystore; - const char* manufacture_name; -}; - -typedef enum { - FaacSLHDecoderStepReset = 0, - FaacSLHDecoderStepFoundPreambula, - FaacSLHDecoderStepSaveDuration, - FaacSLHDecoderStepCheckDuration, -} FaacSLHDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder = { - .alloc = subghz_protocol_decoder_faac_slh_alloc, - .free = subghz_protocol_decoder_faac_slh_free, - - .feed = subghz_protocol_decoder_faac_slh_feed, - .reset = subghz_protocol_decoder_faac_slh_reset, - - .get_hash_data = subghz_protocol_decoder_faac_slh_get_hash_data, - .serialize = subghz_protocol_decoder_faac_slh_serialize, - .deserialize = subghz_protocol_decoder_faac_slh_deserialize, - .get_string = subghz_protocol_decoder_faac_slh_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder = { - .alloc = subghz_protocol_encoder_faac_slh_alloc, - .free = subghz_protocol_encoder_faac_slh_free, - - .deserialize = subghz_protocol_encoder_faac_slh_deserialize, - .stop = subghz_protocol_encoder_faac_slh_stop, - .yield = subghz_protocol_encoder_faac_slh_yield, -}; - -const SubGhzProtocol subghz_protocol_faac_slh = { - .name = SUBGHZ_PROTOCOL_FAAC_SLH_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_faac_slh_decoder, - .encoder = &subghz_protocol_faac_slh_encoder, -}; - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - */ -static void subghz_protocol_faac_slh_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name); - -void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolEncoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolEncoderFaacSLH)); - - instance->base.protocol = &subghz_protocol_faac_slh; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_faac_slh_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderFaacSLH* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) { - instance->generic.cnt++; - uint32_t fix = instance->generic.serial << 4 | instance->generic.btn; - uint32_t hop = 0; - uint32_t decrypt = 0; - uint64_t man = 0; - int res = 0; - char fixx[8] = {}; - int shiftby = 32; - for(int i = 0; i < 8; i++) { - fixx[i] = (fix >> (shiftby -= 4)) & 0xF; - } - if((instance->generic.cnt % 2) == 0) { - decrypt = fixx[6] << 28 | fixx[7] << 24 | fixx[5] << 20 | - (instance->generic.cnt & 0xFFFFF); - } else { - decrypt = fixx[2] << 28 | fixx[3] << 24 | fixx[4] << 20 | - (instance->generic.cnt & 0xFFFFF); - } - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_FAAC: - //FAAC Learning - man = subghz_protocol_keeloq_common_faac_learning( - instance->generic.seed, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - } - break; - } - } - if(hop) { - instance->generic.data = (uint64_t)fix << 32 | hop; - } - return true; -} - -bool subghz_protocol_faac_slh_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint32_t cnt, - uint32_t seed, - const char* manufacture_name, - SubGhzRadioPreset* preset) { - furi_assert(context); - // roguemaster don't steal!!! - SubGhzProtocolEncoderFaacSLH* instance = context; - instance->generic.serial = serial; - instance->generic.btn = btn; - instance->generic.cnt = (cnt & 0xFFFFF); - instance->generic.seed = seed; - instance->manufacture_name = manufacture_name; - instance->generic.data_count_bit = 64; - bool res = subghz_protocol_faac_slh_gen_data(instance); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderFaacSLH instance - * @return true On success - */ -static bool subghz_protocol_encoder_faac_slh_get_upload(SubGhzProtocolEncoderFaacSLH* instance) { - furi_assert(instance); - - subghz_protocol_faac_slh_gen_data(instance); - size_t index = 0; - size_t size_upload = 2 + (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; - } - - //Send header - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderFaacSLH* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_E(TAG, "Missing Seed"); - break; - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - - subghz_protocol_faac_slh_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_encoder_faac_slh_get_upload(instance); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_faac_slh_stop(void* context) { - SubGhzProtocolEncoderFaacSLH* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context) { - SubGhzProtocolEncoderFaacSLH* 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_faac_slh_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolDecoderFaacSLH)); - instance->base.protocol = &subghz_protocol_faac_slh; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - return instance; -} - -void subghz_protocol_decoder_faac_slh_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - free(instance); -} - -void subghz_protocol_decoder_faac_slh_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - instance->decoder.parser_step = FaacSLHDecoderStepReset; -} - -void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - - switch(instance->decoder.parser_step) { - case FaacSLHDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < - subghz_protocol_faac_slh_const.te_delta * 3)) { - instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; - } - break; - case FaacSLHDecoderStepFoundPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) < - subghz_protocol_faac_slh_const.te_delta * 3)) { - //Found Preambula - instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = FaacSLHDecoderStepReset; - } - break; - case FaacSLHDecoderStepSaveDuration: - if(level) { - if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 + - subghz_protocol_faac_slh_const.te_delta)) { - instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula; - if(instance->decoder.decode_count_bit == - subghz_protocol_faac_slh_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = FaacSLHDecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = FaacSLHDecoderStepReset; - } - break; - case FaacSLHDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_short) < - subghz_protocol_faac_slh_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long) < - subghz_protocol_faac_slh_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_long) < - subghz_protocol_faac_slh_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_short) < - subghz_protocol_faac_slh_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = FaacSLHDecoderStepReset; - } - } else { - instance->decoder.parser_step = FaacSLHDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manifacture_name Manufacturer name - */ -static void subghz_protocol_faac_slh_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name) { - uint32_t code_fix = instance->data >> 32; - uint32_t code_hop = instance->data & 0xFFFFFFFF; - instance->serial = code_fix >> 4; - instance->btn = code_fix & 0xF; - uint32_t decrypt = 0; - uint64_t man; - - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_FAAC: - // FAAC Learning - man = subghz_protocol_keeloq_common_faac_learning( - instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(code_hop, man); - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - break; - } - } - instance->cnt = decrypt & 0xFFFFF; -} - -uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_faac_slh_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_E(TAG, "Unable to add Seed"); - res = false; - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - - subghz_protocol_faac_slh_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - return res; -} - -bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* 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_faac_slh_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_E(TAG, "Missing Seed"); - break; - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderFaacSLH* instance = context; - subghz_protocol_faac_slh_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - uint32_t code_fix = instance->generic.data >> 32; - uint32_t code_hop = instance->generic.data & 0xFFFFFFFF; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%lX%08lX\r\n" - "Fix:%08lX Cnt:%05lX\r\n" - "Hop:%08lX Btn:%X\r\n" - "Sn:%07lX Sd:%08lX", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - code_fix, - instance->generic.cnt, - code_hop, - instance->generic.btn, - instance->generic.serial, - instance->generic.seed); -} diff --git a/applications/main/subghz/protocols/faac_slh.h b/applications/main/subghz/protocols/faac_slh.h deleted file mode 100644 index 9390da43a..000000000 --- a/applications/main/subghz/protocols/faac_slh.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_FAAC_SLH_NAME "Faac SLH" - -typedef struct SubGhzProtocolDecoderFaacSLH SubGhzProtocolDecoderFaacSLH; -typedef struct SubGhzProtocolEncoderFaacSLH SubGhzProtocolEncoderFaacSLH; - -extern const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder; -extern const SubGhzProtocol subghz_protocol_faac_slh; - -/** - * Allocate SubGhzProtocolEncoderFaacSLH. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderFaacSLH* pointer to a SubGhzProtocolEncoderFaacSLH instance - */ -void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderFaacSLH. - * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance - */ -void subghz_protocol_encoder_faac_slh_free(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 28 bit - * @param btn Button number, 4 bit - * @param cnt Counter value, 16 bit - * @param seed Seed value, 32 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_faac_slh_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint32_t cnt, - uint32_t seed, - const char* manufacture_name, - SubGhzRadioPreset* preset); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance - */ -void subghz_protocol_encoder_faac_slh_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderFaacSLH. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderFaacSLH* pointer to a SubGhzProtocolDecoderFaacSLH instance - */ -void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderFaacSLH. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - */ -void subghz_protocol_decoder_faac_slh_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderFaacSLH. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - */ -void subghz_protocol_decoder_faac_slh_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderFaacSLH. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_faac_slh_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderFaacSLH. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance - * @param output Resulting text - */ -void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/gate_tx.c b/applications/main/subghz/protocols/gate_tx.c deleted file mode 100644 index 4c7c2d484..000000000 --- a/applications/main/subghz/protocols/gate_tx.c +++ /dev/null @@ -1,334 +0,0 @@ -#include "gate_tx.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolGateTx" - -static const SubGhzBlockConst subghz_protocol_gate_tx_const = { - .te_short = 350, - .te_long = 700, - .te_delta = 100, - .min_count_bit_for_found = 24, -}; - -struct SubGhzProtocolDecoderGateTx { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderGateTx { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - GateTXDecoderStepReset = 0, - GateTXDecoderStepFoundStartBit, - GateTXDecoderStepSaveDuration, - GateTXDecoderStepCheckDuration, -} GateTXDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder = { - .alloc = subghz_protocol_decoder_gate_tx_alloc, - .free = subghz_protocol_decoder_gate_tx_free, - - .feed = subghz_protocol_decoder_gate_tx_feed, - .reset = subghz_protocol_decoder_gate_tx_reset, - - .get_hash_data = subghz_protocol_decoder_gate_tx_get_hash_data, - .serialize = subghz_protocol_decoder_gate_tx_serialize, - .deserialize = subghz_protocol_decoder_gate_tx_deserialize, - .get_string = subghz_protocol_decoder_gate_tx_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder = { - .alloc = subghz_protocol_encoder_gate_tx_alloc, - .free = subghz_protocol_encoder_gate_tx_free, - - .deserialize = subghz_protocol_encoder_gate_tx_deserialize, - .stop = subghz_protocol_encoder_gate_tx_stop, - .yield = subghz_protocol_encoder_gate_tx_yield, -}; - -const SubGhzProtocol subghz_protocol_gate_tx = { - .name = SUBGHZ_PROTOCOL_GATE_TX_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_gate_tx_decoder, - .encoder = &subghz_protocol_gate_tx_encoder, -}; - -void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderGateTx* instance = malloc(sizeof(SubGhzProtocolEncoderGateTx)); - - instance->base.protocol = &subghz_protocol_gate_tx; - 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_gate_tx_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderGateTx* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderGateTx instance - * @return true On success - */ -static bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGateTx* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short * 49); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderGateTx* 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_gate_tx_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); - - if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_gate_tx_stop(void* context) { - SubGhzProtocolEncoderGateTx* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context) { - SubGhzProtocolEncoderGateTx* 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_gate_tx_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderGateTx* instance = malloc(sizeof(SubGhzProtocolDecoderGateTx)); - instance->base.protocol = &subghz_protocol_gate_tx; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_gate_tx_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - free(instance); -} - -void subghz_protocol_decoder_gate_tx_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - instance->decoder.parser_step = GateTXDecoderStepReset; -} - -void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - - switch(instance->decoder.parser_step) { - case GateTXDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short * 47) < - subghz_protocol_gate_tx_const.te_delta * 47)) { - //Found Preambula - instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; - } - break; - case GateTXDecoderStepFoundStartBit: - if(level && ((DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < - subghz_protocol_gate_tx_const.te_delta * 3))) { - //Found start bit - instance->decoder.parser_step = GateTXDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = GateTXDecoderStepReset; - } - break; - case GateTXDecoderStepSaveDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 + - subghz_protocol_gate_tx_const.te_delta)) { - instance->decoder.parser_step = GateTXDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_gate_tx_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = GateTXDecoderStepCheckDuration; - } - } - break; - case GateTXDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_short) < - subghz_protocol_gate_tx_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) < - subghz_protocol_gate_tx_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = GateTXDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_long) < - subghz_protocol_gate_tx_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short) < - subghz_protocol_gate_tx_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = GateTXDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = GateTXDecoderStepReset; - } - } else { - instance->decoder.parser_step = GateTXDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_gate_tx_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_gate_tx_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_gate_tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_gate_tx_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_gate_tx_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderGateTx* instance = context; - subghz_protocol_gate_tx_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%06lX\r\n" - "Sn:%05lX Btn:%X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFF), - instance->generic.serial, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/gate_tx.h b/applications/main/subghz/protocols/gate_tx.h deleted file mode 100644 index 4bfba3597..000000000 --- a/applications/main/subghz/protocols/gate_tx.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_GATE_TX_NAME "GateTX" - -typedef struct SubGhzProtocolDecoderGateTx SubGhzProtocolDecoderGateTx; -typedef struct SubGhzProtocolEncoderGateTx SubGhzProtocolEncoderGateTx; - -extern const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder; -extern const SubGhzProtocol subghz_protocol_gate_tx; - -/** - * Allocate SubGhzProtocolEncoderGateTx. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderGateTx* pointer to a SubGhzProtocolEncoderGateTx instance - */ -void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderGateTx. - * @param context Pointer to a SubGhzProtocolEncoderGateTx instance - */ -void subghz_protocol_encoder_gate_tx_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderGateTx instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderGateTx instance - */ -void subghz_protocol_encoder_gate_tx_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderGateTx instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderGateTx. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderGateTx* pointer to a SubGhzProtocolDecoderGateTx instance - */ -void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderGateTx. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - */ -void subghz_protocol_decoder_gate_tx_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderGateTx. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - */ -void subghz_protocol_decoder_gate_tx_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderGateTx. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_gate_tx_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderGateTx. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderGateTx instance - * @param output Resulting text - */ -void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek.c b/applications/main/subghz/protocols/holtek.c deleted file mode 100644 index 8aaad3b71..000000000 --- a/applications/main/subghz/protocols/holtek.c +++ /dev/null @@ -1,374 +0,0 @@ -#include "holtek.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://pdf1.alldatasheet.com/datasheet-pdf/view/82103/HOLTEK/HT640.html - * https://fccid.io/OJM-CMD-HHLR-XXXA - * - */ - -#define TAG "SubGhzProtocolHoltek" - -#define HOLTEK_HEADER_MASK 0xF000000000 -#define HOLTEK_HEADER 0x5000000000 - -static const SubGhzBlockConst subghz_protocol_holtek_const = { - .te_short = 430, - .te_long = 870, - .te_delta = 100, - .min_count_bit_for_found = 40, -}; - -struct SubGhzProtocolDecoderHoltek { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderHoltek { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - HoltekDecoderStepReset = 0, - HoltekDecoderStepFoundStartBit, - HoltekDecoderStepSaveDuration, - HoltekDecoderStepCheckDuration, -} HoltekDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_holtek_decoder = { - .alloc = subghz_protocol_decoder_holtek_alloc, - .free = subghz_protocol_decoder_holtek_free, - - .feed = subghz_protocol_decoder_holtek_feed, - .reset = subghz_protocol_decoder_holtek_reset, - - .get_hash_data = subghz_protocol_decoder_holtek_get_hash_data, - .serialize = subghz_protocol_decoder_holtek_serialize, - .deserialize = subghz_protocol_decoder_holtek_deserialize, - .get_string = subghz_protocol_decoder_holtek_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_holtek_encoder = { - .alloc = subghz_protocol_encoder_holtek_alloc, - .free = subghz_protocol_encoder_holtek_free, - - .deserialize = subghz_protocol_encoder_holtek_deserialize, - .stop = subghz_protocol_encoder_holtek_stop, - .yield = subghz_protocol_encoder_holtek_yield, -}; - -const SubGhzProtocol subghz_protocol_holtek = { - .name = SUBGHZ_PROTOCOL_HOLTEK_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_holtek_decoder, - .encoder = &subghz_protocol_holtek_encoder, -}; - -void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderHoltek* instance = malloc(sizeof(SubGhzProtocolEncoderHoltek)); - - instance->base.protocol = &subghz_protocol_holtek; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_holtek_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderHoltek* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderHoltek instance - * @return true On success - */ -static bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHoltek* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short * 36); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderHoltek* 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_holtek_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); - - if(!subghz_protocol_encoder_holtek_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_holtek_stop(void* context) { - SubGhzProtocolEncoderHoltek* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_holtek_yield(void* context) { - SubGhzProtocolEncoderHoltek* 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_holtek_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderHoltek* instance = malloc(sizeof(SubGhzProtocolDecoderHoltek)); - instance->base.protocol = &subghz_protocol_holtek; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_holtek_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - free(instance); -} - -void subghz_protocol_decoder_holtek_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - instance->decoder.parser_step = HoltekDecoderStepReset; -} - -void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - - switch(instance->decoder.parser_step) { - case HoltekDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short * 36) < - subghz_protocol_holtek_const.te_delta * 36)) { - //Found Preambula - instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; - } - break; - case HoltekDecoderStepFoundStartBit: - if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < - subghz_protocol_holtek_const.te_delta)) { - //Found StartBit - instance->decoder.parser_step = HoltekDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = HoltekDecoderStepReset; - } - break; - case HoltekDecoderStepSaveDuration: - //save duration - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 + - subghz_protocol_holtek_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_holtek_const.min_count_bit_for_found) { - if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { - 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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = HoltekDecoderStepFoundStartBit; - break; - } else { - instance->decoder.te_last = duration; - - instance->decoder.parser_step = HoltekDecoderStepCheckDuration; - } - } else { - instance->decoder.parser_step = HoltekDecoderStepReset; - } - break; - case HoltekDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_short) < - subghz_protocol_holtek_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_long) < - subghz_protocol_holtek_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = HoltekDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_long) < - subghz_protocol_holtek_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) < - subghz_protocol_holtek_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = HoltekDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = HoltekDecoderStepReset; - } - } else { - instance->decoder.parser_step = HoltekDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* instance) { - if((instance->data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) { - instance->serial = - subghz_protocol_blocks_reverse_key((instance->data >> 16) & 0xFFFFF, 20); - uint16_t btn = instance->data & 0xFFFF; - if((btn & 0xf) != 0xA) { - instance->btn = 0x1 << 4 | (btn & 0xF); - } else if(((btn >> 4) & 0xF) != 0xA) { - instance->btn = 0x2 << 4 | ((btn >> 4) & 0xF); - } else if(((btn >> 8) & 0xF) != 0xA) { - instance->btn = 0x3 << 4 | ((btn >> 8) & 0xF); - } else if(((btn >> 12) & 0xF) != 0xA) { - instance->btn = 0x4 << 4 | ((btn >> 12) & 0xF); - } else { - instance->btn = 0; - } - } else { - instance->serial = 0; - instance->btn = 0; - instance->cnt = 0; - } -} - -uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_holtek_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_holtek_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_holtek_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderHoltek* instance = context; - subghz_protocol_holtek_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%05lX Btn:%X ", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.serial, - instance->generic.btn >> 4); - - if((instance->generic.btn & 0xF) == 0xE) { - furi_string_cat_printf(output, "ON\r\n"); - } else if(((instance->generic.btn & 0xF) == 0xB)) { - furi_string_cat_printf(output, "OFF\r\n"); - } -} diff --git a/applications/main/subghz/protocols/holtek.h b/applications/main/subghz/protocols/holtek.h deleted file mode 100644 index 252a26dc7..000000000 --- a/applications/main/subghz/protocols/holtek.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_HOLTEK_NAME "Holtek" - -typedef struct SubGhzProtocolDecoderHoltek SubGhzProtocolDecoderHoltek; -typedef struct SubGhzProtocolEncoderHoltek SubGhzProtocolEncoderHoltek; - -extern const SubGhzProtocolDecoder subghz_protocol_holtek_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_holtek_encoder; -extern const SubGhzProtocol subghz_protocol_holtek; - -/** - * Allocate SubGhzProtocolEncoderHoltek. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderHoltek* pointer to a SubGhzProtocolEncoderHoltek instance - */ -void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderHoltek. - * @param context Pointer to a SubGhzProtocolEncoderHoltek instance - */ -void subghz_protocol_encoder_holtek_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderHoltek instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderHoltek instance - */ -void subghz_protocol_encoder_holtek_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderHoltek instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_holtek_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderHoltek. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderHoltek* pointer to a SubGhzProtocolDecoderHoltek instance - */ -void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderHoltek. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - */ -void subghz_protocol_decoder_holtek_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderHoltek. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - */ -void subghz_protocol_decoder_holtek_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderHoltek. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_holtek_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderHoltek. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderHoltek instance - * @param output Resulting text - */ -void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/holtek_ht12x.c b/applications/main/subghz/protocols/holtek_ht12x.c deleted file mode 100644 index 169387ded..000000000 --- a/applications/main/subghz/protocols/holtek_ht12x.c +++ /dev/null @@ -1,400 +0,0 @@ -#include "holtek_ht12x.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf - * - */ - -#define TAG "SubGhzProtocolHoltek_HT12X" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c" -#define CNT_TO_DIP(dip) \ - (dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \ - (dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \ - (dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1') - -static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = { - .te_short = 320, - .te_long = 640, - .te_delta = 200, - .min_count_bit_for_found = 12, -}; - -struct SubGhzProtocolDecoderHoltek_HT12X { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint32_t te; - uint32_t last_data; -}; - -struct SubGhzProtocolEncoderHoltek_HT12X { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint32_t te; -}; - -typedef enum { - Holtek_HT12XDecoderStepReset = 0, - Holtek_HT12XDecoderStepFoundStartBit, - Holtek_HT12XDecoderStepSaveDuration, - Holtek_HT12XDecoderStepCheckDuration, -} Holtek_HT12XDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = { - .alloc = subghz_protocol_decoder_holtek_th12x_alloc, - .free = subghz_protocol_decoder_holtek_th12x_free, - - .feed = subghz_protocol_decoder_holtek_th12x_feed, - .reset = subghz_protocol_decoder_holtek_th12x_reset, - - .get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data, - .serialize = subghz_protocol_decoder_holtek_th12x_serialize, - .deserialize = subghz_protocol_decoder_holtek_th12x_deserialize, - .get_string = subghz_protocol_decoder_holtek_th12x_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = { - .alloc = subghz_protocol_encoder_holtek_th12x_alloc, - .free = subghz_protocol_encoder_holtek_th12x_free, - - .deserialize = subghz_protocol_encoder_holtek_th12x_deserialize, - .stop = subghz_protocol_encoder_holtek_th12x_stop, - .yield = subghz_protocol_encoder_holtek_th12x_yield, -}; - -const SubGhzProtocol subghz_protocol_holtek_th12x = { - .name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_holtek_th12x_decoder, - .encoder = &subghz_protocol_holtek_th12x_encoder, -}; - -void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderHoltek_HT12X* instance = - malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X)); - - instance->base.protocol = &subghz_protocol_holtek_th12x; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_holtek_th12x_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderHoltek_HT12X* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - * @return true On success - */ -static bool - subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - - //Send header - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36); - //Send start bit - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)instance->te * 2); - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)instance->te * 2); - } - } - return true; -} - -bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderHoltek_HT12X* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_holtek_th12x_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); - - if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_holtek_th12x_stop(void* context) { - SubGhzProtocolEncoderHoltek_HT12X* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) { - SubGhzProtocolEncoderHoltek_HT12X* 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_holtek_th12x_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderHoltek_HT12X* instance = - malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X)); - instance->base.protocol = &subghz_protocol_holtek_th12x; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_holtek_th12x_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - free(instance); -} - -void subghz_protocol_decoder_holtek_th12x_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; -} - -void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - - switch(instance->decoder.parser_step) { - case Holtek_HT12XDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) < - subghz_protocol_holtek_th12x_const.te_delta * 36)) { - //Found Preambula - instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; - } - break; - case Holtek_HT12XDecoderStepFoundStartBit: - if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < - subghz_protocol_holtek_th12x_const.te_delta)) { - //Found StartBit - instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = duration; - } else { - instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; - } - break; - case Holtek_HT12XDecoderStepSaveDuration: - //save duration - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 + - subghz_protocol_holtek_th12x_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { - if((instance->last_data == instance->decoder.decode_data) && - instance->last_data) { - instance->te /= (instance->decoder.decode_count_bit * 3 + 1); - - 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->last_data = instance->decoder.decode_data; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = 0; - instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit; - break; - } else { - instance->decoder.te_last = duration; - instance->te += duration; - instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration; - } - } else { - instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; - } - break; - case Holtek_HT12XDecoderStepCheckDuration: - if(level) { - instance->te += duration; - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) < - subghz_protocol_holtek_th12x_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) < - subghz_protocol_holtek_th12x_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) < - subghz_protocol_holtek_th12x_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) < - subghz_protocol_holtek_th12x_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; - } - } else { - instance->decoder.parser_step = Holtek_HT12XDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) { - instance->btn = instance->data & 0x0F; - instance->cnt = (instance->data >> 4) & 0xFF; -} - -uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_holtek_th12x_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { - FURI_LOG_E(TAG, "Unable to add TE"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_holtek_th12x_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - ret = true; - } while(false); - return ret; -} - -static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) { - furi_string_cat_printf( - output, - "%s%s%s%s\r\n", - (((event >> 3) & 0x1) == 0x0 ? "B1 " : ""), - (((event >> 2) & 0x1) == 0x0 ? "B2 " : ""), - (((event >> 1) & 0x1) == 0x0 ? "B3 " : ""), - (((event >> 0) & 0x1) == 0x0 ? "B4 " : "")); -} - -void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderHoltek_HT12X* instance = context; - subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%03lX\r\n" - "Btn: ", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFF)); - subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output); - furi_string_cat_printf( - output, - "DIP:" DIP_PATTERN "\r\n" - "Te:%luus\r\n", - CNT_TO_DIP(instance->generic.cnt), - instance->te); -} diff --git a/applications/main/subghz/protocols/holtek_ht12x.h b/applications/main/subghz/protocols/holtek_ht12x.h deleted file mode 100644 index 7b5c31dd7..000000000 --- a/applications/main/subghz/protocols/holtek_ht12x.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X" - -typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X; -typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X; - -extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder; -extern const SubGhzProtocol subghz_protocol_holtek_th12x; - -/** - * Allocate SubGhzProtocolEncoderHoltek_HT12X. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - */ -void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderHoltek_HT12X. - * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - */ -void subghz_protocol_encoder_holtek_th12x_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - */ -void subghz_protocol_encoder_holtek_th12x_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderHoltek_HT12X. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - */ -void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderHoltek_HT12X. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - */ -void subghz_protocol_decoder_holtek_th12x_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderHoltek_HT12X. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - */ -void subghz_protocol_decoder_holtek_th12x_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderHoltek_HT12X. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_holtek_th12x_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderHoltek_HT12X. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance - * @param output Resulting text - */ -void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/honeywell_wdb.c b/applications/main/subghz/protocols/honeywell_wdb.c deleted file mode 100644 index 3b940fc67..000000000 --- a/applications/main/subghz/protocols/honeywell_wdb.c +++ /dev/null @@ -1,399 +0,0 @@ -#include "honeywell_wdb.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolHoneywellWDB" - -/* - * - * https://github.com/klohner/honeywell-wireless-doorbell - * - */ - -static const SubGhzBlockConst subghz_protocol_honeywell_wdb_const = { - .te_short = 160, - .te_long = 320, - .te_delta = 60, - .min_count_bit_for_found = 48, -}; - -struct SubGhzProtocolDecoderHoneywell_WDB { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - const char* device_type; - const char* alert; - uint8_t secret_knock; - uint8_t relay; - uint8_t lowbat; -}; - -struct SubGhzProtocolEncoderHoneywell_WDB { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - Honeywell_WDBDecoderStepReset = 0, - Honeywell_WDBDecoderStepFoundStartBit, - Honeywell_WDBDecoderStepSaveDuration, - Honeywell_WDBDecoderStepCheckDuration, -} Honeywell_WDBDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder = { - .alloc = subghz_protocol_decoder_honeywell_wdb_alloc, - .free = subghz_protocol_decoder_honeywell_wdb_free, - - .feed = subghz_protocol_decoder_honeywell_wdb_feed, - .reset = subghz_protocol_decoder_honeywell_wdb_reset, - - .get_hash_data = subghz_protocol_decoder_honeywell_wdb_get_hash_data, - .serialize = subghz_protocol_decoder_honeywell_wdb_serialize, - .deserialize = subghz_protocol_decoder_honeywell_wdb_deserialize, - .get_string = subghz_protocol_decoder_honeywell_wdb_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder = { - .alloc = subghz_protocol_encoder_honeywell_wdb_alloc, - .free = subghz_protocol_encoder_honeywell_wdb_free, - - .deserialize = subghz_protocol_encoder_honeywell_wdb_deserialize, - .stop = subghz_protocol_encoder_honeywell_wdb_stop, - .yield = subghz_protocol_encoder_honeywell_wdb_yield, -}; - -const SubGhzProtocol subghz_protocol_honeywell_wdb = { - .name = SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_honeywell_wdb_decoder, - .encoder = &subghz_protocol_honeywell_wdb_encoder, -}; - -void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderHoneywell_WDB* instance = - malloc(sizeof(SubGhzProtocolEncoderHoneywell_WDB)); - - instance->base.protocol = &subghz_protocol_honeywell_wdb; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_honeywell_wdb_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderHoneywell_WDB* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - * @return true On success - */ -static bool subghz_protocol_encoder_honeywell_wdb_get_upload( - SubGhzProtocolEncoderHoneywell_WDB* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long); - } - } - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3); - return true; -} - -bool subghz_protocol_encoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderHoneywell_WDB* 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_honeywell_wdb_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); - - if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_honeywell_wdb_stop(void* context) { - SubGhzProtocolEncoderHoneywell_WDB* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) { - SubGhzProtocolEncoderHoneywell_WDB* 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_honeywell_wdb_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderHoneywell_WDB* instance = - malloc(sizeof(SubGhzProtocolDecoderHoneywell_WDB)); - instance->base.protocol = &subghz_protocol_honeywell_wdb; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_honeywell_wdb_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - free(instance); -} - -void subghz_protocol_decoder_honeywell_wdb_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; -} - -void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - switch(instance->decoder.parser_step) { - case Honeywell_WDBDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < - subghz_protocol_honeywell_wdb_const.te_delta)) { - //Found header Honeywell_WDB - instance->decoder.decode_count_bit = 0; - instance->decoder.decode_data = 0; - instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; - } - break; - case Honeywell_WDBDecoderStepSaveDuration: - if(level) { //save interval - if(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) < - subghz_protocol_honeywell_wdb_const.te_delta) { - if((instance->decoder.decode_count_bit == - subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) && - ((instance->decoder.decode_data & 0x01) == - subghz_protocol_blocks_get_parity( - instance->decoder.decode_data >> 1, - subghz_protocol_honeywell_wdb_const.min_count_bit_for_found - 1))) { - 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 = Honeywell_WDBDecoderStepReset; - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = Honeywell_WDBDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; - } - break; - case Honeywell_WDBDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_short) < - subghz_protocol_honeywell_wdb_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_long) < - subghz_protocol_honeywell_wdb_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_long) < - subghz_protocol_honeywell_wdb_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short) < - subghz_protocol_honeywell_wdb_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration; - } else - instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; - } else { - instance->decoder.parser_step = Honeywell_WDBDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzProtocolDecoderHoneywell_WDB* instance - */ -static void subghz_protocol_honeywell_wdb_check_remote_controller( - SubGhzProtocolDecoderHoneywell_WDB* instance) { - /* - * - * Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series Wireless Chimes - * 0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555 - * 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 - * XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to recognize signal) - * XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter) - * .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested) - * .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor) - * .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on receiver) - * .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light pattern, 11 = full volume alarm) - * .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x rapidly) - * .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received transmission, only some models) - * .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't observe any effects) - * .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery alert) - * .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits) - * - */ - - instance->generic.serial = (instance->generic.data >> 28) & 0xFFFFF; - switch((instance->generic.data >> 20) & 0x3) { - case 0x02: - instance->device_type = "Doorbell"; - break; - case 0x01: - instance->device_type = "PIR-Motion"; - break; - default: - instance->device_type = "Unknown"; - break; - } - - switch((instance->generic.data >> 16) & 0x3) { - case 0x00: - instance->alert = "Normal"; - break; - case 0x01: - case 0x02: - instance->alert = "High"; - break; - case 0x03: - instance->alert = "Full"; - break; - default: - instance->alert = "Unknown"; - break; - } - - instance->secret_knock = (uint8_t)((instance->generic.data >> 4) & 0x1); - instance->relay = (uint8_t)((instance->generic.data >> 3) & 0x1); - instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1); -} - -uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_honeywell_wdb_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_honeywell_wdb_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_honeywell_wdb_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderHoneywell_WDB* instance = context; - subghz_protocol_honeywell_wdb_check_remote_controller(instance); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%05lX\r\n" - "DT:%s Al:%s\r\n" - "SK:%01X R:%01X LBat:%01X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF), - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.serial, - instance->device_type, - instance->alert, - instance->secret_knock, - instance->relay, - instance->lowbat); -} diff --git a/applications/main/subghz/protocols/honeywell_wdb.h b/applications/main/subghz/protocols/honeywell_wdb.h deleted file mode 100644 index 828631837..000000000 --- a/applications/main/subghz/protocols/honeywell_wdb.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME "Honeywell" - -typedef struct SubGhzProtocolDecoderHoneywell_WDB SubGhzProtocolDecoderHoneywell_WDB; -typedef struct SubGhzProtocolEncoderHoneywell_WDB SubGhzProtocolEncoderHoneywell_WDB; - -extern const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder; -extern const SubGhzProtocol subghz_protocol_honeywell_wdb; - -/** - * Allocate SubGhzProtocolEncoderHoneywell_WDB. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderHoneywell_WDB* pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - */ -void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderHoneywell_WDB. - * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - */ -void subghz_protocol_encoder_honeywell_wdb_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - */ -void subghz_protocol_encoder_honeywell_wdb_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderHoneywell_WDB. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderHoneywell_WDB* pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - */ -void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderHoneywell_WDB. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - */ -void subghz_protocol_decoder_honeywell_wdb_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderHoneywell_WDB. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - */ -void subghz_protocol_decoder_honeywell_wdb_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderHoneywell_WDB. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_honeywell_wdb_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderHoneywell_WDB. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_honeywell_wdb_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance - * @param output Resulting text - */ -void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/hormann.c b/applications/main/subghz/protocols/hormann.c deleted file mode 100644 index 67b8cdfca..000000000 --- a/applications/main/subghz/protocols/hormann.c +++ /dev/null @@ -1,341 +0,0 @@ -#include "hormann.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolHormannHSM" - -#define HORMANN_HSM_PATTERN 0xFF000000003 - -static const SubGhzBlockConst subghz_protocol_hormann_const = { - .te_short = 500, - .te_long = 1000, - .te_delta = 200, - .min_count_bit_for_found = 44, -}; - -struct SubGhzProtocolDecoderHormann { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderHormann { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - HormannDecoderStepReset = 0, - HormannDecoderStepFoundStartHeader, - HormannDecoderStepFoundHeader, - HormannDecoderStepFoundStartBit, - HormannDecoderStepSaveDuration, - HormannDecoderStepCheckDuration, -} HormannDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_hormann_decoder = { - .alloc = subghz_protocol_decoder_hormann_alloc, - .free = subghz_protocol_decoder_hormann_free, - - .feed = subghz_protocol_decoder_hormann_feed, - .reset = subghz_protocol_decoder_hormann_reset, - - .get_hash_data = subghz_protocol_decoder_hormann_get_hash_data, - .serialize = subghz_protocol_decoder_hormann_serialize, - .deserialize = subghz_protocol_decoder_hormann_deserialize, - .get_string = subghz_protocol_decoder_hormann_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_hormann_encoder = { - .alloc = subghz_protocol_encoder_hormann_alloc, - .free = subghz_protocol_encoder_hormann_free, - - .deserialize = subghz_protocol_encoder_hormann_deserialize, - .stop = subghz_protocol_encoder_hormann_stop, - .yield = subghz_protocol_encoder_hormann_yield, -}; - -const SubGhzProtocol subghz_protocol_hormann = { - .name = SUBGHZ_PROTOCOL_HORMANN_HSM_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_hormann_decoder, - .encoder = &subghz_protocol_hormann_encoder, -}; - -void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderHormann* instance = malloc(sizeof(SubGhzProtocolEncoderHormann)); - - instance->base.protocol = &subghz_protocol_hormann; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 2048; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_hormann_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderHormann* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderHormann instance - * @return true On success - */ -static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHormann* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1; - 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; - } - instance->encoder.repeat = 10; //original remote does 10 repeats - - for(size_t repeat = 0; repeat < 20; repeat++) { - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_long); - } - } - } - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24); - return true; -} - -bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderHormann* 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_hormann_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); - - if(!subghz_protocol_encoder_hormann_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_hormann_stop(void* context) { - SubGhzProtocolEncoderHormann* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_hormann_yield(void* context) { - SubGhzProtocolEncoderHormann* 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_hormann_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderHormann* instance = malloc(sizeof(SubGhzProtocolDecoderHormann)); - instance->base.protocol = &subghz_protocol_hormann; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_hormann_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - free(instance); -} - -static bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) { - return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN; -} - -void subghz_protocol_decoder_hormann_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - instance->decoder.parser_step = HormannDecoderStepReset; -} - -void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - - switch(instance->decoder.parser_step) { - case HormannDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) < - subghz_protocol_hormann_const.te_delta * 24)) { - instance->decoder.parser_step = HormannDecoderStepFoundStartBit; - } - break; - case HormannDecoderStepFoundStartBit: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < - subghz_protocol_hormann_const.te_delta)) { - instance->decoder.parser_step = HormannDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = HormannDecoderStepReset; - } - break; - case HormannDecoderStepSaveDuration: - if(level) { //save interval - if(duration >= (subghz_protocol_hormann_const.te_short * 5) && - subghz_protocol_decoder_hormann_check_pattern(instance)) { - instance->decoder.parser_step = HormannDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_hormann_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); - } - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = HormannDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = HormannDecoderStepReset; - } - break; - case HormannDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_short) < - subghz_protocol_hormann_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_long) < - subghz_protocol_hormann_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = HormannDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_long) < - subghz_protocol_hormann_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) < - subghz_protocol_hormann_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = HormannDecoderStepSaveDuration; - } else - instance->decoder.parser_step = HormannDecoderStepReset; - } else { - instance->decoder.parser_step = HormannDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance) { - instance->btn = (instance->data >> 4) & 0xF; -} - -uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_hormann_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_hormann_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_hormann_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderHormann* instance = context; - subghz_protocol_hormann_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s\r\n" - "%dbit\r\n" - "Key:0x%03lX%08lX\r\n" - "Btn:0x%01X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/hormann.h b/applications/main/subghz/protocols/hormann.h deleted file mode 100644 index 857a50041..000000000 --- a/applications/main/subghz/protocols/hormann.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_HORMANN_HSM_NAME "Hormann HSM" - -typedef struct SubGhzProtocolDecoderHormann SubGhzProtocolDecoderHormann; -typedef struct SubGhzProtocolEncoderHormann SubGhzProtocolEncoderHormann; - -extern const SubGhzProtocolDecoder subghz_protocol_hormann_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_hormann_encoder; -extern const SubGhzProtocol subghz_protocol_hormann; - -/** - * Allocate SubGhzProtocolEncoderHormann. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderHormann* pointer to a SubGhzProtocolEncoderHormann instance - */ -void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderHormann. - * @param context Pointer to a SubGhzProtocolEncoderHormann instance - */ -void subghz_protocol_encoder_hormann_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderHormann instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderHormann instance - */ -void subghz_protocol_encoder_hormann_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderHormann instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_hormann_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderHormann. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderHormann* pointer to a SubGhzProtocolDecoderHormann instance - */ -void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderHormann. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - */ -void subghz_protocol_decoder_hormann_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderHormann. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - */ -void subghz_protocol_decoder_hormann_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderHormann. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_hormann_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderHormann. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderHormann instance - * @param output Resulting text - */ -void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/ido.c b/applications/main/subghz/protocols/ido.c deleted file mode 100644 index dff9defc0..000000000 --- a/applications/main/subghz/protocols/ido.c +++ /dev/null @@ -1,234 +0,0 @@ -#include "ido.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocol_iDo_117/111" - -static const SubGhzBlockConst subghz_protocol_ido_const = { - .te_short = 450, - .te_long = 1450, - .te_delta = 150, - .min_count_bit_for_found = 48, -}; - -struct SubGhzProtocolDecoderIDo { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderIDo { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - IDoDecoderStepReset = 0, - IDoDecoderStepFoundPreambula, - IDoDecoderStepSaveDuration, - IDoDecoderStepCheckDuration, -} IDoDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_ido_decoder = { - .alloc = subghz_protocol_decoder_ido_alloc, - .free = subghz_protocol_decoder_ido_free, - - .feed = subghz_protocol_decoder_ido_feed, - .reset = subghz_protocol_decoder_ido_reset, - - .get_hash_data = subghz_protocol_decoder_ido_get_hash_data, - .deserialize = subghz_protocol_decoder_ido_deserialize, - .serialize = subghz_protocol_decoder_ido_serialize, - .get_string = subghz_protocol_decoder_ido_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_ido_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol subghz_protocol_ido = { - .name = SUBGHZ_PROTOCOL_IDO_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Save, - - .decoder = &subghz_protocol_ido_decoder, - .encoder = &subghz_protocol_ido_encoder, -}; - -void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderIDo* instance = malloc(sizeof(SubGhzProtocolDecoderIDo)); - instance->base.protocol = &subghz_protocol_ido; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_ido_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - free(instance); -} - -void subghz_protocol_decoder_ido_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - instance->decoder.parser_step = IDoDecoderStepReset; -} - -void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - - switch(instance->decoder.parser_step) { - case IDoDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < - subghz_protocol_ido_const.te_delta * 5)) { - instance->decoder.parser_step = IDoDecoderStepFoundPreambula; - } - break; - case IDoDecoderStepFoundPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) < - subghz_protocol_ido_const.te_delta * 5)) { - //Found Preambula - instance->decoder.parser_step = IDoDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = IDoDecoderStepReset; - } - break; - case IDoDecoderStepSaveDuration: - if(level) { - if(duration >= ((uint32_t)subghz_protocol_ido_const.te_short * 5 + - subghz_protocol_ido_const.te_delta)) { - instance->decoder.parser_step = IDoDecoderStepFoundPreambula; - if(instance->decoder.decode_count_bit >= - subghz_protocol_ido_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = IDoDecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = IDoDecoderStepReset; - } - break; - case IDoDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < - subghz_protocol_ido_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_ido_const.te_long) < - subghz_protocol_ido_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = IDoDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) < - subghz_protocol_ido_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short) < - subghz_protocol_ido_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = IDoDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = IDoDecoderStepReset; - } - } else { - instance->decoder.parser_step = IDoDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* instance) { - uint64_t code_found_reverse = - subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); - uint32_t code_fix = code_found_reverse & 0xFFFFFF; - - instance->serial = code_fix & 0xFFFFF; - instance->btn = (code_fix >> 20) & 0x0F; -} - -uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_ido_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != subghz_protocol_ido_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_ido_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderIDo* instance = context; - - subghz_protocol_ido_check_remote_controller(&instance->generic); - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - uint32_t code_fix = code_found_reverse & 0xFFFFFF; - uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Fix:%06lX \r\n" - "Hop:%06lX \r\n" - "Sn:%05lX Btn:%X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - code_fix, - code_hop, - instance->generic.serial, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/ido.h b/applications/main/subghz/protocols/ido.h deleted file mode 100644 index 634f6ff89..000000000 --- a/applications/main/subghz/protocols/ido.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_IDO_NAME "iDo 117/111" - -typedef struct SubGhzProtocolDecoderIDo SubGhzProtocolDecoderIDo; -typedef struct SubGhzProtocolEncoderIDo SubGhzProtocolEncoderIDo; - -extern const SubGhzProtocolDecoder subghz_protocol_ido_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_ido_encoder; -extern const SubGhzProtocol subghz_protocol_ido; - -/** - * Allocate SubGhzProtocolDecoderIDo. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderIDo* pointer to a SubGhzProtocolDecoderIDo instance - */ -void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderIDo. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - */ -void subghz_protocol_decoder_ido_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderIDo. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - */ -void subghz_protocol_decoder_ido_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderIDo. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_ido_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderIDo. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderIDo instance - * @param output Resulting text - */ -void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/intertechno_v3.c b/applications/main/subghz/protocols/intertechno_v3.c deleted file mode 100644 index 2c4e514cc..000000000 --- a/applications/main/subghz/protocols/intertechno_v3.c +++ /dev/null @@ -1,472 +0,0 @@ -#include "intertechno_v3.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolIntertechnoV3" - -#define CH_PATTERN "%c%c%c%c" -#define CNT_TO_CH(ch) \ - (ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0') - -#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36 - -static const SubGhzBlockConst subghz_protocol_intertechno_v3_const = { - .te_short = 275, - .te_long = 1375, - .te_delta = 150, - .min_count_bit_for_found = 32, -}; - -struct SubGhzProtocolDecoderIntertechno_V3 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderIntertechno_V3 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - IntertechnoV3DecoderStepReset = 0, - IntertechnoV3DecoderStepStartSync, - IntertechnoV3DecoderStepFoundSync, - IntertechnoV3DecoderStepStartDuration, - IntertechnoV3DecoderStepSaveDuration, - IntertechnoV3DecoderStepCheckDuration, - IntertechnoV3DecoderStepEndDuration, -} IntertechnoV3DecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = { - .alloc = subghz_protocol_decoder_intertechno_v3_alloc, - .free = subghz_protocol_decoder_intertechno_v3_free, - - .feed = subghz_protocol_decoder_intertechno_v3_feed, - .reset = subghz_protocol_decoder_intertechno_v3_reset, - - .get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data, - .serialize = subghz_protocol_decoder_intertechno_v3_serialize, - .deserialize = subghz_protocol_decoder_intertechno_v3_deserialize, - .get_string = subghz_protocol_decoder_intertechno_v3_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = { - .alloc = subghz_protocol_encoder_intertechno_v3_alloc, - .free = subghz_protocol_encoder_intertechno_v3_free, - - .deserialize = subghz_protocol_encoder_intertechno_v3_deserialize, - .stop = subghz_protocol_encoder_intertechno_v3_stop, - .yield = subghz_protocol_encoder_intertechno_v3_yield, -}; - -const SubGhzProtocol subghz_protocol_intertechno_v3 = { - .name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_intertechno_v3_decoder, - .encoder = &subghz_protocol_intertechno_v3_encoder, -}; - -void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderIntertechno_V3* instance = - malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3)); - - instance->base.protocol = &subghz_protocol_intertechno_v3; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_intertechno_v3_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderIntertechno_V3* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - * @return true On success - */ -static bool subghz_protocol_encoder_intertechno_v3_get_upload( - SubGhzProtocolEncoderIntertechno_V3* instance) { - furi_assert(instance); - size_t index = 0; - - //Send header - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38); - //Send sync - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) { - //send bit dimm - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - } else if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long); - } - } - instance->encoder.size_upload = index; - return true; -} - -bool subghz_protocol_encoder_intertechno_v3_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderIntertechno_V3* 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_intertechno_v3_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { - 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); - - if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_intertechno_v3_stop(void* context) { - SubGhzProtocolEncoderIntertechno_V3* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) { - SubGhzProtocolEncoderIntertechno_V3* 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_intertechno_v3_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderIntertechno_V3* instance = - malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3)); - instance->base.protocol = &subghz_protocol_intertechno_v3; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_intertechno_v3_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - free(instance); -} - -void subghz_protocol_decoder_intertechno_v3_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; -} - -void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - switch(instance->decoder.parser_step) { - case IntertechnoV3DecoderStepReset: - if((!level) && - (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) < - subghz_protocol_intertechno_v3_const.te_delta * 15)) { - instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; - } - break; - case IntertechnoV3DecoderStepStartSync: - if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta)) { - instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - - case IntertechnoV3DecoderStepFoundSync: - if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) < - subghz_protocol_intertechno_v3_const.te_delta * 3)) { - instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - - case IntertechnoV3DecoderStepStartDuration: - if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta)) { - instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - - case IntertechnoV3DecoderStepSaveDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) { - instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync; - if((instance->decoder.decode_count_bit == - subghz_protocol_intertechno_v3_const.min_count_bit_for_found) || - (instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) { - 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); - } - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - case IntertechnoV3DecoderStepCheckDuration: - if(level) { - //Add 0 bit - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; - } else if( - //Add 1 bit - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) < - subghz_protocol_intertechno_v3_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; - - } else if( - //Add dimm_state - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta) && - (instance->decoder.decode_count_bit == 27)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration; - - } else - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - - case IntertechnoV3DecoderStepEndDuration: - if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) < - subghz_protocol_intertechno_v3_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) < - subghz_protocol_intertechno_v3_const.te_delta * 2))) { - instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration; - } else { - instance->decoder.parser_step = IntertechnoV3DecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) { - /* - * A frame is either 32 or 36 bits: - * - * _ - * start bit: | |__________ (T,10T) - * _ _ - * '0': | |_| |_____ (T,T,T,5T) - * _ _ - * '1': | |_____| |_ (T,5T,T,T) - * _ _ - * dimm: | |_| |_ (T,T,T,T) - * - * _ - * stop bit: | |____...____ (T,38T) - * - * if frame 32 bits - * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch on/off ~ch - * Key:0x3F86C59F => 00111111100001101100010110 0 1 1111 - * - * if frame 36 bits - * SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch dimm ~ch dimm_level - * Key:0x42D2E8856 => 01000010110100101110100010 0 X 0101 0110 - * - */ - - if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { - instance->serial = (instance->data >> 6) & 0x3FFFFFF; - if((instance->data >> 5) & 0x1) { - instance->cnt = 1 << 5; - } else { - instance->cnt = (~instance->data & 0xF); - } - instance->btn = (instance->data >> 4) & 0x1; - } else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { - instance->serial = (instance->data >> 10) & 0x3FFFFFF; - if((instance->data >> 9) & 0x1) { - instance->cnt = 1 << 5; - } else { - instance->cnt = (~(instance->data >> 4) & 0xF); - } - instance->btn = (instance->data) & 0xF; - } else { - instance->serial = 0; - instance->cnt = 0; - instance->btn = 0; - } -} - -uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_intertechno_v3_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_intertechno_v3_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if((instance->generic.data_count_bit != - subghz_protocol_intertechno_v3_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderIntertechno_V3* instance = context; - - subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%.11s %db\r\n" - "Key:0x%08llX\r\n" - "Sn:%07lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - instance->generic.data, - instance->generic.serial); - - if(instance->generic.data_count_bit == - subghz_protocol_intertechno_v3_const.min_count_bit_for_found) { - if(instance->generic.cnt >> 5) { - furi_string_cat_printf( - output, "Ch: All Btn:%s\r\n", (instance->generic.btn ? "On" : "Off")); - } else { - furi_string_cat_printf( - output, - "Ch:" CH_PATTERN " Btn:%s\r\n", - CNT_TO_CH(instance->generic.cnt), - (instance->generic.btn ? "On" : "Off")); - } - } else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) { - furi_string_cat_printf( - output, - "Ch:" CH_PATTERN " Dimm:%d%%\r\n", - CNT_TO_CH(instance->generic.cnt), - (int)(6.67 * (float)instance->generic.btn)); - } -} diff --git a/applications/main/subghz/protocols/intertechno_v3.h b/applications/main/subghz/protocols/intertechno_v3.h deleted file mode 100644 index ffee14b04..000000000 --- a/applications/main/subghz/protocols/intertechno_v3.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME "Intertechno_V3" - -typedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3; -typedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3; - -extern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder; -extern const SubGhzProtocol subghz_protocol_intertechno_v3; - -/** - * Allocate SubGhzProtocolEncoderIntertechno_V3. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - */ -void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderIntertechno_V3. - * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - */ -void subghz_protocol_encoder_intertechno_v3_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_intertechno_v3_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - */ -void subghz_protocol_encoder_intertechno_v3_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderIntertechno_V3. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - */ -void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderIntertechno_V3. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - */ -void subghz_protocol_decoder_intertechno_v3_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderIntertechno_V3. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - */ -void subghz_protocol_decoder_intertechno_v3_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderIntertechno_V3. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_intertechno_v3_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderIntertechno_V3. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_intertechno_v3_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq.c b/applications/main/subghz/protocols/keeloq.c deleted file mode 100644 index a0970de4d..000000000 --- a/applications/main/subghz/protocols/keeloq.c +++ /dev/null @@ -1,1115 +0,0 @@ -#include "keeloq.h" -#include "keeloq_common.h" - -#include "../subghz_keystore.h" -#include - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolKeeloq" - -static const SubGhzBlockConst subghz_protocol_keeloq_const = { - .te_short = 400, - .te_long = 800, - .te_delta = 140, - .min_count_bit_for_found = 64, -}; - -struct SubGhzProtocolDecoderKeeloq { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - SubGhzKeystore* keystore; - const char* manufacture_name; - - FuriString* manufacture_from_file; -}; - -struct SubGhzProtocolEncoderKeeloq { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - SubGhzKeystore* keystore; - const char* manufacture_name; - - FuriString* manufacture_from_file; -}; - -typedef enum { - KeeloqDecoderStepReset = 0, - KeeloqDecoderStepCheckPreambula, - KeeloqDecoderStepSaveDuration, - KeeloqDecoderStepCheckDuration, -} KeeloqDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder = { - .alloc = subghz_protocol_decoder_keeloq_alloc, - .free = subghz_protocol_decoder_keeloq_free, - - .feed = subghz_protocol_decoder_keeloq_feed, - .reset = subghz_protocol_decoder_keeloq_reset, - - .get_hash_data = subghz_protocol_decoder_keeloq_get_hash_data, - .serialize = subghz_protocol_decoder_keeloq_serialize, - .deserialize = subghz_protocol_decoder_keeloq_deserialize, - .get_string = subghz_protocol_decoder_keeloq_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder = { - .alloc = subghz_protocol_encoder_keeloq_alloc, - .free = subghz_protocol_encoder_keeloq_free, - - .deserialize = subghz_protocol_encoder_keeloq_deserialize, - .stop = subghz_protocol_encoder_keeloq_stop, - .yield = subghz_protocol_encoder_keeloq_yield, -}; - -const SubGhzProtocol subghz_protocol_keeloq = { - .name = SUBGHZ_PROTOCOL_KEELOQ_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_keeloq_decoder, - .encoder = &subghz_protocol_keeloq_encoder, -}; - -static const char* mfname; -static int kl_type; - -void keeloq_reset_mfname() { - mfname = ""; -} - -void keeloq_reset_kl_type() { - kl_type = 0; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - */ -static void subghz_protocol_keeloq_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name); - -void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolEncoderKeeloq* instance = malloc(sizeof(SubGhzProtocolEncoderKeeloq)); - - instance->base.protocol = &subghz_protocol_keeloq; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - - instance->encoder.repeat = 100; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - - instance->manufacture_from_file = furi_string_alloc(); - - return instance; -} - -void subghz_protocol_encoder_keeloq_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderKeeloq* instance = context; - furi_string_free(instance->manufacture_from_file); - free(instance->encoder.upload); - free(instance); -} - -/** - * Key generation from simple data - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance - * @param btn Button number, 4 bit - */ -static bool subghz_protocol_keeloq_gen_data(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - uint32_t fix = (uint32_t)btn << 28 | instance->generic.serial; - uint32_t decrypt = (uint32_t)btn << 28 | - (instance->generic.serial & 0x3FF) - << 16 | //ToDo in some protocols the discriminator is 0 - instance->generic.cnt; - uint32_t hop = 0; - uint64_t man = 0; - uint64_t code_found_reverse; - int res = 0; - if(instance->manufacture_name == 0x0) { - instance->manufacture_name = ""; - } - - // DTM Neo uses 12bit -> simple learning -- FAAC_RC,XT , Mutanco_Mutancode -> 12bit normal learning - if((strcmp(instance->manufacture_name, "DTM_Neo") == 0) || - (strcmp(instance->manufacture_name, "FAAC_RC,XT") == 0) || - (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; - } - - // Nice Smilo, MHouse, JCM, Normstahl -> 8bit serial - simple learning - if((strcmp(instance->manufacture_name, "NICE_Smilo") == 0) || - (strcmp(instance->manufacture_name, "NICE_MHOUSE") == 0) || - (strcmp(instance->manufacture_name, "JCM_Tech") == 0) || - (strcmp(instance->manufacture_name, "Normstahl") == 0)) { - decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; - } - - if(strcmp(instance->manufacture_name, "Unknown") == 0) { - code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - hop = code_found_reverse & 0x00000000ffffffff; - } else if(strcmp(instance->manufacture_name, "AN-Motors") == 0) { - hop = (instance->generic.cnt & 0xFF) << 24 | (instance->generic.cnt & 0xFF) << 16 | - (instance->generic.btn & 0xF) << 12 | 0x404; - } else if(strcmp(instance->manufacture_name, "HCS101") == 0) { - hop = instance->generic.cnt << 16 | (instance->generic.btn & 0xF) << 12 | 0x000; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - //Simple Learning - hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - break; - case KEELOQ_LEARNING_NORMAL: - //Simple Learning - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_SECURE: - //Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->generic.seed, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - //Magic XOR type-1 Learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - instance->generic.serial, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - //Magic Serial Type 1 learning - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_UNKNOWN: - if(kl_type == 1) { - hop = - subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - } - if(kl_type == 2) { - man = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - if(kl_type == 3) { - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->generic.seed, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - if(kl_type == 4) { - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - instance->generic.serial, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - break; - } - break; - } - } - } - if(hop) { - uint64_t yek = (uint64_t)fix << 32 | hop; - instance->generic.data = - subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); - } - return true; -} - -bool subghz_protocol_keeloq_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - const char* manufacture_name, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderKeeloq* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->manufacture_name = manufacture_name; - instance->generic.data_count_bit = 64; - bool res = subghz_protocol_keeloq_gen_data(instance, btn); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -bool subghz_protocol_keeloq_bft_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - uint32_t seed, - const char* manufacture_name, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderKeeloq* instance = context; - instance->generic.serial = serial; - instance->generic.btn = btn; - instance->generic.cnt = cnt; - instance->generic.seed = seed; - instance->manufacture_name = manufacture_name; - instance->generic.data_count_bit = 64; - // roguuemaster don't steal.!!!! - bool res = subghz_protocol_keeloq_gen_data(instance, btn); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return true On success - */ -static bool - subghz_protocol_encoder_keeloq_get_upload(SubGhzProtocolEncoderKeeloq* instance, uint8_t btn) { - furi_assert(instance); - - //gen new key - if(subghz_protocol_keeloq_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display - } else { - return false; - } - - size_t index = 0; - size_t size_upload = 11 * 2 + 2 + (instance->generic.data_count_bit * 2) + 4; - 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; - } - - //Send header - for(uint8_t i = 11; i > 0; i--) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); - } - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 10); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short); - } - } - // +send 2 status bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_long); - // send end - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_keeloq_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_keeloq_const.te_short * 40); - - return true; -} - -bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderKeeloq* 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_keeloq_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_D(TAG, "ENCODER: Missing Seed"); - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); - } - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - subghz_protocol_keeloq_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - if(!subghz_protocol_encoder_keeloq_get_upload(instance, instance->generic.btn)) break; - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_keeloq_stop(void* context) { - SubGhzProtocolEncoderKeeloq* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_keeloq_yield(void* context) { - SubGhzProtocolEncoderKeeloq* 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_keeloq_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolDecoderKeeloq* instance = malloc(sizeof(SubGhzProtocolDecoderKeeloq)); - instance->base.protocol = &subghz_protocol_keeloq; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - instance->manufacture_from_file = furi_string_alloc(); - - return instance; -} - -void subghz_protocol_decoder_keeloq_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - furi_string_free(instance->manufacture_from_file); - - free(instance); -} - -void subghz_protocol_decoder_keeloq_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - instance->decoder.parser_step = KeeloqDecoderStepReset; - mfname = ""; - kl_type = 0; -} - -void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - - switch(instance->decoder.parser_step) { - case KeeloqDecoderStepReset: - if((level) && DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < - subghz_protocol_keeloq_const.te_delta) { - instance->decoder.parser_step = KeeloqDecoderStepCheckPreambula; - instance->header_count++; - } - break; - case KeeloqDecoderStepCheckPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < - subghz_protocol_keeloq_const.te_delta)) { - instance->decoder.parser_step = KeeloqDecoderStepReset; - break; - } - if((instance->header_count > 2) && - (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short * 10) < - subghz_protocol_keeloq_const.te_delta * 10)) { - // Found header - instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = KeeloqDecoderStepReset; - instance->header_count = 0; - } - break; - case KeeloqDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = KeeloqDecoderStepCheckDuration; - } - break; - case KeeloqDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_keeloq_const.te_short * 2 + - subghz_protocol_keeloq_const.te_delta)) { - // Found end TX - instance->decoder.parser_step = KeeloqDecoderStepReset; - if((instance->decoder.decode_count_bit >= - subghz_protocol_keeloq_const.min_count_bit_for_found) && - (instance->decoder.decode_count_bit <= - subghz_protocol_keeloq_const.min_count_bit_for_found + 2)) { - if(instance->generic.data != instance->decoder.decode_data) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = - subghz_protocol_keeloq_const.min_count_bit_for_found; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - } - break; - } else if( - (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 * 2)) { - if(instance->decoder.decode_count_bit < - subghz_protocol_keeloq_const.min_count_bit_for_found) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else { - instance->decoder.decode_count_bit++; - } - 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 * 2) && - (DURATION_DIFF(duration, subghz_protocol_keeloq_const.te_short) < - subghz_protocol_keeloq_const.te_delta)) { - if(instance->decoder.decode_count_bit < - subghz_protocol_keeloq_const.min_count_bit_for_found) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else { - instance->decoder.decode_count_bit++; - } - instance->decoder.parser_step = KeeloqDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = KeeloqDecoderStepReset; - instance->header_count = 0; - } - } else { - instance->decoder.parser_step = KeeloqDecoderStepReset; - instance->header_count = 0; - } - break; - } -} - -/** - * Validation of decrypt data. - * @param instance Pointer to a SubGhzBlockGeneric instance - * @param decrypt Decrypd data - * @param btn Button number, 4 bit - * @param end_serial decrement the last 10 bits of the serial number - * @return true On success - */ -static inline bool subghz_protocol_keeloq_check_decrypt( - SubGhzBlockGeneric* instance, - uint32_t decrypt, - uint8_t btn, - uint32_t end_serial) { - furi_assert(instance); - if((decrypt >> 28 == btn) && (((((uint16_t)(decrypt >> 16)) & 0xFF) == end_serial) || - ((((uint16_t)(decrypt >> 16)) & 0xFF) == 0))) { - instance->cnt = decrypt & 0x0000FFFF; - return true; - } - return false; -} - -/** - * Checking the accepted code against the database manafacture key - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param fix Fix part of the parcel - * @param hop Hop encrypted part of the parcel - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - * @return true on successful search - */ -static uint8_t subghz_protocol_keeloq_check_remote_controller_selector( - SubGhzBlockGeneric* instance, - uint32_t fix, - uint32_t hop, - SubGhzKeystore* keystore, - const char** manufacture_name) { - // protocol HCS300 uses 10 bits in discriminator, HCS200 uses 8 bits, for backward compatibility, we are looking for the 8-bit pattern - // HCS300 -> uint16_t end_serial = (uint16_t)(fix & 0x3FF); - // HCS200 -> uint16_t end_serial = (uint16_t)(fix & 0xFF); - - uint16_t end_serial = (uint16_t)(fix & 0xFF); - uint8_t btn = (uint8_t)(fix >> 28); - uint32_t decrypt = 0; - uint64_t man; - int res = 0; - if(mfname == 0x0) { - mfname = ""; - } - - if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_SECURE: - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2: - man = subghz_protocol_keeloq_common_magic_serial_type2_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3: - man = subghz_protocol_keeloq_common_magic_serial_type3_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_secure_learning(fix, instance->seed, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Magic xor type1 learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - break; - } - } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_SECURE: - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_XOR_TYPE_1: - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1: - man = subghz_protocol_keeloq_common_magic_serial_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Secure Learning - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_secure_learning( - fix, instance->seed, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 3; - return 1; - } - - // Magic xor type1 learning - man = subghz_protocol_keeloq_common_magic_xor_type1_learning( - fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - // Check for mirrored man - man = subghz_protocol_keeloq_common_magic_xor_type1_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man); - if(subghz_protocol_keeloq_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 4; - return 1; - } - - break; - } - } - } - } - - *manufacture_name = "Unknown"; - mfname = "Unknown"; - instance->cnt = 0; - - return 0; -} - -static void subghz_protocol_keeloq_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name) { - uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); - uint32_t key_fix = key >> 32; - uint32_t key_hop = key & 0x00000000ffffffff; - // Check key AN-Motors - if((key_hop >> 24) == ((key_hop >> 16) & 0x00ff) && - (key_fix >> 28) == ((key_hop >> 12) & 0x0f) && (key_hop & 0xFFF) == 0x404) { - *manufacture_name = "AN-Motors"; - mfname = *manufacture_name; - instance->cnt = key_hop >> 16; - } else if((key_hop & 0xFFF) == (0x000) && (key_fix >> 28) == ((key_hop >> 12) & 0x0f)) { - *manufacture_name = "HCS101"; - mfname = *manufacture_name; - instance->cnt = key_hop >> 16; - } else { - subghz_protocol_keeloq_check_remote_controller_selector( - instance, key_fix, key_hop, keystore, manufacture_name); - } - - instance->serial = key_fix & 0x0FFFFFFF; - instance->btn = key_fix >> 28; -} - -uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_keeloq_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - subghz_protocol_keeloq_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - if(strcmp(instance->manufacture_name, "BFT") == 0) { - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_E(TAG, "DECODER Serialize: Unable to add Seed"); - res = false; - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - } - - if(res && !flipper_format_write_string_cstr( - flipper_format, "Manufacture", instance->manufacture_name)) { - FURI_LOG_E(TAG, "DECODER Serialize: Unable to add manufacture name"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* 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_keeloq_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - - uint8_t seed_data[sizeof(uint32_t)] = {0}; - for(size_t i = 0; i < sizeof(uint32_t); i++) { - seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF; - } - if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) { - FURI_LOG_D(TAG, "DECODER: Missing Seed"); - } - instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 | - seed_data[3]; - - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); - } - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderKeeloq* instance = context; - - subghz_protocol_keeloq_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - uint32_t code_found_reverse_hi = code_found_reverse >> 32; - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - if(strcmp(instance->manufacture_name, "BFT") == 0) { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04lX\r\n" - "Hop:0x%08lX Btn:%01X\r\n" - "MF:%s Sd:%08lX", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - instance->generic.cnt, - code_found_reverse_lo, - instance->generic.btn, - instance->manufacture_name, - instance->generic.seed); - } else { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04lX\r\n" - "Hop:0x%08lX Btn:%01X\r\n" - "MF:%s", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - instance->generic.cnt, - code_found_reverse_lo, - instance->generic.btn, - instance->manufacture_name); - } -} diff --git a/applications/main/subghz/protocols/keeloq.h b/applications/main/subghz/protocols/keeloq.h deleted file mode 100644 index 7b0cfc3bd..000000000 --- a/applications/main/subghz/protocols/keeloq.h +++ /dev/null @@ -1,153 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_KEELOQ_NAME "KeeLoq" - -typedef struct SubGhzProtocolDecoderKeeloq SubGhzProtocolDecoderKeeloq; -typedef struct SubGhzProtocolEncoderKeeloq SubGhzProtocolEncoderKeeloq; - -extern const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder; -extern const SubGhzProtocol subghz_protocol_keeloq; - -void keeloq_reset_mfname(); - -void keeloq_reset_kl_type(); - -/** - * Allocate SubGhzProtocolEncoderKeeloq. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance - */ -void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderKeeloq. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - */ -void subghz_protocol_encoder_keeloq_free(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 28 bit - * @param btn Button number, 4 bit - * @param cnt Counter value, 16 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_keeloq_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - const char* manufacture_name, - SubGhzRadioPreset* preset); - -/** - * Key generation for BFT. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 28 bit - * @param btn Button number, 4 bit - * @param cnt Counter value, 16 bit - * @param seed Seed value, 32 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_keeloq_bft_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - uint32_t seed, - const char* manufacture_name, - SubGhzRadioPreset* preset); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - */ -void subghz_protocol_encoder_keeloq_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_keeloq_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderKeeloq. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderKeeloq* pointer to a SubGhzProtocolDecoderKeeloq instance - */ -void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderKeeloq. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - */ -void subghz_protocol_decoder_keeloq_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderKeeloq. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - */ -void subghz_protocol_decoder_keeloq_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderKeeloq. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_keeloq_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderKeeloq. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderKeeloq instance - * @param output Resulting text - */ -void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/keeloq_common.c b/applications/main/subghz/protocols/keeloq_common.c deleted file mode 100644 index 1d2d04457..000000000 --- a/applications/main/subghz/protocols/keeloq_common.c +++ /dev/null @@ -1,142 +0,0 @@ -#include "keeloq_common.h" - -#include - -#include - -#define bit(x, n) (((x) >> (n)) & 1) -#define g5(x, a, b, c, d, e) \ - (bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16) - -/** Simple Learning Encrypt - * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter - * @param key - manufacture (64bit) - * @return keeloq encrypt data - */ -inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) { - uint32_t x = data, r; - for(r = 0; r < 528; r++) - x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^ - bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31))) - << 31); - return x; -} - -/** Simple Learning Decrypt - * @param data - keelog encrypt data - * @param key - manufacture (64bit) - * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter - */ -inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) { - uint32_t x = data, r; - for(r = 0; r < 528; r++) - x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^ - bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30)); - return x; -} - -/** Normal Learning - * @param data - serial number (28bit) - * @param key - manufacture (64bit) - * @return manufacture for this serial number (64bit) - */ -inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) { - uint32_t k1, k2; - - data &= 0x0FFFFFFF; - data |= 0x20000000; - k1 = subghz_protocol_keeloq_common_decrypt(data, key); - - data &= 0x0FFFFFFF; - data |= 0x60000000; - k2 = subghz_protocol_keeloq_common_decrypt(data, key); - - return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya -} - -/** Secure Learning - * @param data - serial number (28bit) - * @param seed - seed number (32bit) - * @param key - manufacture (64bit) - * @return manufacture for this serial number (64bit) - */ - -inline uint64_t subghz_protocol_keeloq_common_secure_learning( - uint32_t data, - uint32_t seed, - const uint64_t key) { - uint32_t k1, k2; - - data &= 0x0FFFFFFF; - k1 = subghz_protocol_keeloq_common_decrypt(data, key); - k2 = subghz_protocol_keeloq_common_decrypt(seed, key); - - return ((uint64_t)k1 << 32) | k2; -} - -/** Magic_xor_type1 Learning - * @param data - serial number (28bit) - * @param xor - magic xor (64bit) - * @return manufacture for this serial number (64bit) - */ - -inline uint64_t - subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) { - data &= 0x0FFFFFFF; - return (((uint64_t)data << 32) | data) ^ xor; -} - -/** Faac SLH (Spa) Learning - * @param seed - seed number (32bit) - * @param key - mfkey (64bit) - * @return man_learning for this seed number (64bit) - */ - -inline uint64_t - subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key) { - uint16_t hs = seed >> 16; - const uint16_t ending = 0x544D; - uint32_t lsb = (uint32_t)hs << 16 | ending; - uint64_t man = (uint64_t)subghz_protocol_keeloq_common_encrypt(seed, key) << 32 | - subghz_protocol_keeloq_common_encrypt(lsb, key); - return man; -} -/** Magic_serial_type1 Learning - * @param data - serial number (28bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -inline uint64_t - subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) { - return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) | - ((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32); -} - -/** Magic_serial_type2 Learning - * @param data - btn+serial number (32bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -inline uint64_t - subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) { - uint8_t* p = (uint8_t*)&data; - uint8_t* m = (uint8_t*)&man; - m[7] = p[0]; - m[6] = p[1]; - m[5] = p[2]; - m[4] = p[3]; - return man; -} - -/** Magic_serial_type3 Learning - * @param data - serial number (24bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -inline uint64_t - subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) { - return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF); -} diff --git a/applications/main/subghz/protocols/keeloq_common.h b/applications/main/subghz/protocols/keeloq_common.h deleted file mode 100644 index a6c0d346e..000000000 --- a/applications/main/subghz/protocols/keeloq_common.h +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once - -#include "base.h" - -#include - -/* - * Keeloq - * https://ru.wikipedia.org/wiki/KeeLoq - * https://phreakerclub.com/forum/showthread.php?t=1094 - * - */ -#define KEELOQ_NLF 0x3A5C742E - -/* - * KeeLoq learning types - * https://phreakerclub.com/forum/showthread.php?t=67 - */ -#define KEELOQ_LEARNING_UNKNOWN 0u -#define KEELOQ_LEARNING_SIMPLE 1u -#define KEELOQ_LEARNING_NORMAL 2u -#define KEELOQ_LEARNING_SECURE 3u -#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u -#define KEELOQ_LEARNING_FAAC 5u -#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u -#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u -#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u - -/** - * Simple Learning Encrypt - * @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter - * @param key - manufacture (64bit) - * @return keeloq encrypt data - */ -uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key); - -/** - * Simple Learning Decrypt - * @param data - keeloq encrypt data - * @param key - manufacture (64bit) - * @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter - */ -uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key); - -/** - * Normal Learning - * @param data - serial number (28bit) - * @param key - manufacture (64bit) - * @return manufacture for this serial number (64bit) - */ -uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key); - -/** - * Secure Learning - * @param data - serial number (28bit) - * @param seed - seed number (32bit) - * @param key - manufacture (64bit) - * @return manufacture for this serial number (64bit) - */ -uint64_t - subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key); - -/** - * Magic_xor_type1 Learning - * @param data - serial number (28bit) - * @param xor - magic xor (64bit) - * @return manufacture for this serial number (64bit) - */ -uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor); - -/** Faac SLH (Spa) Learning - * @param seed - seed number (32bit) - * @param key - mfkey (64bit) - * @return man_learning for this fix number (64bit) - */ -uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key); - -/** Magic_serial_type1 Learning - * @param data - serial number (28bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man); - -/** Magic_serial_type2 Learning - * @param data - btn+serial number (32bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man); - -/** Magic_serial_type3 Learning - * @param data - btn+serial number (32bit) - * @param man - magic man (64bit) - * @return manufacture for this serial number (64bit) - */ - -uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man); diff --git a/applications/main/subghz/protocols/kia.c b/applications/main/subghz/protocols/kia.c deleted file mode 100644 index a5d9e37ef..000000000 --- a/applications/main/subghz/protocols/kia.c +++ /dev/null @@ -1,279 +0,0 @@ -#include "kia.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocoKIA" - -static const SubGhzBlockConst subghz_protocol_kia_const = { - .te_short = 250, - .te_long = 500, - .te_delta = 100, - .min_count_bit_for_found = 61, -}; - -struct SubGhzProtocolDecoderKIA { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderKIA { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - KIADecoderStepReset = 0, - KIADecoderStepCheckPreambula, - KIADecoderStepSaveDuration, - KIADecoderStepCheckDuration, -} KIADecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_kia_decoder = { - .alloc = subghz_protocol_decoder_kia_alloc, - .free = subghz_protocol_decoder_kia_free, - - .feed = subghz_protocol_decoder_kia_feed, - .reset = subghz_protocol_decoder_kia_reset, - - .get_hash_data = subghz_protocol_decoder_kia_get_hash_data, - .serialize = subghz_protocol_decoder_kia_serialize, - .deserialize = subghz_protocol_decoder_kia_deserialize, - .get_string = subghz_protocol_decoder_kia_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol subghz_protocol_kia = { - .name = SUBGHZ_PROTOCOL_KIA_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable, - - .decoder = &subghz_protocol_kia_decoder, - .encoder = &subghz_protocol_kia_encoder, -}; - -void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA)); - instance->base.protocol = &subghz_protocol_kia; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_kia_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - free(instance); -} - -void subghz_protocol_decoder_kia_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - instance->decoder.parser_step = KIADecoderStepReset; -} - -void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - - switch(instance->decoder.parser_step) { - case KIADecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta)) { - instance->decoder.parser_step = KIADecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - case KIADecoderStepCheckPreambula: - if(level) { - if((DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < - subghz_protocol_kia_const.te_delta)) { - instance->decoder.te_last = duration; - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - } else if( - (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta)) { - // Found header - instance->header_count++; - break; - } else if( - (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < - subghz_protocol_kia_const.te_delta) && - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < - subghz_protocol_kia_const.te_delta)) { - // Found start bit - if(instance->header_count > 15) { - instance->decoder.parser_step = KIADecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - break; - case KIADecoderStepSaveDuration: - if(level) { - if(duration >= - (subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) { - //Found stop bit - instance->decoder.parser_step = KIADecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_kia_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = KIADecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - break; - case KIADecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) < - subghz_protocol_kia_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = KIADecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) < - subghz_protocol_kia_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) < - subghz_protocol_kia_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = KIADecoderStepSaveDuration; - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - } else { - instance->decoder.parser_step = KIADecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_kia_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x08; - size_t i, j; - for(i = 0; i < len; i++) { - crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) - crc = (uint8_t)((crc << 1) ^ 0x7F); - else - crc <<= 1; - } - } - return crc; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance) { - /* - * 0x0F 0112 43B04EC 1 7D - * 0x0F 0113 43B04EC 1 DF - * 0x0F 0114 43B04EC 1 30 - * 0x0F 0115 43B04EC 2 13 - * 0x0F 0116 43B04EC 3 F5 - * CNT Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x08) - */ - - instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF); - instance->btn = (instance->data >> 8) & 0x0F; - instance->cnt = (instance->data >> 40) & 0xFFFF; -} - -uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_kia_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != subghz_protocol_kia_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_kia_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderKIA* instance = context; - - subghz_protocol_kia_check_remote_controller(&instance->generic); - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Sn:%07lX Btn:%X Cnt:%04lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); -} diff --git a/applications/main/subghz/protocols/kia.h b/applications/main/subghz/protocols/kia.h deleted file mode 100644 index a9afcf149..000000000 --- a/applications/main/subghz/protocols/kia.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_KIA_NAME "KIA Seed" - -typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA; -typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA; - -extern const SubGhzProtocolDecoder subghz_protocol_kia_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_kia_encoder; -extern const SubGhzProtocol subghz_protocol_kia; - -/** - * Allocate SubGhzProtocolDecoderKIA. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderKIA* pointer to a SubGhzProtocolDecoderKIA instance - */ -void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderKIA. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - */ -void subghz_protocol_decoder_kia_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderKIA. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - */ -void subghz_protocol_decoder_kia_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderKIA. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_kia_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderKIA. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderKIA instance - * @param output Resulting text - */ -void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.c b/applications/main/subghz/protocols/kinggates_stylo_4k.c deleted file mode 100644 index 5f2a83d77..000000000 --- a/applications/main/subghz/protocols/kinggates_stylo_4k.c +++ /dev/null @@ -1,581 +0,0 @@ -#include "kinggates_stylo_4k.h" -#include "keeloq_common.h" - -#include "../subghz_keystore.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocoKingGates_stylo_4k" - -static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = { - .te_short = 400, - .te_long = 1100, - .te_delta = 140, - .min_count_bit_for_found = 89, -}; - -struct SubGhzProtocolDecoderKingGates_stylo_4k { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - SubGhzKeystore* keystore; -}; - -struct SubGhzProtocolEncoderKingGates_stylo_4k { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - SubGhzKeystore* keystore; -}; - -typedef enum { - KingGates_stylo_4kDecoderStepReset = 0, - KingGates_stylo_4kDecoderStepCheckPreambula, - KingGates_stylo_4kDecoderStepCheckStartBit, - KingGates_stylo_4kDecoderStepSaveDuration, - KingGates_stylo_4kDecoderStepCheckDuration, -} KingGates_stylo_4kDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = { - .alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc, - .free = subghz_protocol_decoder_kinggates_stylo_4k_free, - - .feed = subghz_protocol_decoder_kinggates_stylo_4k_feed, - .reset = subghz_protocol_decoder_kinggates_stylo_4k_reset, - - .get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data, - .serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize, - .deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize, - .get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = { - .alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc, - .free = subghz_protocol_encoder_kinggates_stylo_4k_free, - - .deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize, - .stop = subghz_protocol_encoder_kinggates_stylo_4k_stop, - .yield = subghz_protocol_encoder_kinggates_stylo_4k_yield, -}; - -const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = { - .name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_kinggates_stylo_4k_decoder, - .encoder = &subghz_protocol_kinggates_stylo_4k_encoder, -}; - -// -// Encoder -// - -// Pre define function -static void subghz_protocol_kinggates_stylo_4k_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore); - -void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolEncoderKingGates_stylo_4k* instance = - malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k)); - - instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 512; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - - return instance; -} - -void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; - free(instance->encoder.upload); - free(instance); -} - -void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) { - SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) { - SubGhzProtocolEncoderKingGates_stylo_4k* 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; -} - -/** - * Key generation from simple data - * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance - * @param btn Button number, 4 bit - */ -static bool subghz_protocol_kinggates_stylo_4k_gen_data( - SubGhzProtocolEncoderKingGates_stylo_4k* instance, - uint8_t btn) { - UNUSED(btn); - uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32); - uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53); - int res = 0; - uint32_t decrypt = 0; - - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); - if(res == 0) { - //Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - break; - } - } - instance->generic.cnt = decrypt & 0xFFFF; - - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - - instance->generic.btn = (fix >> 17) & 0x0F; - instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); - - uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt; - - uint64_t encrypt = 0; - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k"); - if(res == 0) { - //Simple Learning - encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key); - encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32); - instance->generic.data_2 = encrypt << 4; - return true; - } - } - - return false; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - * @return true On success - */ -static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload( - SubGhzProtocolEncoderKingGates_stylo_4k* instance, - uint8_t btn) { - furi_assert(instance); - - // Gen new key - if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display - } else { - return false; - } - - size_t index = 0; - - // Start - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500); - - // Send header - for(uint8_t i = 12; i > 0; i--) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - } - - // After header - instance->encoder.upload[index - 1].duration = - (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2; - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2); - - // Send key fix - for(uint8_t i = 53; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - } - } - - // Send key hop - for(uint8_t i = 36; i > 0; i--) { - if(bit_read(instance->generic.data_2, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long); - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short); - } - } - - // Set upload size after generating upload, fix it later - - instance->encoder.size_upload = index; - - return true; -} - -bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderKingGates_stylo_4k* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - subghz_protocol_kinggates_stylo_4k_remote_controller( - &instance->generic, instance->keystore); - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Data"); - break; - } - - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; - } - - subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -// -// Decoder -// -void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolDecoderKingGates_stylo_4k* instance = - malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k)); - instance->base.protocol = &subghz_protocol_kinggates_stylo_4k; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - return instance; -} - -void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - free(instance); -} - -void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; -} - -void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - - switch(instance->decoder.parser_step) { - case KingGates_stylo_4kDecoderStepReset: - if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < - subghz_protocol_kinggates_stylo_4k_const.te_delta) { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula; - instance->header_count++; - } - break; - case KingGates_stylo_4kDecoderStepCheckPreambula: - if((!level) && - (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < - subghz_protocol_kinggates_stylo_4k_const.te_delta)) { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; - break; - } - if((instance->header_count > 2) && - (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) < - subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { - // Found header - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit; - } else { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; - instance->header_count = 0; - } - break; - case KingGates_stylo_4kDecoderStepCheckStartBit: - if((level) && - DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) < - subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->generic.data_2 = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - } - break; - case KingGates_stylo_4kDecoderStepSaveDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { - instance->generic.data = instance->generic.data_2; - instance->generic.data_2 = 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 = KingGates_stylo_4kDecoderStepReset; - instance->decoder.decode_data = 0; - instance->generic.data_2 = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration; - } - } else { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; - instance->header_count = 0; - } - break; - case KingGates_stylo_4kDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) < - subghz_protocol_kinggates_stylo_4k_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) < - subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) < - subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) < - subghz_protocol_kinggates_stylo_4k_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; - instance->header_count = 0; - } - if(instance->decoder.decode_count_bit == 53) { - instance->generic.data_2 = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } - } else { - instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset; - instance->header_count = 0; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param data Input encrypted data - * @param keystore Pointer to a SubGhzKeystore* instance - */ -static void subghz_protocol_kinggates_stylo_4k_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore) { - /** - * 9500us 12*(400/400) 2200/800|1-bit|0-bit| - * _ _ _ __ ___ _ - * ________| |_| |_..._| |_____| |_| |___| |..... - * - * 1-bit 400/1100 us - * 0-bit 1100/400 us - * - * The package consists of 89 bits of data, LSB first - * Data - 1C9037F0C80000 CE280BA00 - * S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0 - * 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000 - * - * Encryption - keeloq Simple Learning - * key C S[3] CNT - * Decrypt - 0xEC270B9C => 0x E C 27 0B9C - * - * - * -*/ - - uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32); - uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53); - bool ret = false; - uint32_t decrypt = 0; - instance->btn = (fix >> 17) & 0x0F; - instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF); - - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) { - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) && - (((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) { - ret = true; - break; - } - } - } - if(ret) { - instance->cnt = decrypt & 0xFFFF; - } else { - instance->btn = 0; - instance->serial = 0; - instance->cnt = 0; - } -} - -uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> (i * 8)) & 0xFF; - } - - if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Data"); - res = false; - } - return res; - - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Data"); - break; - } - - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i]; - } - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderKingGates_stylo_4k* instance = context; - subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore); - - furi_string_cat_printf( - output, - "%s\r\n" - "Key:0x%llX%07llX %dbit\r\n" - "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%04lX\r\n", - instance->generic.protocol_name, - instance->generic.data, - instance->generic.data_2, - instance->generic.data_count_bit, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); -} \ No newline at end of file diff --git a/applications/main/subghz/protocols/kinggates_stylo_4k.h b/applications/main/subghz/protocols/kinggates_stylo_4k.h deleted file mode 100644 index 9717f6715..000000000 --- a/applications/main/subghz/protocols/kinggates_stylo_4k.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once -#include "base.h" - -#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k" - -typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k; -typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k; - -extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder; -extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k; - -/** - * Allocate SubGhzProtocolEncoderKingGates_stylo_4k. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - */ -void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderKingGates_stylo_4k. - * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - */ -void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - */ -void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderKingGates_stylo_4k. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - */ -void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderKingGates_stylo_4k. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - */ -void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - */ -void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderKingGates_stylo_4k. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_kinggates_stylo_4k_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance - * @param output Resulting text - */ -void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear.c b/applications/main/subghz/protocols/linear.c deleted file mode 100644 index 2fc8b20c8..000000000 --- a/applications/main/subghz/protocols/linear.c +++ /dev/null @@ -1,352 +0,0 @@ -#include "linear.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolLinear" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c" -#define DATA_TO_DIP(dip) \ - (dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \ - (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \ - (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \ - (dip & 0x0001 ? '1' : '0') - -static const SubGhzBlockConst subghz_protocol_linear_const = { - .te_short = 500, - .te_long = 1500, - .te_delta = 150, - .min_count_bit_for_found = 10, -}; - -struct SubGhzProtocolDecoderLinear { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderLinear { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - LinearDecoderStepReset = 0, - LinearDecoderStepSaveDuration, - LinearDecoderStepCheckDuration, -} LinearDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_linear_decoder = { - .alloc = subghz_protocol_decoder_linear_alloc, - .free = subghz_protocol_decoder_linear_free, - - .feed = subghz_protocol_decoder_linear_feed, - .reset = subghz_protocol_decoder_linear_reset, - - .get_hash_data = subghz_protocol_decoder_linear_get_hash_data, - .serialize = subghz_protocol_decoder_linear_serialize, - .deserialize = subghz_protocol_decoder_linear_deserialize, - .get_string = subghz_protocol_decoder_linear_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_linear_encoder = { - .alloc = subghz_protocol_encoder_linear_alloc, - .free = subghz_protocol_encoder_linear_free, - - .deserialize = subghz_protocol_encoder_linear_deserialize, - .stop = subghz_protocol_encoder_linear_stop, - .yield = subghz_protocol_encoder_linear_yield, -}; - -const SubGhzProtocol subghz_protocol_linear = { - .name = SUBGHZ_PROTOCOL_LINEAR_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_linear_decoder, - .encoder = &subghz_protocol_linear_encoder, -}; - -void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderLinear* instance = malloc(sizeof(SubGhzProtocolEncoderLinear)); - - instance->base.protocol = &subghz_protocol_linear; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop) - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_linear_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderLinear* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderLinear instance - * @return true On success - */ -static bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinear* 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; - } - - //Send key data - 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_linear_const.te_short * 3); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 3); - } - } - //Send end bit - if(bit_read(instance->generic.data, 0)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3); - //Send PT_GUARD - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 42); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short); - //Send PT_GUARD - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 44); - } - - return true; -} - -bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderLinear* 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_linear_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); - - if(!subghz_protocol_encoder_linear_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_linear_stop(void* context) { - SubGhzProtocolEncoderLinear* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_linear_yield(void* context) { - SubGhzProtocolEncoderLinear* 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_linear_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderLinear* instance = malloc(sizeof(SubGhzProtocolDecoderLinear)); - instance->base.protocol = &subghz_protocol_linear; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_linear_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - free(instance); -} - -void subghz_protocol_decoder_linear_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - instance->decoder.parser_step = LinearDecoderStepReset; -} - -void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - switch(instance->decoder.parser_step) { - case LinearDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) < - subghz_protocol_linear_const.te_delta * 20)) { - //Found header Linear - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } - break; - case LinearDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = LinearDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - break; - case LinearDecoderStepCheckDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_linear_const.te_short * 5)) { - instance->decoder.parser_step = LinearDecoderStepReset; - //checking that the duration matches the guardtime - if((DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) > - subghz_protocol_linear_const.te_delta * 20)) { - break; - } - if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < - subghz_protocol_linear_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else if( - DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < - subghz_protocol_linear_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } - if(instance->decoder.decode_count_bit == - subghz_protocol_linear_const.min_count_bit_for_found) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - - 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); - } - break; - } - - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) < - subghz_protocol_linear_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_linear_const.te_long) < - subghz_protocol_linear_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) < - subghz_protocol_linear_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short) < - subghz_protocol_linear_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_linear_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_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_linear_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderLinear* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Yek:0x%08lX\r\n" - "DIP:" DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo, - DATA_TO_DIP(code_found_lo)); -} diff --git a/applications/main/subghz/protocols/linear.h b/applications/main/subghz/protocols/linear.h deleted file mode 100644 index 923337ac2..000000000 --- a/applications/main/subghz/protocols/linear.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_LINEAR_NAME "Linear" - -typedef struct SubGhzProtocolDecoderLinear SubGhzProtocolDecoderLinear; -typedef struct SubGhzProtocolEncoderLinear SubGhzProtocolEncoderLinear; - -extern const SubGhzProtocolDecoder subghz_protocol_linear_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_linear_encoder; -extern const SubGhzProtocol subghz_protocol_linear; - -/** - * Allocate SubGhzProtocolEncoderLinear. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderLinear* pointer to a SubGhzProtocolEncoderLinear instance - */ -void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderLinear. - * @param context Pointer to a SubGhzProtocolEncoderLinear instance - */ -void subghz_protocol_encoder_linear_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderLinear instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderLinear instance - */ -void subghz_protocol_encoder_linear_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderLinear instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_linear_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderLinear. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderLinear* pointer to a SubGhzProtocolDecoderLinear instance - */ -void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderLinear. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - */ -void subghz_protocol_decoder_linear_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderLinear. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - */ -void subghz_protocol_decoder_linear_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderLinear. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_linear_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderLinear. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderLinear instance - * @param output Resulting text - */ -void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/linear_delta3.c b/applications/main/subghz/protocols/linear_delta3.c deleted file mode 100644 index 869edac84..000000000 --- a/applications/main/subghz/protocols/linear_delta3.c +++ /dev/null @@ -1,359 +0,0 @@ -#include "linear_delta3.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolLinearDelta3" - -#define DIP_PATTERN "%c%c%c%c%c%c%c%c" -#define DATA_TO_DIP(dip) \ - (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \ - (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \ - (dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0') - -static const SubGhzBlockConst subghz_protocol_linear_delta3_const = { - .te_short = 500, - .te_long = 2000, - .te_delta = 150, - .min_count_bit_for_found = 8, -}; - -struct SubGhzProtocolDecoderLinearDelta3 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint32_t last_data; -}; - -struct SubGhzProtocolEncoderLinearDelta3 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - LinearDecoderStepReset = 0, - LinearDecoderStepSaveDuration, - LinearDecoderStepCheckDuration, -} LinearDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = { - .alloc = subghz_protocol_decoder_linear_delta3_alloc, - .free = subghz_protocol_decoder_linear_delta3_free, - - .feed = subghz_protocol_decoder_linear_delta3_feed, - .reset = subghz_protocol_decoder_linear_delta3_reset, - - .get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data, - .serialize = subghz_protocol_decoder_linear_delta3_serialize, - .deserialize = subghz_protocol_decoder_linear_delta3_deserialize, - .get_string = subghz_protocol_decoder_linear_delta3_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = { - .alloc = subghz_protocol_encoder_linear_delta3_alloc, - .free = subghz_protocol_encoder_linear_delta3_free, - - .deserialize = subghz_protocol_encoder_linear_delta3_deserialize, - .stop = subghz_protocol_encoder_linear_delta3_stop, - .yield = subghz_protocol_encoder_linear_delta3_yield, -}; - -const SubGhzProtocol subghz_protocol_linear_delta3 = { - .name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_linear_delta3_decoder, - .encoder = &subghz_protocol_linear_delta3_encoder, -}; - -void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderLinearDelta3* instance = - malloc(sizeof(SubGhzProtocolEncoderLinearDelta3)); - - instance->base.protocol = &subghz_protocol_linear_delta3; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 16; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_linear_delta3_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderLinearDelta3* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance - * @return true On success - */ -static bool - subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* 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; - } - - //Send key data - 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_linear_delta3_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long); - } - } - //Send end bit - if(bit_read(instance->generic.data, 0)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short); - //Send PT_GUARD - instance->encoder.upload[index] = level_duration_make( - false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long); - //Send PT_GUARD - instance->encoder.upload[index] = level_duration_make( - false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70); - } - - return true; -} - -bool subghz_protocol_encoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_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); - - if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_linear_delta3_stop(void* context) { - SubGhzProtocolEncoderLinearDelta3* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) { - SubGhzProtocolEncoderLinearDelta3* 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_linear_delta3_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderLinearDelta3* instance = - malloc(sizeof(SubGhzProtocolDecoderLinearDelta3)); - instance->base.protocol = &subghz_protocol_linear_delta3; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_linear_delta3_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - free(instance); -} - -void subghz_protocol_decoder_linear_delta3_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - instance->decoder.parser_step = LinearDecoderStepReset; - instance->last_data = 0; -} - -void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - switch(instance->decoder.parser_step) { - case LinearDecoderStepReset: - if((!level) && - (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) < - subghz_protocol_linear_delta3_const.te_delta * 24)) { - //Found header Linear - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } - break; - case LinearDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = LinearDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - break; - case LinearDecoderStepCheckDuration: - if(!level) { - if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) { - instance->decoder.parser_step = LinearDecoderStepReset; - if(DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < - subghz_protocol_linear_delta3_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < - subghz_protocol_linear_delta3_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } - if(instance->decoder.decode_count_bit == - subghz_protocol_linear_delta3_const.min_count_bit_for_found) { - if((instance->last_data == instance->decoder.decode_data) && - instance->last_data) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - - 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 = LinearDecoderStepSaveDuration; - instance->last_data = instance->decoder.decode_data; - } - break; - } - - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) < - subghz_protocol_linear_delta3_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) < - subghz_protocol_linear_delta3_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) < - subghz_protocol_linear_delta3_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) < - subghz_protocol_linear_delta3_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = LinearDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - - } else { - instance->decoder.parser_step = LinearDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8)); -} - -bool subghz_protocol_decoder_linear_delta3_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_linear_delta3_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_linear_delta3_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderLinearDelta3* instance = context; - - uint32_t data = instance->generic.data & 0xFF; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX\r\n" - "DIP:" DIP_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - data, - DATA_TO_DIP(data)); -} diff --git a/applications/main/subghz/protocols/linear_delta3.h b/applications/main/subghz/protocols/linear_delta3.h deleted file mode 100644 index 2f0a32e68..000000000 --- a/applications/main/subghz/protocols/linear_delta3.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3" - -typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3; -typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3; - -extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder; -extern const SubGhzProtocol subghz_protocol_linear_delta3; - -/** - * Allocate SubGhzProtocolEncoderLinearDelta3. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance - */ -void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderLinearDelta3. - * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance - */ -void subghz_protocol_encoder_linear_delta3_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance - */ -void subghz_protocol_encoder_linear_delta3_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderLinearDelta3. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance - */ -void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderLinearDelta3. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - */ -void subghz_protocol_decoder_linear_delta3_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderLinearDelta3. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - */ -void subghz_protocol_decoder_linear_delta3_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderLinearDelta3. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_linear_delta3_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderLinearDelta3. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_linear_delta3_deserialize( - void* context, - FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/magellan.c b/applications/main/subghz/protocols/magellan.c deleted file mode 100644 index 67d3fe3d3..000000000 --- a/applications/main/subghz/protocols/magellan.c +++ /dev/null @@ -1,445 +0,0 @@ -#include "magellan.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolMagellan" - -static const SubGhzBlockConst subghz_protocol_magellan_const = { - .te_short = 200, - .te_long = 400, - .te_delta = 100, - .min_count_bit_for_found = 32, -}; - -struct SubGhzProtocolDecoderMagellan { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderMagellan { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - MagellanDecoderStepReset = 0, - MagellanDecoderStepCheckPreambula, - MagellanDecoderStepFoundPreambula, - MagellanDecoderStepSaveDuration, - MagellanDecoderStepCheckDuration, -} MagellanDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = { - .alloc = subghz_protocol_decoder_magellan_alloc, - .free = subghz_protocol_decoder_magellan_free, - - .feed = subghz_protocol_decoder_magellan_feed, - .reset = subghz_protocol_decoder_magellan_reset, - - .get_hash_data = subghz_protocol_decoder_magellan_get_hash_data, - .serialize = subghz_protocol_decoder_magellan_serialize, - .deserialize = subghz_protocol_decoder_magellan_deserialize, - .get_string = subghz_protocol_decoder_magellan_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = { - .alloc = subghz_protocol_encoder_magellan_alloc, - .free = subghz_protocol_encoder_magellan_free, - - .deserialize = subghz_protocol_encoder_magellan_deserialize, - .stop = subghz_protocol_encoder_magellan_stop, - .yield = subghz_protocol_encoder_magellan_yield, -}; - -const SubGhzProtocol subghz_protocol_magellan = { - .name = SUBGHZ_PROTOCOL_MAGELLAN_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_magellan_decoder, - .encoder = &subghz_protocol_magellan_encoder, -}; - -void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan)); - - instance->base.protocol = &subghz_protocol_magellan; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_magellan_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderMagellan* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderMagellan instance - * @return true On success - */ -static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) { - furi_assert(instance); - - size_t index = 0; - - //Send header - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); - for(uint8_t i = 0; i < 12; i++) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); - } - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); - - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short); - } - } - - //Send stop bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100); - - instance->encoder.size_upload = index; - return true; -} - -bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderMagellan* 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_magellan_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); - - if(!subghz_protocol_encoder_magellan_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_magellan_stop(void* context) { - SubGhzProtocolEncoderMagellan* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_magellan_yield(void* context) { - SubGhzProtocolEncoderMagellan* 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_magellan_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan)); - instance->base.protocol = &subghz_protocol_magellan; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_magellan_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - free(instance); -} - -void subghz_protocol_decoder_magellan_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - instance->decoder.parser_step = MagellanDecoderStepReset; -} - -uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x00; - size_t i, j; - for(i = 0; i < len; i++) { - crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) - crc = (uint8_t)((crc << 1) ^ 0x31); - else - crc <<= 1; - } - } - return crc; -} - -static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) { - uint8_t data[3] = { - instance->decoder.decode_data >> 24, - instance->decoder.decode_data >> 16, - instance->decoder.decode_data >> 8}; - return (instance->decoder.decode_data & 0xFF) == - subghz_protocol_magellan_crc8(data, sizeof(data)); -} - -void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - - switch(instance->decoder.parser_step) { - case MagellanDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta)) { - instance->decoder.parser_step = MagellanDecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - - case MagellanDecoderStepCheckPreambula: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta)) { - // Found header - instance->header_count++; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < - subghz_protocol_magellan_const.te_delta * 2) && - (instance->header_count > 10)) { - instance->decoder.parser_step = MagellanDecoderStepFoundPreambula; - } else { - instance->decoder.parser_step = MagellanDecoderStepReset; - } - } - break; - - case MagellanDecoderStepFoundPreambula: - if(level) { - instance->decoder.te_last = duration; - } else { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) < - subghz_protocol_magellan_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < - subghz_protocol_magellan_const.te_delta * 2)) { - instance->decoder.parser_step = MagellanDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = MagellanDecoderStepReset; - } - } - break; - - case MagellanDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = MagellanDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = MagellanDecoderStepReset; - } - break; - - case MagellanDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) < - subghz_protocol_magellan_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = MagellanDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) < - subghz_protocol_magellan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) < - subghz_protocol_magellan_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = MagellanDecoderStepSaveDuration; - } else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) { - //Found stop bit - if((instance->decoder.decode_count_bit == - subghz_protocol_magellan_const.min_count_bit_for_found) && - subghz_protocol_magellan_check_crc(instance)) { - 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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = MagellanDecoderStepReset; - } else { - instance->decoder.parser_step = MagellanDecoderStepReset; - } - } else { - instance->decoder.parser_step = MagellanDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) { - /* -* package 32b data 24b CRC8 -* 0x037AE4828 => 001101111010111001001000 00101000 -* -* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC -* -* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236) -* -* event codes -* bit_0: 1-Open/Motion, 0-close/ok -* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok) -* bit_2: ? -* bit_3: 1-power on -* bit_4: model type - wireless reed -* bit_5: model type - motion sensor -* bit_6: ? -* bit_7: ? -* -*/ - uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24); - instance->serial = data_rev & 0xFFFF; - instance->btn = (data_rev >> 16) & 0xFF; -} - -static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) { - furi_string_cat_printf( - output, - "%s%s%s%s%s%s%s%s", - ((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") : - (event & 0x1 ? " Motion" : " Ok")), - ((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""), - ((event >> 2) & 0x1 ? ", ?" : ""), - ((event >> 3) & 0x1 ? ", Power On" : ""), - ((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""), - ((event >> 5) & 0x1 ? ", MT:Motion_\nSensor" : ""), - ((event >> 6) & 0x1 ? ", ?" : ""), - ((event >> 7) & 0x1 ? ", ?" : "")); -} - -uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_magellan_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_magellan_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_magellan_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderMagellan* instance = context; - subghz_protocol_magellan_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Sn:%03ld%03ld, Event:0x%02X\r\n" - "Stat:", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - (instance->generic.serial >> 8) & 0xFF, - instance->generic.serial & 0xFF, - instance->generic.btn); - - subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output); -} diff --git a/applications/main/subghz/protocols/magellan.h b/applications/main/subghz/protocols/magellan.h deleted file mode 100644 index a179c9cb4..000000000 --- a/applications/main/subghz/protocols/magellan.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan" - -typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan; -typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan; - -extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder; -extern const SubGhzProtocol subghz_protocol_magellan; - -/** - * Allocate SubGhzProtocolEncoderMagellan. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance - */ -void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderMagellan. - * @param context Pointer to a SubGhzProtocolEncoderMagellan instance - */ -void subghz_protocol_encoder_magellan_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderMagellan instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderMagellan instance - */ -void subghz_protocol_encoder_magellan_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderMagellan instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_magellan_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderMagellan. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance - */ -void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderMagellan. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - */ -void subghz_protocol_decoder_magellan_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderMagellan. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - */ -void subghz_protocol_decoder_magellan_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderMagellan. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_magellan_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderMagellan. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderMagellan instance - * @param output Resulting text - */ -void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/marantec.c b/applications/main/subghz/protocols/marantec.c deleted file mode 100644 index d557c29b0..000000000 --- a/applications/main/subghz/protocols/marantec.c +++ /dev/null @@ -1,393 +0,0 @@ -#include "marantec.h" -#include -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolMarantec" - -static const SubGhzBlockConst subghz_protocol_marantec_const = { - .te_short = 1000, - .te_long = 2000, - .te_delta = 200, - .min_count_bit_for_found = 49, -}; - -struct SubGhzProtocolDecoderMarantec { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - ManchesterState manchester_saved_state; - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderMarantec { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - MarantecDecoderStepReset = 0, - MarantecDecoderFoundHeader, - MarantecDecoderStepDecoderData, -} MarantecDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = { - .alloc = subghz_protocol_decoder_marantec_alloc, - .free = subghz_protocol_decoder_marantec_free, - - .feed = subghz_protocol_decoder_marantec_feed, - .reset = subghz_protocol_decoder_marantec_reset, - - .get_hash_data = subghz_protocol_decoder_marantec_get_hash_data, - .serialize = subghz_protocol_decoder_marantec_serialize, - .deserialize = subghz_protocol_decoder_marantec_deserialize, - .get_string = subghz_protocol_decoder_marantec_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = { - .alloc = subghz_protocol_encoder_marantec_alloc, - .free = subghz_protocol_encoder_marantec_free, - - .deserialize = subghz_protocol_encoder_marantec_deserialize, - .stop = subghz_protocol_encoder_marantec_stop, - .yield = subghz_protocol_encoder_marantec_yield, -}; - -const SubGhzProtocol subghz_protocol_marantec = { - .name = SUBGHZ_PROTOCOL_MARANTEC_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_marantec_decoder, - .encoder = &subghz_protocol_marantec_encoder, -}; - -void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec)); - - instance->base.protocol = &subghz_protocol_marantec; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_marantec_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderMarantec* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static LevelDuration - subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) { - LevelDuration data = {.duration = 0, .level = 0}; - switch(result) { - case ManchesterEncoderResultShortLow: - data.duration = subghz_protocol_marantec_const.te_short; - data.level = false; - break; - case ManchesterEncoderResultLongLow: - data.duration = subghz_protocol_marantec_const.te_long; - data.level = false; - break; - case ManchesterEncoderResultLongHigh: - data.duration = subghz_protocol_marantec_const.te_long; - data.level = true; - break; - case ManchesterEncoderResultShortHigh: - data.duration = subghz_protocol_marantec_const.te_short; - data.level = true; - break; - - default: - furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); - break; - } - return level_duration_make(data.level, data.duration); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderMarantec instance - */ -static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) { - furi_assert(instance); - size_t index = 0; - - ManchesterEncoderState enc_state; - manchester_encoder_reset(&enc_state); - ManchesterEncoderResult result; - - if(!manchester_encoder_advance( - &enc_state, - bit_read(instance->generic.data, instance->generic.data_count_bit - 1), - &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_marantec_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, - bit_read(instance->generic.data, instance->generic.data_count_bit - 1), - &result); - } - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5); - - for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) { - if(!manchester_encoder_advance( - &enc_state, bit_read(instance->generic.data, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_marantec_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, bit_read(instance->generic.data, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_marantec_add_duration_to_upload(result); - } - instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - instance->encoder.size_upload = index; -} - -uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x08; - size_t i, j; - for(i = 0; i < len; i++) { - crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) - crc = (uint8_t)((crc << 1) ^ 0x1D); - else - crc <<= 1; - } - } - return crc; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) { - instance->btn = (instance->data >> 16) & 0xF; - instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); -} - -bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderMarantec* 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_marantec_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_marantec_remote_controller(&instance->generic); - subghz_protocol_encoder_marantec_get_upload(instance); - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_marantec_stop(void* context) { - SubGhzProtocolEncoderMarantec* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_marantec_yield(void* context) { - SubGhzProtocolEncoderMarantec* 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_marantec_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec)); - instance->base.protocol = &subghz_protocol_marantec; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_marantec_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - free(instance); -} - -void subghz_protocol_decoder_marantec_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - ManchesterEvent event = ManchesterEventReset; - - switch(instance->decoder.parser_step) { - case MarantecDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) < - subghz_protocol_marantec_const.te_delta * 8)) { - //Found header marantec - instance->decoder.parser_step = MarantecDecoderStepDecoderData; - instance->decoder.decode_data = 1; - instance->decoder.decode_count_bit = 1; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - } - break; - case MarantecDecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < - subghz_protocol_marantec_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < - subghz_protocol_marantec_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 + - subghz_protocol_marantec_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_marantec_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.decode_data = 1; - instance->decoder.decode_count_bit = 1; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - } else { - instance->decoder.parser_step = MarantecDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) < - subghz_protocol_marantec_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) < - subghz_protocol_marantec_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = MarantecDecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_marantec_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_marantec_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_marantec_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderMarantec* instance = context; - subghz_protocol_marantec_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%07lX \r\n" - "Btn:%X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.serial, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/marantec.h b/applications/main/subghz/protocols/marantec.h deleted file mode 100644 index e330ccf16..000000000 --- a/applications/main/subghz/protocols/marantec.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec" - -typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec; -typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec; - -extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder; -extern const SubGhzProtocol subghz_protocol_marantec; - -/** - * Allocate SubGhzProtocolEncoderMarantec. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance - */ -void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderMarantec. - * @param context Pointer to a SubGhzProtocolEncoderMarantec instance - */ -void subghz_protocol_encoder_marantec_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderMarantec instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderMarantec instance - */ -void subghz_protocol_encoder_marantec_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderMarantec instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_marantec_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderMarantec. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance - */ -void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderMarantec. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - */ -void subghz_protocol_decoder_marantec_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderMarantec. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - */ -void subghz_protocol_decoder_marantec_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderMarantec. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_marantec_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderMarantec. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderMarantec instance - * @param output Resulting text - */ -void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/megacode.c b/applications/main/subghz/protocols/megacode.c deleted file mode 100644 index 05b5b6894..000000000 --- a/applications/main/subghz/protocols/megacode.c +++ /dev/null @@ -1,429 +0,0 @@ -#include "megacode.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://wiki.cuvoodoo.info/doku.php?id=megacode - * https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf - * https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf - * https://github.com/aaronsp777/megadecoder - * https://github.com/rjmendez/Linear_keyfob - * https://github.com/j07rdi/Linear_MegaCode_Garage_Remote - * - */ - -#define TAG "SubGhzProtocolMegaCode" - -static const SubGhzBlockConst subghz_protocol_megacode_const = { - .te_short = 1000, - .te_long = 1000, - .te_delta = 200, - .min_count_bit_for_found = 24, -}; - -struct SubGhzProtocolDecoderMegaCode { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - uint8_t last_bit; -}; - -struct SubGhzProtocolEncoderMegaCode { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - MegaCodeDecoderStepReset = 0, - MegaCodeDecoderStepFoundStartBit, - MegaCodeDecoderStepSaveDuration, - MegaCodeDecoderStepCheckDuration, -} MegaCodeDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_megacode_decoder = { - .alloc = subghz_protocol_decoder_megacode_alloc, - .free = subghz_protocol_decoder_megacode_free, - - .feed = subghz_protocol_decoder_megacode_feed, - .reset = subghz_protocol_decoder_megacode_reset, - - .get_hash_data = subghz_protocol_decoder_megacode_get_hash_data, - .serialize = subghz_protocol_decoder_megacode_serialize, - .deserialize = subghz_protocol_decoder_megacode_deserialize, - .get_string = subghz_protocol_decoder_megacode_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_megacode_encoder = { - .alloc = subghz_protocol_encoder_megacode_alloc, - .free = subghz_protocol_encoder_megacode_free, - - .deserialize = subghz_protocol_encoder_megacode_deserialize, - .stop = subghz_protocol_encoder_megacode_stop, - .yield = subghz_protocol_encoder_megacode_yield, -}; - -const SubGhzProtocol subghz_protocol_megacode = { - .name = SUBGHZ_PROTOCOL_MEGACODE_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_megacode_decoder, - .encoder = &subghz_protocol_megacode_encoder, -}; - -void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode)); - - instance->base.protocol = &subghz_protocol_megacode; - 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_megacode_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderMegaCode* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance - * @return true On success - */ -static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) { - furi_assert(instance); - uint8_t last_bit = 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; - } - - /* - * Due to the nature of the protocol - * - * 00000 1 - * _____|-| = 1 becomes - * - * 00 1 000 - * __|-|___ = 0 becomes - * - * it's easier for us to generate an upload backwards - */ - - size_t index = size_upload - 1; - - // Send end level - instance->encoder.upload[index--] = - level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); - if(bit_read(instance->generic.data, 0)) { - last_bit = 1; - } else { - last_bit = 0; - } - - //Send key data - for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) { - if(bit_read(instance->generic.data, i)) { - //if bit 1 - instance->encoder.upload[index--] = level_duration_make( - false, - last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 : - (uint32_t)subghz_protocol_megacode_const.te_short * 2); - last_bit = 1; - } else { - //if bit 0 - instance->encoder.upload[index--] = level_duration_make( - false, - last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 : - (uint32_t)subghz_protocol_megacode_const.te_short * 5); - last_bit = 0; - } - instance->encoder.upload[index--] = - level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short); - } - - //Send PT_GUARD - if(bit_read(instance->generic.data, 0)) { - //if end bit 1 - instance->encoder.upload[index] = - level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11); - } else { - //if end bit 1 - instance->encoder.upload[index] = - level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14); - } - - return true; -} - -bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderMegaCode* 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_megacode_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); - - if(!subghz_protocol_encoder_megacode_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_megacode_stop(void* context) { - SubGhzProtocolEncoderMegaCode* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_megacode_yield(void* context) { - SubGhzProtocolEncoderMegaCode* 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_megacode_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode)); - instance->base.protocol = &subghz_protocol_megacode; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_megacode_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - free(instance); -} - -void subghz_protocol_decoder_megacode_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - instance->decoder.parser_step = MegaCodeDecoderStepReset; -} - -void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - switch(instance->decoder.parser_step) { - case MegaCodeDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) < - subghz_protocol_megacode_const.te_delta * 17)) { //10..16ms - //Found header MegaCode - instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit; - } - break; - case MegaCodeDecoderStepFoundStartBit: - if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < - subghz_protocol_megacode_const.te_delta)) { - //Found start bit MegaCode - instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->last_bit = 1; - - } else { - instance->decoder.parser_step = MegaCodeDecoderStepReset; - } - break; - case MegaCodeDecoderStepSaveDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_megacode_const.te_short * 10)) { - instance->decoder.parser_step = MegaCodeDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_megacode_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); - } - break; - } - - if(!instance->last_bit) { - instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3; - } else { - instance->decoder.te_last = duration; - } - instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = MegaCodeDecoderStepReset; - } - break; - case MegaCodeDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) < - subghz_protocol_megacode_const.te_delta * 5) && - (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < - subghz_protocol_megacode_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->last_bit = 1; - instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) < - subghz_protocol_megacode_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) < - subghz_protocol_megacode_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->last_bit = 0; - instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration; - } else - instance->decoder.parser_step = MegaCodeDecoderStepReset; - } else { - instance->decoder.parser_step = MegaCodeDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) { - /* - * Short: 1000 µs - * Long: 1000 µs - * Gap: 11000 .. 14000 µs - * A Linear Megacode transmission consists of 24 bit frames starting with - * the most significant bit and ending with the least. Each of the 24 bit - * frames is 6 milliseconds wide and always contains a single 1 millisecond - * pulse. A frame with more than 1 pulse or a frame with no pulse is invalid - * and a receiver should reset and begin watching for another start bit. - * Start bit is always 1. - * - * - * Example (I created with my own remote): - * Remote “A” has the code “17316”, a Facility Code of “3”, and a single button. - * Start bit (S) = 1 - * Facility Code 3 (F) = 0011 - * Remote Code (Key) 17316 = 43A4 = 0100001110100100 - * Button (Btn) 1 = 001 - * S F Key Btn - * Result = 1|0011|0100001110100100|001 - * - * 00000 1 - * _____|-| = 1 becomes - * - * 00 1 000 - * __|-|___ = 0 becomes - * - * The device needs to transmit with a 9000 µs gap between retransmissions: - * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 - * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 - * wait 9000 µs - * 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001 - * 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001 - * - */ - if((instance->data >> 23) == 1) { - instance->serial = (instance->data >> 3) & 0xFFFF; - instance->btn = instance->data & 0b111; - instance->cnt = (instance->data >> 19) & 0b1111; - } else { - instance->serial = 0; - instance->btn = 0; - instance->cnt = 0; - } -} - -uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_megacode_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_megacode_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_megacode_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderMegaCode* instance = context; - subghz_protocol_megacode_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%06lX\r\n" - "Sn:0x%04lX - %lu\r\n" - "Facility:%lX Btn:%X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)instance->generic.data, - instance->generic.serial, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/megacode.h b/applications/main/subghz/protocols/megacode.h deleted file mode 100644 index e31434fa3..000000000 --- a/applications/main/subghz/protocols/megacode.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_MEGACODE_NAME "MegaCode" - -typedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode; -typedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode; - -extern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder; -extern const SubGhzProtocol subghz_protocol_megacode; - -/** - * Allocate SubGhzProtocolEncoderMegaCode. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance - */ -void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderMegaCode. - * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance - */ -void subghz_protocol_encoder_megacode_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance - */ -void subghz_protocol_encoder_megacode_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderMegaCode instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_megacode_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderMegaCode. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance - */ -void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderMegaCode. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - */ -void subghz_protocol_decoder_megacode_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderMegaCode. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - */ -void subghz_protocol_decoder_megacode_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderMegaCode. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_megacode_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderMegaCode. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderMegaCode instance - * @param output Resulting text - */ -void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_radio.c b/applications/main/subghz/protocols/nero_radio.c deleted file mode 100644 index c8126b1e1..000000000 --- a/applications/main/subghz/protocols/nero_radio.c +++ /dev/null @@ -1,397 +0,0 @@ -#include "nero_radio.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolNeroRadio" - -static const SubGhzBlockConst subghz_protocol_nero_radio_const = { - .te_short = 200, - .te_long = 400, - .te_delta = 80, - .min_count_bit_for_found = 56, -}; - -struct SubGhzProtocolDecoderNeroRadio { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderNeroRadio { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - NeroRadioDecoderStepReset = 0, - NeroRadioDecoderStepCheckPreambula, - NeroRadioDecoderStepSaveDuration, - NeroRadioDecoderStepCheckDuration, -} NeroRadioDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder = { - .alloc = subghz_protocol_decoder_nero_radio_alloc, - .free = subghz_protocol_decoder_nero_radio_free, - - .feed = subghz_protocol_decoder_nero_radio_feed, - .reset = subghz_protocol_decoder_nero_radio_reset, - - .get_hash_data = subghz_protocol_decoder_nero_radio_get_hash_data, - .serialize = subghz_protocol_decoder_nero_radio_serialize, - .deserialize = subghz_protocol_decoder_nero_radio_deserialize, - .get_string = subghz_protocol_decoder_nero_radio_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder = { - .alloc = subghz_protocol_encoder_nero_radio_alloc, - .free = subghz_protocol_encoder_nero_radio_free, - - .deserialize = subghz_protocol_encoder_nero_radio_deserialize, - .stop = subghz_protocol_encoder_nero_radio_stop, - .yield = subghz_protocol_encoder_nero_radio_yield, -}; - -const SubGhzProtocol subghz_protocol_nero_radio = { - .name = SUBGHZ_PROTOCOL_NERO_RADIO_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_nero_radio_decoder, - .encoder = &subghz_protocol_nero_radio_encoder, -}; - -void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolEncoderNeroRadio)); - - instance->base.protocol = &subghz_protocol_nero_radio; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_nero_radio_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderNeroRadio* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderNeroRadio instance - * @return true On success - */ -static bool - subghz_protocol_encoder_nero_radio_get_upload(SubGhzProtocolEncoderNeroRadio* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = 49 * 2 + 2 + (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; - } - - //Send header - for(uint8_t i = 0; i < 49; i++) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); - } - - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); - - //Send key data - 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_nero_radio_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_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_nero_radio_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37); - } - return true; -} - -bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderNeroRadio* 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_nero_radio_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); - - if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_nero_radio_stop(void* context) { - SubGhzProtocolEncoderNeroRadio* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context) { - SubGhzProtocolEncoderNeroRadio* 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_nero_radio_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolDecoderNeroRadio)); - instance->base.protocol = &subghz_protocol_nero_radio; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_nero_radio_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - free(instance); -} - -void subghz_protocol_decoder_nero_radio_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - instance->decoder.parser_step = NeroRadioDecoderStepReset; -} - -void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - - switch(instance->decoder.parser_step) { - case NeroRadioDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta)) { - instance->decoder.parser_step = NeroRadioDecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - case NeroRadioDecoderStepCheckPreambula: - if(level) { - if((DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short * 4) < - subghz_protocol_nero_radio_const.te_delta)) { - instance->decoder.te_last = duration; - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - } else if( - DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta) { - if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta) { - // Found header - instance->header_count++; - break; - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short * 4) < - subghz_protocol_nero_radio_const.te_delta) { - // Found start bit - if(instance->header_count > 40) { - instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - break; - case NeroRadioDecoderStepSaveDuration: - if(level) { - instance->decoder.te_last = duration; - instance->decoder.parser_step = NeroRadioDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - break; - case NeroRadioDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 + - subghz_protocol_nero_radio_const.te_delta * 2)) { - //Found stop bit - if(DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < - subghz_protocol_nero_radio_const.te_delta) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - } - instance->decoder.parser_step = NeroRadioDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nero_radio_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048 - break; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_long) < - subghz_protocol_nero_radio_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) < - subghz_protocol_nero_radio_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) < - subghz_protocol_nero_radio_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroRadioDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_nero_radio_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_radio_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_nero_radio_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderNeroRadio* instance = context; - - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - uint32_t code_found_reverse_hi = code_found_reverse >> 32; - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Yek:0x%lX%08lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - code_found_reverse_lo); -} diff --git a/applications/main/subghz/protocols/nero_radio.h b/applications/main/subghz/protocols/nero_radio.h deleted file mode 100644 index 361da6173..000000000 --- a/applications/main/subghz/protocols/nero_radio.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_NERO_RADIO_NAME "Nero Radio" - -typedef struct SubGhzProtocolDecoderNeroRadio SubGhzProtocolDecoderNeroRadio; -typedef struct SubGhzProtocolEncoderNeroRadio SubGhzProtocolEncoderNeroRadio; - -extern const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder; -extern const SubGhzProtocol subghz_protocol_nero_radio; - -/** - * Allocate SubGhzProtocolEncoderNeroRadio. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderNeroRadio* pointer to a SubGhzProtocolEncoderNeroRadio instance - */ -void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderNeroRadio. - * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance - */ -void subghz_protocol_encoder_nero_radio_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance - */ -void subghz_protocol_encoder_nero_radio_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderNeroRadio. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderNeroRadio* pointer to a SubGhzProtocolDecoderNeroRadio instance - */ -void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderNeroRadio. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - */ -void subghz_protocol_decoder_nero_radio_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderNeroRadio. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - */ -void subghz_protocol_decoder_nero_radio_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderNeroRadio. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_nero_radio_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderNeroRadio. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance - * @param output Resulting text - */ -void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nero_sketch.c b/applications/main/subghz/protocols/nero_sketch.c deleted file mode 100644 index b124b717b..000000000 --- a/applications/main/subghz/protocols/nero_sketch.c +++ /dev/null @@ -1,382 +0,0 @@ -#include "nero_sketch.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolNeroSketch" - -static const SubGhzBlockConst subghz_protocol_nero_sketch_const = { - .te_short = 330, - .te_long = 660, - .te_delta = 150, - .min_count_bit_for_found = 40, -}; - -struct SubGhzProtocolDecoderNeroSketch { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderNeroSketch { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - NeroSketchDecoderStepReset = 0, - NeroSketchDecoderStepCheckPreambula, - NeroSketchDecoderStepSaveDuration, - NeroSketchDecoderStepCheckDuration, -} NeroSketchDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder = { - .alloc = subghz_protocol_decoder_nero_sketch_alloc, - .free = subghz_protocol_decoder_nero_sketch_free, - - .feed = subghz_protocol_decoder_nero_sketch_feed, - .reset = subghz_protocol_decoder_nero_sketch_reset, - - .get_hash_data = subghz_protocol_decoder_nero_sketch_get_hash_data, - .serialize = subghz_protocol_decoder_nero_sketch_serialize, - .deserialize = subghz_protocol_decoder_nero_sketch_deserialize, - .get_string = subghz_protocol_decoder_nero_sketch_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder = { - .alloc = subghz_protocol_encoder_nero_sketch_alloc, - .free = subghz_protocol_encoder_nero_sketch_free, - - .deserialize = subghz_protocol_encoder_nero_sketch_deserialize, - .stop = subghz_protocol_encoder_nero_sketch_stop, - .yield = subghz_protocol_encoder_nero_sketch_yield, -}; - -const SubGhzProtocol subghz_protocol_nero_sketch = { - .name = SUBGHZ_PROTOCOL_NERO_SKETCH_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_nero_sketch_decoder, - .encoder = &subghz_protocol_nero_sketch_encoder, -}; - -void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolEncoderNeroSketch)); - - instance->base.protocol = &subghz_protocol_nero_sketch; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_nero_sketch_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderNeroSketch* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderNeroSketch instance - * @return true On success - */ -static bool - subghz_protocol_encoder_nero_sketch_get_upload(SubGhzProtocolEncoderNeroSketch* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = 47 * 2 + 2 + (instance->generic.data_count_bit * 2) + 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; - } - - //Send header - for(uint8_t i = 0; i < 47; i++) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - } - - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 4); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_long); - } - } - - //Send stop bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 3); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short); - - return true; -} - -bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderNeroSketch* 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_nero_sketch_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); - - if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_nero_sketch_stop(void* context) { - SubGhzProtocolEncoderNeroSketch* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context) { - SubGhzProtocolEncoderNeroSketch* 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_nero_sketch_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolDecoderNeroSketch)); - instance->base.protocol = &subghz_protocol_nero_sketch; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_nero_sketch_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - free(instance); -} - -void subghz_protocol_decoder_nero_sketch_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - instance->decoder.parser_step = NeroSketchDecoderStepReset; -} - -void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - - switch(instance->decoder.parser_step) { - case NeroSketchDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta)) { - instance->decoder.parser_step = NeroSketchDecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - case NeroSketchDecoderStepCheckPreambula: - if(level) { - if((DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short * 4) < - subghz_protocol_nero_sketch_const.te_delta)) { - instance->decoder.te_last = duration; - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - } else if( - DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta) { - if(DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta) { - // Found header - instance->header_count++; - break; - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short * 4) < - subghz_protocol_nero_sketch_const.te_delta) { - // Found start bit - if(instance->header_count > 40) { - instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - break; - case NeroSketchDecoderStepSaveDuration: - if(level) { - if(duration >= (subghz_protocol_nero_sketch_const.te_short * 2 + - subghz_protocol_nero_sketch_const.te_delta * 2)) { - //Found stop bit - instance->decoder.parser_step = NeroSketchDecoderStepReset; - if(instance->decoder.decode_count_bit == - subghz_protocol_nero_sketch_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = NeroSketchDecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - break; - case NeroSketchDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_long) < - subghz_protocol_nero_sketch_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_long) < - subghz_protocol_nero_sketch_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) < - subghz_protocol_nero_sketch_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - } else { - instance->decoder.parser_step = NeroSketchDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_nero_sketch_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_nero_sketch_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_nero_sketch_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderNeroSketch* instance = context; - - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - uint32_t code_found_reverse_hi = code_found_reverse >> 32; - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Yek:0x%lX%08lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - code_found_reverse_lo); -} diff --git a/applications/main/subghz/protocols/nero_sketch.h b/applications/main/subghz/protocols/nero_sketch.h deleted file mode 100644 index ac87fb00a..000000000 --- a/applications/main/subghz/protocols/nero_sketch.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_NERO_SKETCH_NAME "Nero Sketch" - -typedef struct SubGhzProtocolDecoderNeroSketch SubGhzProtocolDecoderNeroSketch; -typedef struct SubGhzProtocolEncoderNeroSketch SubGhzProtocolEncoderNeroSketch; - -extern const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder; -extern const SubGhzProtocol subghz_protocol_nero_sketch; - -/** - * Allocate SubGhzProtocolEncoderNeroSketch. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderNeroSketch* pointer to a SubGhzProtocolEncoderNeroSketch instance - */ -void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderNeroSketch. - * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance - */ -void subghz_protocol_encoder_nero_sketch_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance - */ -void subghz_protocol_encoder_nero_sketch_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderNeroSketch. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderNeroSketch* pointer to a SubGhzProtocolDecoderNeroSketch instance - */ -void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderNeroSketch. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - */ -void subghz_protocol_decoder_nero_sketch_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderNeroSketch. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - */ -void subghz_protocol_decoder_nero_sketch_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderNeroSketch. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_nero_sketch_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderNeroSketch. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance - * @param output Resulting text - */ -void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flo.c b/applications/main/subghz/protocols/nice_flo.c deleted file mode 100644 index a57d5f4da..000000000 --- a/applications/main/subghz/protocols/nice_flo.c +++ /dev/null @@ -1,330 +0,0 @@ -#include "nice_flo.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolNiceFLO" - -static const SubGhzBlockConst subghz_protocol_nice_flo_const = { - .te_short = 700, - .te_long = 1400, - .te_delta = 200, - .min_count_bit_for_found = 12, -}; - -struct SubGhzProtocolDecoderNiceFlo { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderNiceFlo { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - NiceFloDecoderStepReset = 0, - NiceFloDecoderStepFoundStartBit, - NiceFloDecoderStepSaveDuration, - NiceFloDecoderStepCheckDuration, -} NiceFloDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder = { - .alloc = subghz_protocol_decoder_nice_flo_alloc, - .free = subghz_protocol_decoder_nice_flo_free, - - .feed = subghz_protocol_decoder_nice_flo_feed, - .reset = subghz_protocol_decoder_nice_flo_reset, - - .get_hash_data = subghz_protocol_decoder_nice_flo_get_hash_data, - .serialize = subghz_protocol_decoder_nice_flo_serialize, - .deserialize = subghz_protocol_decoder_nice_flo_deserialize, - .get_string = subghz_protocol_decoder_nice_flo_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder = { - .alloc = subghz_protocol_encoder_nice_flo_alloc, - .free = subghz_protocol_encoder_nice_flo_free, - - .deserialize = subghz_protocol_encoder_nice_flo_deserialize, - .stop = subghz_protocol_encoder_nice_flo_stop, - .yield = subghz_protocol_encoder_nice_flo_yield, -}; - -const SubGhzProtocol subghz_protocol_nice_flo = { - .name = SUBGHZ_PROTOCOL_NICE_FLO_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_nice_flo_decoder, - .encoder = &subghz_protocol_nice_flo_encoder, -}; - -void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlo)); - - instance->base.protocol = &subghz_protocol_nice_flo; - 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_nice_flo_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderNiceFlo* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderNiceFlo instance - * @return true On success - */ -static bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNiceFlo* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short * 36); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderNiceFlo* 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_nice_flo_const.min_count_bit_for_found) || - (instance->generic.data_count_bit > - 2 * subghz_protocol_nice_flo_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); - - if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_nice_flo_stop(void* context) { - SubGhzProtocolEncoderNiceFlo* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context) { - SubGhzProtocolEncoderNiceFlo* 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_nice_flo_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlo)); - instance->base.protocol = &subghz_protocol_nice_flo; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_nice_flo_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - free(instance); -} - -void subghz_protocol_decoder_nice_flo_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - instance->decoder.parser_step = NiceFloDecoderStepReset; -} - -void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - - switch(instance->decoder.parser_step) { - case NiceFloDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short * 36) < - subghz_protocol_nice_flo_const.te_delta * 36)) { - //Found header Nice Flo - instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; - } - break; - case NiceFloDecoderStepFoundStartBit: - if(!level) { - break; - } else if( - DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < - subghz_protocol_nice_flo_const.te_delta) { - //Found start bit Nice Flo - instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = NiceFloDecoderStepReset; - } - break; - case NiceFloDecoderStepSaveDuration: - if(!level) { //save interval - if(duration >= (subghz_protocol_nice_flo_const.te_short * 4)) { - instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit >= - subghz_protocol_nice_flo_const.min_count_bit_for_found) { - instance->generic.serial = 0x0; - instance->generic.btn = 0x0; - - 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); - } - break; - } - instance->decoder.te_last = duration; - instance->decoder.parser_step = NiceFloDecoderStepCheckDuration; - } else { - instance->decoder.parser_step = NiceFloDecoderStepReset; - } - break; - case NiceFloDecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_short) < - subghz_protocol_nice_flo_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_long) < - subghz_protocol_nice_flo_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_long) < - subghz_protocol_nice_flo_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) < - subghz_protocol_nice_flo_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = NiceFloDecoderStepSaveDuration; - } else - instance->decoder.parser_step = NiceFloDecoderStepReset; - } else { - instance->decoder.parser_step = NiceFloDecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_nice_flo_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if((instance->generic.data_count_bit < - subghz_protocol_nice_flo_const.min_count_bit_for_found) || - (instance->generic.data_count_bit > - 2 * subghz_protocol_nice_flo_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_nice_flo_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlo* instance = context; - - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Yek:0x%08lX\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_lo, - code_found_reverse_lo); -} diff --git a/applications/main/subghz/protocols/nice_flo.h b/applications/main/subghz/protocols/nice_flo.h deleted file mode 100644 index e382e6146..000000000 --- a/applications/main/subghz/protocols/nice_flo.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_NICE_FLO_NAME "Nice FLO" - -typedef struct SubGhzProtocolDecoderNiceFlo SubGhzProtocolDecoderNiceFlo; -typedef struct SubGhzProtocolEncoderNiceFlo SubGhzProtocolEncoderNiceFlo; - -extern const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder; -extern const SubGhzProtocol subghz_protocol_nice_flo; - -/** - * Allocate SubGhzProtocolEncoderNiceFlo. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderNiceFlo* pointer to a SubGhzProtocolEncoderNiceFlo instance - */ -void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderNiceFlo. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance - */ -void subghz_protocol_encoder_nice_flo_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance - */ -void subghz_protocol_encoder_nice_flo_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderNiceFlo. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderNiceFlo* pointer to a SubGhzProtocolDecoderNiceFlo instance - */ -void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderNiceFlo. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - */ -void subghz_protocol_decoder_nice_flo_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderNiceFlo. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - */ -void subghz_protocol_decoder_nice_flo_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderNiceFlo. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_nice_flo_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderNiceFlo. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance - * @param output Resulting text - */ -void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/nice_flor_s.c b/applications/main/subghz/protocols/nice_flor_s.c deleted file mode 100644 index 6447676cc..000000000 --- a/applications/main/subghz/protocols/nice_flor_s.c +++ /dev/null @@ -1,694 +0,0 @@ -#include "nice_flor_s.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * https://phreakerclub.com/1615 - * https://phreakerclub.com/forum/showthread.php?t=2360 - * https://vrtp.ru/index.php?showtopic=27867 - */ - -#define TAG "SubGhzProtocolNiceFlorS" - -#define NICE_ONE_COUNT_BIT 72 -#define NICE_ONE_NAME "Nice One" - -static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = { - .te_short = 500, - .te_long = 1000, - .te_delta = 300, - .min_count_bit_for_found = 52, -}; - -struct SubGhzProtocolDecoderNiceFlorS { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - const char* nice_flor_s_rainbow_table_file_name; - uint64_t data; -}; - -struct SubGhzProtocolEncoderNiceFlorS { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - const char* nice_flor_s_rainbow_table_file_name; -}; - -typedef enum { - NiceFlorSDecoderStepReset = 0, - NiceFlorSDecoderStepCheckHeader, - NiceFlorSDecoderStepFoundHeader, - NiceFlorSDecoderStepSaveDuration, - NiceFlorSDecoderStepCheckDuration, -} NiceFlorSDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder = { - .alloc = subghz_protocol_decoder_nice_flor_s_alloc, - .free = subghz_protocol_decoder_nice_flor_s_free, - - .feed = subghz_protocol_decoder_nice_flor_s_feed, - .reset = subghz_protocol_decoder_nice_flor_s_reset, - - .get_hash_data = subghz_protocol_decoder_nice_flor_s_get_hash_data, - .serialize = subghz_protocol_decoder_nice_flor_s_serialize, - .deserialize = subghz_protocol_decoder_nice_flor_s_deserialize, - .get_string = subghz_protocol_decoder_nice_flor_s_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder = { - .alloc = subghz_protocol_encoder_nice_flor_s_alloc, - .free = subghz_protocol_encoder_nice_flor_s_free, - - .deserialize = subghz_protocol_encoder_nice_flor_s_deserialize, - .stop = subghz_protocol_encoder_nice_flor_s_stop, - .yield = subghz_protocol_encoder_nice_flor_s_yield, -}; - -const SubGhzProtocol subghz_protocol_nice_flor_s = { - .name = SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | - SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_nice_flor_s_decoder, - .encoder = &subghz_protocol_nice_flor_s_encoder, -}; - -static void subghz_protocol_nice_flor_s_remote_controller( - SubGhzBlockGeneric* instance, - const char* file_name); - -void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolEncoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlorS)); - - instance->base.protocol = &subghz_protocol_nice_flor_s; - instance->generic.protocol_name = instance->base.protocol->name; - instance->nice_flor_s_rainbow_table_file_name = - subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); - if(instance->nice_flor_s_rainbow_table_file_name) { - FURI_LOG_D( - TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); - } - instance->encoder.repeat = 10; - instance->encoder.size_upload = 1728; //wrong!! upload 186*16 = 2976 - actual size about 1728 - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_nice_flor_s_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderNiceFlorS* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance - * @return true On success - */ -static void subghz_protocol_encoder_nice_flor_s_get_upload( - SubGhzProtocolEncoderNiceFlorS* instance, - uint8_t btn, - const char* file_name) { - furi_assert(instance); - size_t index = 0; - btn = instance->generic.btn; - - size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16); - if(size_upload > instance->encoder.size_upload) { - FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer."); - } else { - instance->encoder.size_upload = size_upload; - } - - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; - uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name); - - for(int i = 0; i < 16; i++) { - static const uint64_t loops[16] = { - 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF}; - - uint8_t byte; - - byte = btn << 4 | (0xF ^ btn ^ loops[i]); - instance->generic.data = (uint64_t)byte << 44 | enc_part; - - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 37); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); - } - } - //Send stop bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); - //instance->encoder.upload[index++] = - //level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); - } - instance->encoder.size_upload = index; -} - -bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderNiceFlorS* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_nice_flor_s_remote_controller( - &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - subghz_protocol_encoder_nice_flor_s_get_upload( - instance, instance->generic.btn, instance->nice_flor_s_rainbow_table_file_name); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_nice_flor_s_stop(void* context) { - SubGhzProtocolEncoderNiceFlorS* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { - SubGhzProtocolEncoderNiceFlorS* 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; -} - -// /** -// * Read bytes from rainbow table -// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 -// * @return crc -// */ -// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { -// uint8_t crc = 0; -// uint8_t crc_data = 0xff; -// for(uint8_t i = 4; i < 68; i++) { -// if(subghz_protocol_blocks_get_bit_array(p, i)) { -// crc = crc_data ^ 1; -// } else { -// crc = crc_data; -// } -// crc_data >>= 1; -// if((crc & 0x01)) { -// crc_data ^= 0x97; -// } -// } -// crc = 0; -// for(uint8_t i = 0; i < 8; i++) { -// crc <<= 1; -// if((crc_data >> i) & 0x01) crc = crc | 1; -// } -// return crc; -// } - -// /** -// * Read bytes from rainbow table -// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX -// * @param num_parcel parcel number 0..15 -// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down -// */ -// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { -// uint8_t k = 0; -// uint8_t crc = 0; -// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); -// if(num_parcel < 4) { -// k = 0x8f; -// } else { -// k = 0x80; -// } - -// if(!hold_bit) { -// hold_bit = 0; -// } else { -// hold_bit = 0x10; -// } -// k = num_parcel ^ k; -// p[7] = k; -// p[8] = hold_bit ^ (k << 4); - -// crc = subghz_protocol_nice_one_crc(p); - -// p[8] |= crc >> 4; -// p[9] = crc << 4; -// } - -/** - * Read bytes from rainbow table - * @param file_name Full path to rainbow table the file - * @param address Byte address in file - * @return data - */ -static uint8_t - subghz_protocol_nice_flor_s_get_byte_in_file(const char* file_name, uint32_t address) { - if(!file_name) return 0; - - uint8_t buffer[1] = {0}; - if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint8_t))) { - return buffer[0]; - } else { - return 0; - } -} - -static inline void subghz_protocol_decoder_nice_flor_s_magic_xor(uint8_t* p, uint8_t k) { - for(uint8_t i = 1; i < 6; i++) { - p[i] ^= k; - } -} - -uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name) { - uint8_t* p = (uint8_t*)&data; - - uint8_t k = 0; - for(uint8_t y = 0; y < 2; y++) { - k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); - subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); - - p[5] &= 0x0f; - p[0] ^= k & 0xe0; - k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; - subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); - - p[5] &= 0x0f; - p[0] ^= k & 0x7; - if(y == 0) { - k = p[0]; - p[0] = p[1]; - p[1] = k; - } - } - - p[5] = ~p[5] & 0x0f; - k = ~p[4]; - p[4] = ~p[0]; - p[0] = ~p[2]; - p[2] = k; - k = ~p[3]; - p[3] = ~p[1]; - p[1] = k; - - return data; -} - -static uint64_t - subghz_protocol_nice_flor_s_decrypt(SubGhzBlockGeneric* instance, const char* file_name) { - furi_assert(instance); - uint64_t data = instance->data; - uint8_t* p = (uint8_t*)&data; - - uint8_t k = 0; - - k = ~p[4]; - p[5] = ~p[5]; - p[4] = ~p[2]; - p[2] = ~p[0]; - p[0] = k; - k = ~p[3]; - p[3] = ~p[1]; - p[1] = k; - - for(uint8_t y = 0; y < 2; y++) { - k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25; - subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); - - p[5] &= 0x0f; - p[0] ^= k & 0x7; - k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f); - subghz_protocol_decoder_nice_flor_s_magic_xor(p, k); - - p[5] &= 0x0f; - p[0] ^= k & 0xe0; - - if(y == 0) { - k = p[0]; - p[0] = p[1]; - p[1] = k; - } - } - - return data; -} - -bool subghz_protocol_nice_flor_s_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderNiceFlorS* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->generic.data_count_bit = 52; - uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt; - uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt( - decrypt, instance->nice_flor_s_rainbow_table_file_name); - uint8_t byte = btn << 4 | (0xF ^ btn ^ 0x3); - instance->generic.data = (uint64_t)byte << 44 | enc_part; - - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - return res; -} - -void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolDecoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlorS)); - instance->base.protocol = &subghz_protocol_nice_flor_s; - instance->generic.protocol_name = instance->base.protocol->name; - instance->nice_flor_s_rainbow_table_file_name = - subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment); - if(instance->nice_flor_s_rainbow_table_file_name) { - FURI_LOG_D( - TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); - } - return instance; -} - -void subghz_protocol_decoder_nice_flor_s_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - instance->nice_flor_s_rainbow_table_file_name = NULL; - free(instance); -} - -void subghz_protocol_decoder_nice_flor_s_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - instance->decoder.parser_step = NiceFlorSDecoderStepReset; -} - -void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - - switch(instance->decoder.parser_step) { - case NiceFlorSDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 38) < - subghz_protocol_nice_flor_s_const.te_delta * 38)) { - //Found start header Nice Flor-S - instance->decoder.parser_step = NiceFlorSDecoderStepCheckHeader; - } - break; - case NiceFlorSDecoderStepCheckHeader: - if((level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < - subghz_protocol_nice_flor_s_const.te_delta * 3)) { - //Found next header Nice Flor-S - instance->decoder.parser_step = NiceFlorSDecoderStepFoundHeader; - } else { - instance->decoder.parser_step = NiceFlorSDecoderStepReset; - } - break; - case NiceFlorSDecoderStepFoundHeader: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < - subghz_protocol_nice_flor_s_const.te_delta * 3)) { - //Found header Nice Flor-S - instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = NiceFlorSDecoderStepReset; - } - break; - case NiceFlorSDecoderStepSaveDuration: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) < - subghz_protocol_nice_flor_s_const.te_delta) { - //Found STOP bit - instance->decoder.parser_step = NiceFlorSDecoderStepReset; - if((instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) || - (instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) { - instance->generic.data = instance->data; - instance->data = instance->decoder.decode_data; - instance->decoder.decode_data = instance->generic.data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - } - break; - } else { - //save interval - instance->decoder.te_last = duration; - instance->decoder.parser_step = NiceFlorSDecoderStepCheckDuration; - } - } - break; - case NiceFlorSDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_short) < - subghz_protocol_nice_flor_s_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_long) < - subghz_protocol_nice_flor_s_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_long) < - subghz_protocol_nice_flor_s_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short) < - subghz_protocol_nice_flor_s_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration; - } else - instance->decoder.parser_step = NiceFlorSDecoderStepReset; - } else { - instance->decoder.parser_step = NiceFlorSDecoderStepReset; - } - if(instance->decoder.decode_count_bit == - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) { - instance->data = instance->decoder.decode_data; - instance->decoder.decode_data = 0; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param file_name Full path to rainbow table the file - */ -static void subghz_protocol_nice_flor_s_remote_controller( - SubGhzBlockGeneric* instance, - const char* file_name) { - /* - * Protocol Nice Flor-S - * Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP - * P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8; - * P1 (4-bit) - batch repetition number, calculated by the formula: - * P1 = 0xF ^ P0 ^ n; where n changes from 1 to 15, then 0, and then in a circle - * key 1: {0xE,0xF,0xC,0xD,0xA,0xB,0x8,0x9,0x6,0x7,0x4,0x5,0x2,0x3,0x0,0x1}; - * key 2: {0xD,0xC,0xF,0xE,0x9,0x8,0xB,0xA,0x5,0x4,0x7,0x6,0x1,0x0,0x3,0x2}; - * key 3: {0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC,0x3,0x2,0x1,0x0,0x7,0x6,0x5,0x4}; - * key 4: {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8}; - * P2 (4-bit) - part of the serial number, P2 = (K ^ S3) & 0xF; - * P3 (byte) - the major part of the encrypted index - * P4 (byte) - the low-order part of the encrypted index - * P5 (byte) - part of the serial number, P5 = K ^ S2; - * P6 (byte) - part of the serial number, P6 = K ^ S1; - * P7 (byte) - part of the serial number, P7 = K ^ S0; - * K (byte) - depends on P3 and P4, K = Fk(P3, P4); - * S3,S2,S1,S0 - serial number of the console 28 bit. - * - * data => 0x1c5783607f7b3 key serial cnt - * decrypt => 0x10436c6820444 => 0x1 0436c682 0444 - * - * Protocol Nice One - * Generally repeats the Nice Flor-S protocol, but there are a few changes - * Packet format first 52 bytes repeat Nice Flor-S protocol - * The additional 20 bytes contain the code of the pressed button, - * the button hold bit and the CRC of the entire message. - * START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP - * P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n; - * P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc - * P10 (4-bit) - 4 lo bit crc - * key+b crc - * data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button - * data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold - * - * A small button hold counter (0..15) is stored between each press, - * i.e. if 1 press of the button stops counter 6, then the next press - * of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)... - * further up to 15 with overflow - * - */ - if(!file_name) { - instance->cnt = 0; - instance->serial = 0; - instance->btn = 0; - } else { - uint64_t decrypt = subghz_protocol_nice_flor_s_decrypt(instance, file_name); - instance->cnt = decrypt & 0xFFFF; - instance->serial = (decrypt >> 16) & 0xFFFFFFF; - instance->btn = (decrypt >> 48) & 0xF; - } -} - -uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_nice_flor_s_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { - if(res && - !flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) { - FURI_LOG_E(TAG, "Unable to add Data"); - res = false; - } - } - return res; -} - -bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if((instance->generic.data_count_bit != - subghz_protocol_nice_flor_s_const.min_count_bit_for_found) && - (instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint32_t temp = 0; - if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) { - FURI_LOG_E(TAG, "Missing Data"); - break; - } - instance->data = (uint64_t)temp; - } - - ret = true; - } while(false); - return ret; -} - -void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderNiceFlorS* instance = context; - - subghz_protocol_nice_flor_s_remote_controller( - &instance->generic, instance->nice_flor_s_rainbow_table_file_name); - - if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%013llX%llX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - NICE_ONE_NAME, - instance->generic.data_count_bit, - instance->generic.data, - instance->data, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); - } else { - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%013llX\r\n" - "Sn:%05lX\r\n" - "Cnt:%04lX Btn:%02X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - instance->generic.data, - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); - } -} diff --git a/applications/main/subghz/protocols/nice_flor_s.h b/applications/main/subghz/protocols/nice_flor_s.h deleted file mode 100644 index e333fc979..000000000 --- a/applications/main/subghz/protocols/nice_flor_s.h +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME "Nice FloR-S" - -typedef struct SubGhzProtocolDecoderNiceFlorS SubGhzProtocolDecoderNiceFlorS; -typedef struct SubGhzProtocolEncoderNiceFlorS SubGhzProtocolEncoderNiceFlorS; - -extern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder; -extern const SubGhzProtocol subghz_protocol_nice_flor_s; - -/** - * Allocate SubGhzProtocolEncoderNiceFlorS. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderNiceFlorS* pointer to a SubGhzProtocolEncoderNiceFlorS instance - */ -void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderNiceFlorS. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance - */ -void subghz_protocol_encoder_nice_flor_s_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance - */ -void subghz_protocol_encoder_nice_flor_s_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context); - -uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name); - -/** - * New remote generation. - * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number - * @param btn Button number, 4 bit - * @param cnt Counter value, 16 bit - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_nice_flor_s_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset); - -/** - * Allocate SubGhzProtocolDecoderNiceFlorS. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderNiceFlorS* pointer to a SubGhzProtocolDecoderNiceFlorS instance - */ -void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderNiceFlorS. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - */ -void subghz_protocol_decoder_nice_flor_s_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderNiceFlorS. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - */ -void subghz_protocol_decoder_nice_flor_s_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderNiceFlorS. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_nice_flor_s_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderNiceFlorS. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance - * @param output Resulting text - */ -void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/phoenix_v2.c b/applications/main/subghz/protocols/phoenix_v2.c deleted file mode 100644 index b3d6f1e98..000000000 --- a/applications/main/subghz/protocols/phoenix_v2.c +++ /dev/null @@ -1,339 +0,0 @@ -#include "phoenix_v2.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolPhoenix_V2" - -//transmission only static mode - -static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = { - .te_short = 427, - .te_long = 853, - .te_delta = 100, - .min_count_bit_for_found = 52, -}; - -struct SubGhzProtocolDecoderPhoenix_V2 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; -}; - -struct SubGhzProtocolEncoderPhoenix_V2 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - Phoenix_V2DecoderStepReset = 0, - Phoenix_V2DecoderStepFoundStartBit, - Phoenix_V2DecoderStepSaveDuration, - Phoenix_V2DecoderStepCheckDuration, -} Phoenix_V2DecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = { - .alloc = subghz_protocol_decoder_phoenix_v2_alloc, - .free = subghz_protocol_decoder_phoenix_v2_free, - - .feed = subghz_protocol_decoder_phoenix_v2_feed, - .reset = subghz_protocol_decoder_phoenix_v2_reset, - - .get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data, - .serialize = subghz_protocol_decoder_phoenix_v2_serialize, - .deserialize = subghz_protocol_decoder_phoenix_v2_deserialize, - .get_string = subghz_protocol_decoder_phoenix_v2_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = { - .alloc = subghz_protocol_encoder_phoenix_v2_alloc, - .free = subghz_protocol_encoder_phoenix_v2_free, - - .deserialize = subghz_protocol_encoder_phoenix_v2_deserialize, - .stop = subghz_protocol_encoder_phoenix_v2_stop, - .yield = subghz_protocol_encoder_phoenix_v2_yield, -}; - -const SubGhzProtocol subghz_protocol_phoenix_v2 = { - .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_phoenix_v2_decoder, - .encoder = &subghz_protocol_phoenix_v2_encoder, -}; - -void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2)); - - instance->base.protocol = &subghz_protocol_phoenix_v2; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_phoenix_v2_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderPhoenix_V2* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - * @return true On success - */ -static bool - subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) { - furi_assert(instance); - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - //Send header - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60); - //Send start bit - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6); - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(!bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long); - } - } - return true; -} - -bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderPhoenix_V2* 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_phoenix_v2_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); - - if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_phoenix_v2_stop(void* context) { - SubGhzProtocolEncoderPhoenix_V2* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) { - SubGhzProtocolEncoderPhoenix_V2* 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_phoenix_v2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2)); - instance->base.protocol = &subghz_protocol_phoenix_v2; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_phoenix_v2_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - free(instance); -} - -void subghz_protocol_decoder_phoenix_v2_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - instance->decoder.parser_step = Phoenix_V2DecoderStepReset; -} - -void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - - switch(instance->decoder.parser_step) { - case Phoenix_V2DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) < - subghz_protocol_phoenix_v2_const.te_delta * 30)) { - //Found Preambula - instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; - } - break; - case Phoenix_V2DecoderStepFoundStartBit: - if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) < - subghz_protocol_phoenix_v2_const.te_delta * 4))) { - //Found start bit - instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.parser_step = Phoenix_V2DecoderStepReset; - } - break; - case Phoenix_V2DecoderStepSaveDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 + - subghz_protocol_phoenix_v2_const.te_delta)) { - instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit; - if(instance->decoder.decode_count_bit == - subghz_protocol_phoenix_v2_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration; - } - } - break; - case Phoenix_V2DecoderStepCheckDuration: - if(level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) < - subghz_protocol_phoenix_v2_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) < - subghz_protocol_phoenix_v2_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) < - subghz_protocol_phoenix_v2_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) < - subghz_protocol_phoenix_v2_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = Phoenix_V2DecoderStepReset; - } - } else { - instance->decoder.parser_step = Phoenix_V2DecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) { - uint64_t data_rev = - subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4); - instance->serial = data_rev & 0xFFFFFFFF; - instance->cnt = (data_rev >> 40) & 0xFFFF; - instance->btn = (data_rev >> 32) & 0xF; -} - -uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_phoenix_v2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_phoenix_v2_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_phoenix_v2_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderPhoenix_V2* instance = context; - subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%02lX%08lX\r\n" - "Sn:0x%07lX \r\n" - "Btn:%X\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.serial, - instance->generic.btn); -} diff --git a/applications/main/subghz/protocols/phoenix_v2.h b/applications/main/subghz/protocols/phoenix_v2.h deleted file mode 100644 index 48487535e..000000000 --- a/applications/main/subghz/protocols/phoenix_v2.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2" - -typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2; -typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2; - -extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder; -extern const SubGhzProtocol subghz_protocol_phoenix_v2; - -/** - * Allocate SubGhzProtocolEncoderPhoenix_V2. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - */ -void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderPhoenix_V2. - * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - */ -void subghz_protocol_encoder_phoenix_v2_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - */ -void subghz_protocol_encoder_phoenix_v2_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderPhoenix_V2. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - */ -void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderPhoenix_V2. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - */ -void subghz_protocol_decoder_phoenix_v2_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderPhoenix_V2. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - */ -void subghz_protocol_decoder_phoenix_v2_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderPhoenix_V2. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_phoenix_v2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderPhoenix_V2. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/power_smart.c b/applications/main/subghz/protocols/power_smart.c deleted file mode 100644 index 1e8d10e95..000000000 --- a/applications/main/subghz/protocols/power_smart.c +++ /dev/null @@ -1,394 +0,0 @@ -#include "power_smart.h" -#include -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolPowerSmart" -#define POWER_SMART_PACKET_HEADER 0xFD000000AA000000 -#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000 - -#define CHANNEL_PATTERN "%c%c%c%c%c%c" -#define CNT_TO_CHANNEL(dip) \ - (dip & 0x0001 ? '*' : '-'), (dip & 0x0002 ? '*' : '-'), (dip & 0x0004 ? '*' : '-'), \ - (dip & 0x0008 ? '*' : '-'), (dip & 0x0010 ? '*' : '-'), (dip & 0x0020 ? '*' : '-') - -static const SubGhzBlockConst subghz_protocol_power_smart_const = { - .te_short = 225, - .te_long = 450, - .te_delta = 100, - .min_count_bit_for_found = 64, -}; - -struct SubGhzProtocolDecoderPowerSmart { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - ManchesterState manchester_saved_state; - uint16_t header_count; -}; - -struct SubGhzProtocolEncoderPowerSmart { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - PowerSmartDecoderStepReset = 0, - PowerSmartDecoderFoundHeader, - PowerSmartDecoderStepDecoderData, -} PowerSmartDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder = { - .alloc = subghz_protocol_decoder_power_smart_alloc, - .free = subghz_protocol_decoder_power_smart_free, - - .feed = subghz_protocol_decoder_power_smart_feed, - .reset = subghz_protocol_decoder_power_smart_reset, - - .get_hash_data = subghz_protocol_decoder_power_smart_get_hash_data, - .serialize = subghz_protocol_decoder_power_smart_serialize, - .deserialize = subghz_protocol_decoder_power_smart_deserialize, - .get_string = subghz_protocol_decoder_power_smart_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder = { - .alloc = subghz_protocol_encoder_power_smart_alloc, - .free = subghz_protocol_encoder_power_smart_free, - - .deserialize = subghz_protocol_encoder_power_smart_deserialize, - .stop = subghz_protocol_encoder_power_smart_stop, - .yield = subghz_protocol_encoder_power_smart_yield, -}; - -const SubGhzProtocol subghz_protocol_power_smart = { - .name = SUBGHZ_PROTOCOL_POWER_SMART_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_power_smart_decoder, - .encoder = &subghz_protocol_power_smart_encoder, -}; - -void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolEncoderPowerSmart)); - - instance->base.protocol = &subghz_protocol_power_smart; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 1024; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_power_smart_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderPowerSmart* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static LevelDuration - subghz_protocol_encoder_power_smart_add_duration_to_upload(ManchesterEncoderResult result) { - LevelDuration data = {.duration = 0, .level = 0}; - switch(result) { - case ManchesterEncoderResultShortLow: - data.duration = subghz_protocol_power_smart_const.te_short; - data.level = false; - break; - case ManchesterEncoderResultLongLow: - data.duration = subghz_protocol_power_smart_const.te_long; - data.level = false; - break; - case ManchesterEncoderResultLongHigh: - data.duration = subghz_protocol_power_smart_const.te_long; - data.level = true; - break; - case ManchesterEncoderResultShortHigh: - data.duration = subghz_protocol_power_smart_const.te_short; - data.level = true; - break; - - default: - furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); - break; - } - return level_duration_make(data.level, data.duration); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderPowerSmart instance - */ -static void - subghz_protocol_encoder_power_smart_get_upload(SubGhzProtocolEncoderPowerSmart* instance) { - furi_assert(instance); - size_t index = 0; - - ManchesterEncoderState enc_state; - manchester_encoder_reset(&enc_state); - ManchesterEncoderResult result; - - for(int i = 8; i > 0; i--) { - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(!manchester_encoder_advance( - &enc_state, !bit_read(instance->generic.data, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_power_smart_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, !bit_read(instance->generic.data, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_power_smart_add_duration_to_upload(result); - } - } - instance->encoder.upload[index] = subghz_protocol_encoder_power_smart_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_power_smart_const.te_long * 1111); - instance->encoder.size_upload = index; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* instance) { - /* - * Protocol: Manchester encoding, symbol rate ~2222. - * Packet Format: - * 0xFDXXXXYYAAZZZZWW where 0xFD and 0xAA sync word - * XXXX = ~ZZZZ, YY=(~WW)-1 - * Example: - * SYNC1 K1 CHANNEL DATA1 K2 DATA2 SYNC2 ~K1 ~CHANNEL ~DATA2 ~K2 (~DATA2)-1 - * 0xFD2137ACAADEC852 => 11111101 0 010000 10011011 1 10101100 10101010 1 1011110 1100100 0 01010010 - * 0xFDA137ACAA5EC852 => 11111101 1 010000 10011011 1 10101100 10101010 0 1011110 1100100 0 01010010 - * 0xFDA136ACAA5EC952 => 11111101 1 010000 10011011 0 10101100 10101010 0 1011110 1100100 1 01010010 - * - * Key: - * K1K2 - * 0 0 - key_unknown - * 0 1 - key_down - * 1 0 - key_up - * 1 1 - key_stop - * - */ - - instance->btn = ((instance->data >> 54) & 0x02) | ((instance->data >> 40) & 0x1); - instance->serial = ((instance->data >> 33) & 0x3FFF00) | ((instance->data >> 32) & 0xFF); - instance->cnt = ((instance->data >> 49) & 0x3F); -} - -bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderPowerSmart* 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_power_smart_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_power_smart_remote_controller(&instance->generic); - subghz_protocol_encoder_power_smart_get_upload(instance); - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_power_smart_stop(void* context) { - SubGhzProtocolEncoderPowerSmart* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_power_smart_yield(void* context) { - SubGhzProtocolEncoderPowerSmart* 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_power_smart_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolDecoderPowerSmart)); - instance->base.protocol = &subghz_protocol_power_smart; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_power_smart_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - free(instance); -} - -void subghz_protocol_decoder_power_smart_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -bool subghz_protocol_power_smart_chek_valid(uint64_t packet) { - uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF); - uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF); - uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF; - uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1); - return (data_1 == data_2) && (data_3 == data_4); -} - -void subghz_protocol_decoder_power_smart_feed( - void* context, - bool level, - volatile uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - ManchesterEvent event = ManchesterEventReset; - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < - subghz_protocol_power_smart_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < - subghz_protocol_power_smart_const.te_delta * 2) { - event = ManchesterEventLongLow; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) < - subghz_protocol_power_smart_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) < - subghz_protocol_power_smart_const.te_delta * 2) { - event = ManchesterEventLongHigh; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data; - } - if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) == - POWER_SMART_PACKET_HEADER) { - if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = - subghz_protocol_power_smart_const.min_count_bit_for_found; - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } - } - } else { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - } -} - -static const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) { - btn &= 0x3; - const char* name_btn[0x4] = {"Unknown", "Down", "Up", "Stop"}; - return name_btn[btn]; -} - -uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_power_smart_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_power_smart_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_power_smart_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderPowerSmart* instance = context; - subghz_protocol_power_smart_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%07lX \r\n" - "Btn:%s\r\n" - "Channel:" CHANNEL_PATTERN "\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)(instance->generic.data & 0xFFFFFFFF), - instance->generic.serial, - subghz_protocol_power_smart_get_name_button(instance->generic.btn), - CNT_TO_CHANNEL(instance->generic.cnt)); -} diff --git a/applications/main/subghz/protocols/power_smart.h b/applications/main/subghz/protocols/power_smart.h deleted file mode 100644 index 806729f8e..000000000 --- a/applications/main/subghz/protocols/power_smart.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_POWER_SMART_NAME "Power Smart" - -typedef struct SubGhzProtocolDecoderPowerSmart SubGhzProtocolDecoderPowerSmart; -typedef struct SubGhzProtocolEncoderPowerSmart SubGhzProtocolEncoderPowerSmart; - -extern const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder; -extern const SubGhzProtocol subghz_protocol_power_smart; - -/** - * Allocate SubGhzProtocolEncoderPowerSmart. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderPowerSmart* pointer to a SubGhzProtocolEncoderPowerSmart instance - */ -void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderPowerSmart. - * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance - */ -void subghz_protocol_encoder_power_smart_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance - */ -void subghz_protocol_encoder_power_smart_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_power_smart_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderPowerSmart. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderPowerSmart* pointer to a SubGhzProtocolDecoderPowerSmart instance - */ -void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderPowerSmart. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - */ -void subghz_protocol_decoder_power_smart_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderPowerSmart. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - */ -void subghz_protocol_decoder_power_smart_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderPowerSmart. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_power_smart_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderPowerSmart. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance - * @param output Resulting text - */ -void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/princeton.c b/applications/main/subghz/protocols/princeton.c deleted file mode 100644 index 7fc8f6524..000000000 --- a/applications/main/subghz/protocols/princeton.c +++ /dev/null @@ -1,374 +0,0 @@ -#include "princeton.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://phreakerclub.com/447 - * - */ - -#define TAG "SubGhzProtocolPrinceton" - -static const SubGhzBlockConst subghz_protocol_princeton_const = { - .te_short = 390, - .te_long = 1170, - .te_delta = 300, - .min_count_bit_for_found = 24, -}; - -struct SubGhzProtocolDecoderPrinceton { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint32_t te; - uint32_t last_data; -}; - -struct SubGhzProtocolEncoderPrinceton { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint32_t te; -}; - -typedef enum { - PrincetonDecoderStepReset = 0, - PrincetonDecoderStepSaveDuration, - PrincetonDecoderStepCheckDuration, -} PrincetonDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_princeton_decoder = { - .alloc = subghz_protocol_decoder_princeton_alloc, - .free = subghz_protocol_decoder_princeton_free, - - .feed = subghz_protocol_decoder_princeton_feed, - .reset = subghz_protocol_decoder_princeton_reset, - - .get_hash_data = subghz_protocol_decoder_princeton_get_hash_data, - .serialize = subghz_protocol_decoder_princeton_serialize, - .deserialize = subghz_protocol_decoder_princeton_deserialize, - .get_string = subghz_protocol_decoder_princeton_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_princeton_encoder = { - .alloc = subghz_protocol_encoder_princeton_alloc, - .free = subghz_protocol_encoder_princeton_free, - - .deserialize = subghz_protocol_encoder_princeton_deserialize, - .stop = subghz_protocol_encoder_princeton_stop, - .yield = subghz_protocol_encoder_princeton_yield, -}; - -const SubGhzProtocol subghz_protocol_princeton = { - .name = SUBGHZ_PROTOCOL_PRINCETON_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_princeton_decoder, - .encoder = &subghz_protocol_princeton_encoder, -}; - -void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderPrinceton* instance = malloc(sizeof(SubGhzProtocolEncoderPrinceton)); - - instance->base.protocol = &subghz_protocol_princeton; - 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.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_princeton_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderPrinceton* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance - * @return true On success - */ -static bool - subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)instance->te * 3); - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)instance->te * 3); - } - } - - //Send Stop bit - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - //Send PT_GUARD - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 30); - - return true; -} - -bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderPrinceton* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_princeton_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); - - if(!subghz_protocol_encoder_princeton_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_princeton_stop(void* context) { - SubGhzProtocolEncoderPrinceton* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_princeton_yield(void* context) { - SubGhzProtocolEncoderPrinceton* 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_princeton_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderPrinceton* instance = malloc(sizeof(SubGhzProtocolDecoderPrinceton)); - instance->base.protocol = &subghz_protocol_princeton; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_princeton_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - free(instance); -} - -void subghz_protocol_decoder_princeton_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - instance->decoder.parser_step = PrincetonDecoderStepReset; - instance->last_data = 0; -} - -void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - - switch(instance->decoder.parser_step) { - case PrincetonDecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short * 36) < - subghz_protocol_princeton_const.te_delta * 36)) { - //Found Preambula - instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = 0; - } - break; - case PrincetonDecoderStepSaveDuration: - //save duration - if(level) { - instance->decoder.te_last = duration; - instance->te += duration; - instance->decoder.parser_step = PrincetonDecoderStepCheckDuration; - } - break; - case PrincetonDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) { - instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; - if(instance->decoder.decode_count_bit == - subghz_protocol_princeton_const.min_count_bit_for_found) { - if((instance->last_data == instance->decoder.decode_data) && - instance->last_data) { - instance->te /= (instance->decoder.decode_count_bit * 4 + 1); - - 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->last_data = instance->decoder.decode_data; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = 0; - break; - } - - instance->te += duration; - - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_short) < - subghz_protocol_princeton_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_long) < - subghz_protocol_princeton_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_long) < - subghz_protocol_princeton_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short) < - subghz_protocol_princeton_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = PrincetonDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = PrincetonDecoderStepReset; - } - } else { - instance->decoder.parser_step = PrincetonDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) { - instance->serial = instance->data >> 4; - instance->btn = instance->data & 0xF; -} - -uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_princeton_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { - FURI_LOG_E(TAG, "Unable to add TE"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* 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_princeton_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderPrinceton* instance = context; - subghz_protocol_princeton_check_remote_controller(&instance->generic); - uint32_t data_rev = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%08lX\r\n" - "Yek:0x%08lX\r\n" - "Sn:0x%05lX Btn:%01X\r\n" - "Te:%luus\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0xFFFFFF), - data_rev, - instance->generic.serial, - instance->generic.btn, - instance->te); -} diff --git a/applications/main/subghz/protocols/princeton.h b/applications/main/subghz/protocols/princeton.h deleted file mode 100644 index a2a11292e..000000000 --- a/applications/main/subghz/protocols/princeton.h +++ /dev/null @@ -1,115 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton; -typedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton; - -extern const SubGhzProtocolDecoder subghz_protocol_princeton_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_princeton_encoder; -extern const SubGhzProtocol subghz_protocol_princeton; - -/** - * Allocate SubGhzProtocolEncoderPrinceton. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderPrinceton* pointer to a SubGhzProtocolEncoderPrinceton instance - */ -void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderPrinceton. - * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance - */ -void subghz_protocol_encoder_princeton_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance - */ -void subghz_protocol_encoder_princeton_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderPrinceton instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_princeton_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderPrinceton. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderPrinceton* pointer to a SubGhzProtocolDecoderPrinceton instance - */ -void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderPrinceton. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - */ -void subghz_protocol_decoder_princeton_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderPrinceton. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - */ -void subghz_protocol_decoder_princeton_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderPrinceton. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_princeton_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderPrinceton. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderPrinceton instance - * @param output Resulting text - */ -void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/protocols/princeton_for_testing.c b/applications/main/subghz/protocols/princeton_for_testing.c deleted file mode 100644 index 478d14cdf..000000000 --- a/applications/main/subghz/protocols/princeton_for_testing.c +++ /dev/null @@ -1,288 +0,0 @@ -#include "princeton_for_testing.h" - -#include -#include "../blocks/math.h" - -/* - * Help - * https://phreakerclub.com/447 - * - */ - -#define SUBGHZ_PT_SHORT 300 -#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3) -#define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30) -#define SUBGHZ_PT_COUNT_KEY_433 9 -#define SUBGHZ_PT_TIMEOUT_433 900 -#define SUBGHZ_PT_COUNT_KEY_868 9 -#define SUBGHZ_PT_TIMEOUT_868 14000 - -#define TAG "SubGhzProtocolPrinceton" - -struct SubGhzEncoderPrinceton { - uint32_t key; - uint16_t te; - size_t repeat; - size_t front; - size_t count_key; - size_t count_key_package; - uint32_t time_high; - uint32_t time_low; - uint32_t timeout; - uint32_t time_stop; -}; - -typedef enum { - PrincetonDecoderStepReset = 0, - PrincetonDecoderStepSaveDuration, - PrincetonDecoderStepCheckDuration, -} PrincetonDecoderStep; - -SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc() { - SubGhzEncoderPrinceton* instance = malloc(sizeof(SubGhzEncoderPrinceton)); - return instance; -} - -void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance) { - furi_assert(instance); - free(instance); -} - -void subghz_encoder_princeton_for_testing_stop( - SubGhzEncoderPrinceton* instance, - uint32_t time_stop) { - instance->time_stop = time_stop; -} - -void subghz_encoder_princeton_for_testing_set( - SubGhzEncoderPrinceton* instance, - uint32_t key, - size_t repeat, - uint32_t frequency) { - furi_assert(instance); - instance->te = SUBGHZ_PT_SHORT; - instance->key = key; - instance->repeat = repeat + 1; - instance->front = 48; - instance->time_high = 0; - instance->time_low = 0; - if(frequency < 700000000) { - instance->count_key_package = SUBGHZ_PT_COUNT_KEY_433; - instance->timeout = SUBGHZ_PT_TIMEOUT_433; - } else { - instance->count_key_package = SUBGHZ_PT_COUNT_KEY_868; - instance->timeout = SUBGHZ_PT_TIMEOUT_868; - } - - instance->count_key = instance->count_key_package + 3; - - if((furi_get_tick() - instance->time_stop) < instance->timeout) { - instance->time_stop = (instance->timeout - (furi_get_tick() - instance->time_stop)) * 1000; - } else { - instance->time_stop = 0; - } -} - -size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance) { - furi_assert(instance); - return instance->repeat; -} - -void subghz_encoder_princeton_for_testing_print_log(void* context) { - SubGhzEncoderPrinceton* instance = context; - float duty_cycle = - ((float)instance->time_high / (instance->time_high + instance->time_low)) * 100; - FURI_LOG_I( - TAG "Encoder", - "Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%", - instance->time_high + instance->time_low, - instance->time_high, - instance->time_low, - (uint32_t)duty_cycle, - (uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL)); -} - -LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) { - SubGhzEncoderPrinceton* instance = context; - if(instance->repeat == 0) { - subghz_encoder_princeton_for_testing_print_log(instance); - return level_duration_reset(); - } - - size_t bit = instance->front / 2; - bool level = !(instance->front % 2); - - LevelDuration ret; - if(bit < 24) { - uint8_t byte = bit / 8; - uint8_t bit_in_byte = bit % 8; - bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1; - if(value) { - ret = level_duration_make(level, level ? instance->te * 3 : instance->te); - if(level) - instance->time_high += instance->te * 3; - else - instance->time_low += instance->te; - } else { - ret = level_duration_make(level, level ? instance->te : instance->te * 3); - if(level) - instance->time_high += instance->te; - else - instance->time_low += instance->te * 3; - } - } else { - if(instance->time_stop) { - ret = level_duration_make(level, level ? instance->te : instance->time_stop); - if(level) - instance->time_high += instance->te; - else { - instance->time_low += instance->time_stop; - instance->time_stop = 0; - instance->front = 47; - } - } else { - if(--instance->count_key != 0) { - ret = level_duration_make(level, level ? instance->te : instance->te * 30); - if(level) - instance->time_high += instance->te; - else - instance->time_low += instance->te * 30; - } else { - instance->count_key = instance->count_key_package + 2; - instance->front = 48; - ret = level_duration_make(level, level ? instance->te : instance->timeout * 1000); - if(level) - instance->time_high += instance->te; - else - instance->time_low += instance->timeout * 1000; - } - } - } - - instance->front++; - if(instance->front == 50) { - instance->repeat--; - instance->front = 0; - } - return ret; -} - -struct SubGhzDecoderPrinceton { - const char* name; - uint16_t te_long; - uint16_t te_short; - uint16_t te_delta; - uint8_t code_count_bit; - uint8_t code_last_count_bit; - uint64_t code_found; - uint64_t code_last_found; - uint8_t code_min_count_bit_for_found; - uint8_t btn; - uint32_t te_last; - uint32_t serial; - uint32_t parser_step; - uint16_t cnt; - uint32_t te; - - SubGhzDecoderPrincetonCallback callback; - void* context; -}; - -SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void) { - SubGhzDecoderPrinceton* instance = malloc(sizeof(SubGhzDecoderPrinceton)); - - instance->te = SUBGHZ_PT_SHORT; - instance->name = "Princeton"; - instance->code_min_count_bit_for_found = 24; - instance->te_short = 400; - instance->te_long = 1200; - instance->te_delta = 250; - return instance; -} - -void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance) { - furi_assert(instance); - free(instance); -} - -void subghz_decoder_princeton_for_testing_set_callback( - SubGhzDecoderPrinceton* instance, - SubGhzDecoderPrincetonCallback callback, - void* context) { - instance->callback = callback; - instance->context = context; -} - -void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance) { - instance->parser_step = PrincetonDecoderStepReset; -} - -static void - subghz_decoder_princeton_for_testing_add_bit(SubGhzDecoderPrinceton* instance, uint8_t bit) { - instance->code_found = instance->code_found << 1 | bit; - instance->code_count_bit++; -} - -void subghz_decoder_princeton_for_testing_parse( - SubGhzDecoderPrinceton* instance, - bool level, - uint32_t duration) { - switch(instance->parser_step) { - case PrincetonDecoderStepReset: - if((!level) && - (DURATION_DIFF(duration, instance->te_short * 36) < instance->te_delta * 36)) { - //Found Preambula - instance->parser_step = PrincetonDecoderStepSaveDuration; - instance->code_found = 0; - instance->code_count_bit = 0; - instance->te = 0; - } - break; - case PrincetonDecoderStepSaveDuration: - //save duration - if(level) { - instance->te_last = duration; - instance->te += duration; - instance->parser_step = PrincetonDecoderStepCheckDuration; - } - break; - case PrincetonDecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)instance->te_short * 10 + instance->te_delta)) { - instance->parser_step = PrincetonDecoderStepSaveDuration; - if(instance->code_count_bit == instance->code_min_count_bit_for_found) { - instance->te /= (instance->code_count_bit * 4 + 1); - - instance->code_last_found = instance->code_found; - instance->code_last_count_bit = instance->code_count_bit; - instance->serial = instance->code_found >> 4; - instance->btn = (uint8_t)instance->code_found & 0x00000F; - - if(instance->callback) instance->callback(instance, instance->context); - } - instance->code_found = 0; - instance->code_count_bit = 0; - instance->te = 0; - break; - } - - instance->te += duration; - - if((DURATION_DIFF(instance->te_last, instance->te_short) < instance->te_delta) && - (DURATION_DIFF(duration, instance->te_long) < instance->te_delta * 3)) { - subghz_decoder_princeton_for_testing_add_bit(instance, 0); - instance->parser_step = PrincetonDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->te_last, instance->te_long) < instance->te_delta * 3) && - (DURATION_DIFF(duration, instance->te_short) < instance->te_delta)) { - subghz_decoder_princeton_for_testing_add_bit(instance, 1); - instance->parser_step = PrincetonDecoderStepSaveDuration; - } else { - instance->parser_step = PrincetonDecoderStepReset; - } - } else { - instance->parser_step = PrincetonDecoderStepReset; - } - break; - } -} diff --git a/applications/main/subghz/protocols/princeton_for_testing.h b/applications/main/subghz/protocols/princeton_for_testing.h deleted file mode 100644 index 07a37ec5f..000000000 --- a/applications/main/subghz/protocols/princeton_for_testing.h +++ /dev/null @@ -1,97 +0,0 @@ -#pragma once - -#include "base.h" - -/** SubGhzDecoderPrinceton anonymous type */ -typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton; -/** SubGhzEncoderPrinceton anonymous type */ -typedef struct SubGhzEncoderPrinceton SubGhzEncoderPrinceton; - -typedef void (*SubGhzDecoderPrincetonCallback)(SubGhzDecoderPrinceton* parser, void* context); - -/** - * Allocate SubGhzEncoderPrinceton - * @return pointer to SubGhzEncoderPrinceton instance - */ -SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc(); - -/** - * Free SubGhzEncoderPrinceton instance - * @param instance - SubGhzEncoderPrinceton instance - */ -void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance); - -/** - * Forced transmission stop. - * @param instance Pointer to a SubGhzEncoderPrinceton instance - * @param time_stop Transmission stop time, ms - */ -void subghz_encoder_princeton_for_testing_stop( - SubGhzEncoderPrinceton* instance, - uint32_t time_stop); - -/** - * Set new encoder params - * @param instance - SubGhzEncoderPrinceton instance - * @param key - 24bit key - * @param repeat - how many times to repeat - * @param frequency - frequency - */ -void subghz_encoder_princeton_for_testing_set( - SubGhzEncoderPrinceton* instance, - uint32_t key, - size_t repeat, - uint32_t frequency); - -/** - * Get repeat count left - * @param instance - SubGhzEncoderPrinceton instance - * @return repeat count left - */ -size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance); - -/** - * Print encoder log - * @param instance - SubGhzEncoderPrinceton instance - */ -void subghz_encoder_princeton_for_testing_print_log(void* context); - -/** - * Get level duration - * @param instance - SubGhzEncoderPrinceton instance - * @return level duration - */ -LevelDuration subghz_encoder_princeton_for_testing_yield(void* context); - -/** - * Allocate SubGhzDecoderPrinceton - * @return SubGhzDecoderPrinceton* - */ -SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(); - -/** - * Free SubGhzDecoderPrinceton - * @param instance - */ -void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance); - -void subghz_decoder_princeton_for_testing_set_callback( - SubGhzDecoderPrinceton* instance, - SubGhzDecoderPrincetonCallback callback, - void* context); - -/** - * Reset internal state - * @param instance - SubGhzDecoderPrinceton instance - */ -void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance); - -/** - * Parse accepted duration - * @param instance - SubGhzDecoderPrinceton instance - * @param data - LevelDuration level_duration - */ -void subghz_decoder_princeton_for_testing_parse( - SubGhzDecoderPrinceton* instance, - bool level, - uint32_t duration); diff --git a/applications/main/subghz/protocols/protocol_items.c b/applications/main/subghz/protocols/protocol_items.c deleted file mode 100644 index 74244c5ff..000000000 --- a/applications/main/subghz/protocols/protocol_items.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "protocol_items.h" - -const SubGhzProtocol* subghz_protocol_registry_items[] = { - &subghz_protocol_gate_tx, - &subghz_protocol_keeloq, - &subghz_protocol_star_line, - &subghz_protocol_nice_flo, - &subghz_protocol_came, - &subghz_protocol_faac_slh, - &subghz_protocol_nice_flor_s, - &subghz_protocol_came_twee, - &subghz_protocol_came_atomo, - &subghz_protocol_nero_sketch, - &subghz_protocol_ido, - &subghz_protocol_kia, - &subghz_protocol_hormann, - &subghz_protocol_nero_radio, - &subghz_protocol_somfy_telis, - &subghz_protocol_somfy_keytis, - &subghz_protocol_scher_khan, - &subghz_protocol_princeton, - &subghz_protocol_raw, - &subghz_protocol_linear, - &subghz_protocol_secplus_v2, - &subghz_protocol_secplus_v1, - &subghz_protocol_megacode, - &subghz_protocol_holtek, - &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_magellan, - &subghz_protocol_intertechno_v3, - &subghz_protocol_clemsa, - &subghz_protocol_ansonic, - &subghz_protocol_smc5326, - &subghz_protocol_holtek_th12x, - &subghz_protocol_linear_delta3, - &subghz_protocol_dooya, - &subghz_protocol_alutech_at_4n, - &subghz_protocol_kinggates_stylo_4k, - &subghz_protocol_bin_raw, -}; - -const SubGhzProtocolRegistry subghz_protocol_registry = { - .items = subghz_protocol_registry_items, - .size = COUNT_OF(subghz_protocol_registry_items)}; diff --git a/applications/main/subghz/protocols/protocol_items.h b/applications/main/subghz/protocols/protocol_items.h deleted file mode 100644 index b7e082bf5..000000000 --- a/applications/main/subghz/protocols/protocol_items.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once -#include "../registry.h" - -#include "princeton.h" -#include "keeloq.h" -#include "star_line.h" -#include "nice_flo.h" -#include "came.h" -#include "faac_slh.h" -#include "nice_flor_s.h" -#include "came_twee.h" -#include "came_atomo.h" -#include "nero_sketch.h" -#include "ido.h" -#include "kia.h" -#include "hormann.h" -#include "nero_radio.h" -#include "somfy_telis.h" -#include "somfy_keytis.h" -#include "scher_khan.h" -#include "gate_tx.h" -#include "raw.h" -#include "linear.h" -#include "linear_delta3.h" -#include "secplus_v2.h" -#include "secplus_v1.h" -#include "megacode.h" -#include "holtek.h" -#include "chamberlain_code.h" -#include "power_smart.h" -#include "marantec.h" -#include "bett.h" -#include "doitrand.h" -#include "phoenix_v2.h" -#include "honeywell_wdb.h" -#include "magellan.h" -#include "intertechno_v3.h" -#include "clemsa.h" -#include "ansonic.h" -#include "smc5326.h" -#include "holtek_ht12x.h" -#include "dooya.h" -#include "alutech_at_4n.h" -#include "kinggates_stylo_4k.h" -#include "bin_raw.h" - -#ifdef __cplusplus -extern "C" { -#endif - -extern const SubGhzProtocolRegistry subghz_protocol_registry; - -#ifdef __cplusplus -} -#endif \ No newline at end of file diff --git a/applications/main/subghz/protocols/raw.c b/applications/main/subghz/protocols/raw.c deleted file mode 100644 index 01a229047..000000000 --- a/applications/main/subghz/protocols/raw.c +++ /dev/null @@ -1,374 +0,0 @@ -#include "raw.h" -#include -#include "../subghz_file_encoder_worker.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#include -#include - -#define TAG "SubGhzProtocolRAW" -#define SUBGHZ_DOWNLOAD_MAX_SIZE 512 - -static const SubGhzBlockConst subghz_protocol_raw_const = { - .te_short = 50, - .te_long = 32700, - .te_delta = 0, - .min_count_bit_for_found = 0, -}; - -struct SubGhzProtocolDecoderRAW { - SubGhzProtocolDecoderBase base; - - int32_t* upload_raw; - uint16_t ind_write; - Storage* storage; - FlipperFormat* flipper_file; - uint32_t file_is_open; - FuriString* file_name; - size_t sample_write; - bool last_level; - bool pause; -}; - -struct SubGhzProtocolEncoderRAW { - SubGhzProtocolEncoderBase base; - - bool is_running; - FuriString* file_name; - SubGhzFileEncoderWorker* file_worker_encoder; -}; - -typedef enum { - RAWFileIsOpenClose = 0, - RAWFileIsOpenWrite, - RAWFileIsOpenRead, -} RAWFilIsOpen; - -const SubGhzProtocolDecoder subghz_protocol_raw_decoder = { - .alloc = subghz_protocol_decoder_raw_alloc, - .free = subghz_protocol_decoder_raw_free, - - .feed = subghz_protocol_decoder_raw_feed, - .reset = subghz_protocol_decoder_raw_reset, - - .get_hash_data = NULL, - .serialize = NULL, - .deserialize = subghz_protocol_decoder_raw_deserialize, - .get_string = subghz_protocol_decoder_raw_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_raw_encoder = { - .alloc = subghz_protocol_encoder_raw_alloc, - .free = subghz_protocol_encoder_raw_free, - - .deserialize = subghz_protocol_encoder_raw_deserialize, - .stop = subghz_protocol_encoder_raw_stop, - .yield = subghz_protocol_encoder_raw_yield, -}; - -const SubGhzProtocol subghz_protocol_raw = { - .name = SUBGHZ_PROTOCOL_RAW_NAME, - .type = SubGhzProtocolTypeRAW, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_RAW | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_raw_decoder, - .encoder = &subghz_protocol_raw_encoder, -}; - -bool subghz_protocol_raw_save_to_file_init( - SubGhzProtocolDecoderRAW* instance, - const char* dev_name, - SubGhzRadioPreset* preset) { - furi_assert(instance); - - instance->storage = furi_record_open(RECORD_STORAGE); - instance->flipper_file = flipper_format_file_alloc(instance->storage); - - FuriString* temp_str; - temp_str = furi_string_alloc(); - bool init = false; - - do { - // Create subghz folder directory if necessary - if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { - break; - } - // Create saved directory if necessary - if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) { - break; - } - - furi_string_set(instance->file_name, dev_name); - // First remove subghz device file if it was saved - furi_string_printf(temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_EXTENSION); - - if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) { - break; - } - - // Open file - if(!flipper_format_file_open_always( - instance->flipper_file, furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to open file for write: %s", furi_string_get_cstr(temp_str)); - break; - } - - if(!flipper_format_write_header_cstr( - instance->flipper_file, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - - if(!flipper_format_write_uint32( - instance->flipper_file, "Frequency", &preset->frequency, 1)) { - FURI_LOG_E(TAG, "Unable to add Frequency"); - break; - } - - subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str); - if(!flipper_format_write_string_cstr( - instance->flipper_file, "Preset", furi_string_get_cstr(temp_str))) { - FURI_LOG_E(TAG, "Unable to add Preset"); - break; - } - if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) { - if(!flipper_format_write_string_cstr( - instance->flipper_file, "Custom_preset_module", "CC1101")) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_module"); - break; - } - if(!flipper_format_write_hex( - instance->flipper_file, "Custom_preset_data", preset->data, preset->data_size)) { - FURI_LOG_E(TAG, "Unable to add Custom_preset_data"); - break; - } - } - if(!flipper_format_write_string_cstr( - instance->flipper_file, "Protocol", instance->base.protocol->name)) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - - instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t)); - instance->file_is_open = RAWFileIsOpenWrite; - instance->sample_write = 0; - instance->last_level = false; - instance->pause = false; - init = true; - } while(0); - - furi_string_free(temp_str); - - return init; -} - -static bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolDecoderRAW* instance) { - furi_assert(instance); - - bool is_write = false; - if(instance->file_is_open == RAWFileIsOpenWrite) { - if(!flipper_format_write_int32( - instance->flipper_file, "RAW_Data", instance->upload_raw, instance->ind_write)) { - FURI_LOG_E(TAG, "Unable to add RAW_Data"); - } else { - instance->sample_write += instance->ind_write; - instance->ind_write = 0; - is_write = true; - } - } - return is_write; -} - -void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) { - furi_assert(instance); - - if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write) - subghz_protocol_raw_save_to_file_write(instance); - if(instance->file_is_open != RAWFileIsOpenClose) { - free(instance->upload_raw); - instance->upload_raw = NULL; - flipper_format_file_close(instance->flipper_file); - flipper_format_free(instance->flipper_file); - furi_record_close(RECORD_STORAGE); - } - - instance->file_is_open = RAWFileIsOpenClose; -} - -void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) { - furi_assert(instance); - - if(instance->pause != pause) { - instance->pause = pause; - } -} - -size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) { - return instance->sample_write + instance->ind_write; -} - -void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderRAW* instance = malloc(sizeof(SubGhzProtocolDecoderRAW)); - instance->base.protocol = &subghz_protocol_raw; - instance->upload_raw = NULL; - instance->ind_write = 0; - instance->last_level = false; - instance->file_is_open = RAWFileIsOpenClose; - instance->file_name = furi_string_alloc(); - - return instance; -} - -void subghz_protocol_decoder_raw_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - furi_string_free(instance->file_name); - free(instance); -} - -void subghz_protocol_decoder_raw_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - instance->ind_write = 0; - instance->last_level = false; -} - -void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderRAW* instance = context; - - if(!instance->pause && (instance->upload_raw != NULL)) { - if(duration > subghz_protocol_raw_const.te_short) { - if(instance->last_level != level) { - instance->last_level = (level ? true : false); - instance->upload_raw[instance->ind_write++] = (level ? duration : -duration); - } - } - - if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) { - subghz_protocol_raw_save_to_file_write(instance); - } - } -} - -bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - UNUSED(context); - UNUSED(flipper_format); - //ToDo stub, for backwards compatibility - return true; -} - -void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) { - furi_assert(context); - //SubGhzProtocolDecoderRAW* instance = context; - UNUSED(context); - //ToDo no use - furi_string_cat_printf(output, "RAW Data"); -} - -void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW)); - - instance->base.protocol = &subghz_protocol_raw; - instance->file_name = furi_string_alloc(); - instance->is_running = false; - return instance; -} - -void subghz_protocol_encoder_raw_stop(void* context) { - SubGhzProtocolEncoderRAW* instance = context; - instance->is_running = false; - if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) { - subghz_file_encoder_worker_stop(instance->file_worker_encoder); - subghz_file_encoder_worker_free(instance->file_worker_encoder); - } -} - -void subghz_protocol_encoder_raw_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderRAW* instance = context; - subghz_protocol_encoder_raw_stop(instance); - furi_string_free(instance->file_name); - free(instance); -} - -void subghz_protocol_raw_file_encoder_worker_set_callback_end( - SubGhzProtocolEncoderRAW* instance, - SubGhzProtocolEncoderRAWCallbackEnd callback_end, - void* context_end) { - furi_assert(instance); - furi_assert(callback_end); - subghz_file_encoder_worker_callback_end( - instance->file_worker_encoder, callback_end, context_end); -} - -static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* instance) { - furi_assert(instance); - - instance->file_worker_encoder = subghz_file_encoder_worker_alloc(); - if(subghz_file_encoder_worker_start( - instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) { - //the worker needs a file in order to open and read part of the file - furi_delay_ms(100); - instance->is_running = true; - } else { - subghz_protocol_encoder_raw_stop(instance); - } - return instance->is_running; -} - -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) { - do { - stream_clean(flipper_format_get_raw_stream(flipper_format)); - if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) { - FURI_LOG_E(TAG, "Unable to add Protocol"); - break; - } - - if(!flipper_format_write_string_cstr(flipper_format, "File_name", file_path)) { - FURI_LOG_E(TAG, "Unable to add File_name"); - break; - } - } while(false); -} - -bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderRAW* instance = context; - bool res = false; - FuriString* temp_str; - temp_str = furi_string_alloc(); - do { - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - - if(!flipper_format_read_string(flipper_format, "File_name", temp_str)) { - FURI_LOG_E(TAG, "Missing File_name"); - break; - } - furi_string_set(instance->file_name, temp_str); - - res = subghz_protocol_encoder_raw_worker_init(instance); - } while(false); - furi_string_free(temp_str); - return res; -} - -LevelDuration subghz_protocol_encoder_raw_yield(void* context) { - SubGhzProtocolEncoderRAW* instance = context; - - if(!instance->is_running) return level_duration_reset(); - return subghz_file_encoder_worker_get_level_duration(instance->file_worker_encoder); -} diff --git a/applications/main/subghz/protocols/raw.h b/applications/main/subghz/protocols/raw.h deleted file mode 100644 index 44c7faec5..000000000 --- a/applications/main/subghz/protocols/raw.h +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_RAW_NAME "RAW" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context); - -typedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW; -typedef struct SubGhzProtocolEncoderRAW SubGhzProtocolEncoderRAW; - -extern const SubGhzProtocolDecoder subghz_protocol_raw_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_raw_encoder; -extern const SubGhzProtocol subghz_protocol_raw; - -/** - * Open file for writing - * @param instance Pointer to a SubGhzProtocolDecoderRAW instance - * @param dev_name File name - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_raw_save_to_file_init( - SubGhzProtocolDecoderRAW* instance, - const char* dev_name, - SubGhzRadioPreset* preset); - -/** - * Stop writing file to flash - * @param instance Pointer to a SubGhzProtocolDecoderRAW instance - */ -void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance); - -/** - * Get the number of samples received SubGhzProtocolDecoderRAW. - * @param instance Pointer to a SubGhzProtocolDecoderRAW instance - * @return count of samples - */ -size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance); - -/** - * Allocate SubGhzProtocolDecoderRAW. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderRAW* pointer to a SubGhzProtocolDecoderRAW instance - */ -void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - */ -void subghz_protocol_decoder_raw_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - */ -void subghz_protocol_decoder_raw_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration); - -/** - * Deserialize data SubGhzProtocolDecoderRAW. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderRAW instance - * @param output Resulting text - */ -void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output); - -/** - * Allocate SubGhzProtocolEncoderRAW. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderRAW* pointer to a SubGhzProtocolEncoderRAW instance - */ -void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderRAW. - * @param context Pointer to a SubGhzProtocolEncoderRAW instance - */ -void subghz_protocol_encoder_raw_free(void* context); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderRAW instance - */ -void subghz_protocol_encoder_raw_stop(void* context); - -/** - * pause writing to flash. - * @param context Pointer to a SubGhzProtocolEncoderRAW instance - * @param pause pause writing - */ -void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause); - -/** - * Set callback on completion of file transfer. - * @param instance Pointer to a SubGhzProtocolEncoderRAW instance - * @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd - * @param context_end Context - */ -void subghz_protocol_raw_file_encoder_worker_set_callback_end( - SubGhzProtocolEncoderRAW* instance, - SubGhzProtocolEncoderRAWCallbackEnd callback_end, - void* context_end); - -/** - * File generation for RAW work. - * @param flipper_format Pointer to a FlipperFormat instance - * @param file_path File path - */ -void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderRAW instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderRAW instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_raw_yield(void* context); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/protocols/scher_khan.c b/applications/main/subghz/protocols/scher_khan.c deleted file mode 100644 index a5de7d04b..000000000 --- a/applications/main/subghz/protocols/scher_khan.c +++ /dev/null @@ -1,288 +0,0 @@ -#include "scher_khan.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -//https://phreakerclub.com/72 -//https://phreakerclub.com/forum/showthread.php?t=7&page=2 -//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar -//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5 - -#define TAG "SubGhzProtocolScherKhan" - -static const SubGhzBlockConst subghz_protocol_scher_khan_const = { - .te_short = 750, - .te_long = 1100, - .te_delta = 150, - .min_count_bit_for_found = 35, -}; - -struct SubGhzProtocolDecoderScherKhan { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - const char* protocol_name; -}; - -struct SubGhzProtocolEncoderScherKhan { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - ScherKhanDecoderStepReset = 0, - ScherKhanDecoderStepCheckPreambula, - ScherKhanDecoderStepSaveDuration, - ScherKhanDecoderStepCheckDuration, -} ScherKhanDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = { - .alloc = subghz_protocol_decoder_scher_khan_alloc, - .free = subghz_protocol_decoder_scher_khan_free, - - .feed = subghz_protocol_decoder_scher_khan_feed, - .reset = subghz_protocol_decoder_scher_khan_reset, - - .get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data, - .serialize = subghz_protocol_decoder_scher_khan_serialize, - .deserialize = subghz_protocol_decoder_scher_khan_deserialize, - .get_string = subghz_protocol_decoder_scher_khan_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = { - .alloc = NULL, - .free = NULL, - - .deserialize = NULL, - .stop = NULL, - .yield = NULL, -}; - -const SubGhzProtocol subghz_protocol_scher_khan = { - .name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Save, - - .decoder = &subghz_protocol_scher_khan_decoder, - .encoder = &subghz_protocol_scher_khan_encoder, -}; - -void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan)); - instance->base.protocol = &subghz_protocol_scher_khan; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_scher_khan_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - free(instance); -} - -void subghz_protocol_decoder_scher_khan_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - instance->decoder.parser_step = ScherKhanDecoderStepReset; -} - -void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - - switch(instance->decoder.parser_step) { - case ScherKhanDecoderStepReset: - if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < - subghz_protocol_scher_khan_const.te_delta)) { - instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula; - instance->decoder.te_last = duration; - instance->header_count = 0; - } - break; - case ScherKhanDecoderStepCheckPreambula: - if(level) { - if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < - subghz_protocol_scher_khan_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < - subghz_protocol_scher_khan_const.te_delta)) { - instance->decoder.te_last = duration; - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - } else if( - (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) < - subghz_protocol_scher_khan_const.te_delta) || - (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < - subghz_protocol_scher_khan_const.te_delta)) { - if(DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) < - subghz_protocol_scher_khan_const.te_delta) { - // Found header - instance->header_count++; - break; - } else if( - DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < - subghz_protocol_scher_khan_const.te_delta) { - // Found start bit - if(instance->header_count >= 2) { - instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 1; - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - break; - case ScherKhanDecoderStepSaveDuration: - if(level) { - if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL + - subghz_protocol_scher_khan_const.te_long)) { - //Found stop bit - instance->decoder.parser_step = ScherKhanDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_scher_khan_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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - break; - case ScherKhanDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) < - subghz_protocol_scher_khan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) < - subghz_protocol_scher_khan_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) < - subghz_protocol_scher_khan_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) < - subghz_protocol_scher_khan_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - } else { - instance->decoder.parser_step = ScherKhanDecoderStepReset; - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param protocol_name - */ -static void subghz_protocol_scher_khan_check_remote_controller( - SubGhzBlockGeneric* instance, - const char** protocol_name) { - /* - * MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic - * 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100 - * 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101 - * 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110 - * 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111 - * Serial Key Ser ~Key CNT - */ - - switch(instance->data_count_bit) { - // case 35: //MAGIC CODE, Static - // instance->protocol_name = "MAGIC CODE, Static"; - // break; - case 51: //MAGIC CODE, Dynamic - *protocol_name = "MAGIC CODE, Dynamic"; - instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F); - instance->btn = (instance->data >> 24) & 0x0F; - instance->cnt = instance->data & 0xFFFF; - break; - // case 57: //MAGIC CODE PRO / PRO2 - // instance->protocol_name = "MAGIC CODE PRO / PRO2"; - // break; - - default: - *protocol_name = "Unknown"; - instance->serial = 0; - instance->btn = 0; - instance->cnt = 0; - break; - } -} - -uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_scher_khan_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - return subghz_block_generic_deserialize(&instance->generic, flipper_format); -} - -void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderScherKhan* instance = context; - - subghz_protocol_scher_khan_check_remote_controller( - &instance->generic, &instance->protocol_name); - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:%07lX Btn:%X Cnt:%04lX\r\n" - "Pt: %s\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt, - instance->protocol_name); -} diff --git a/applications/main/subghz/protocols/scher_khan.h b/applications/main/subghz/protocols/scher_khan.h deleted file mode 100644 index b7e84ea1f..000000000 --- a/applications/main/subghz/protocols/scher_khan.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME "Scher-Khan" - -typedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan; -typedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan; - -extern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder; -extern const SubGhzProtocol subghz_protocol_scher_khan; - -/** - * Allocate SubGhzProtocolDecoderScherKhan. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance - */ -void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderScherKhan. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - */ -void subghz_protocol_decoder_scher_khan_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderScherKhan. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - */ -void subghz_protocol_decoder_scher_khan_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderScherKhan. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_scher_khan_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderScherKhan. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderScherKhan instance - * @param output Resulting text - */ -void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v1.c b/applications/main/subghz/protocols/secplus_v1.c deleted file mode 100644 index 3ef95db36..000000000 --- a/applications/main/subghz/protocols/secplus_v1.c +++ /dev/null @@ -1,634 +0,0 @@ -#include "secplus_v1.h" -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* -* Help -* https://github.com/argilo/secplus -* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c -*/ - -#define TAG "SubGhzProtocoSecPlus_v1" - -#define SECPLUS_V1_BIT_ERR -1 //0b0000 -#define SECPLUS_V1_BIT_0 0 //0b0001 -#define SECPLUS_V1_BIT_1 1 //0b0011 -#define SECPLUS_V1_BIT_2 2 //0b0111 - -#define SECPLUS_V1_PACKET_1_HEADER 0x00 -#define SECPLUS_V1_PACKET_2_HEADER 0x02 -#define SECPLUS_V1_PACKET_1_INDEX_BASE 0 -#define SECPLUS_V1_PACKET_2_INDEX_BASE 21 -#define SECPLUS_V1_PACKET_1_ACCEPTED (1 << 0) -#define SECPLUS_V1_PACKET_2_ACCEPTED (1 << 1) - -static const SubGhzBlockConst subghz_protocol_secplus_v1_const = { - .te_short = 500, - .te_long = 1500, - .te_delta = 100, - .min_count_bit_for_found = 21, -}; - -struct SubGhzProtocolDecoderSecPlus_v1 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint8_t packet_accepted; - uint8_t base_packet_index; - uint8_t data_array[44]; -}; - -struct SubGhzProtocolEncoderSecPlus_v1 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint8_t data_array[44]; -}; - -typedef enum { - SecPlus_v1DecoderStepReset = 0, - SecPlus_v1DecoderStepSearchStartBit, - SecPlus_v1DecoderStepSaveDuration, - SecPlus_v1DecoderStepDecoderData, -} SecPlus_v1DecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = { - .alloc = subghz_protocol_decoder_secplus_v1_alloc, - .free = subghz_protocol_decoder_secplus_v1_free, - - .feed = subghz_protocol_decoder_secplus_v1_feed, - .reset = subghz_protocol_decoder_secplus_v1_reset, - - .get_hash_data = subghz_protocol_decoder_secplus_v1_get_hash_data, - .serialize = subghz_protocol_decoder_secplus_v1_serialize, - .deserialize = subghz_protocol_decoder_secplus_v1_deserialize, - .get_string = subghz_protocol_decoder_secplus_v1_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = { - .alloc = subghz_protocol_encoder_secplus_v1_alloc, - .free = subghz_protocol_encoder_secplus_v1_free, - - .deserialize = subghz_protocol_encoder_secplus_v1_deserialize, - .stop = subghz_protocol_encoder_secplus_v1_stop, - .yield = subghz_protocol_encoder_secplus_v1_yield, -}; - -const SubGhzProtocol subghz_protocol_secplus_v1 = { - .name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Save, - - .decoder = &subghz_protocol_secplus_v1_decoder, - .encoder = &subghz_protocol_secplus_v1_encoder, -}; - -void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1)); - - instance->base.protocol = &subghz_protocol_secplus_v1; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_secplus_v1_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderSecPlus_v1* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - * @return true On success - */ -static bool - subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* 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, "Encoder size upload exceeds allocated encoder buffer."); - return false; - } else { - instance->encoder.size_upload = size_upload; - } - - //Send header packet 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3)); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); - - //Send data packet 1 - for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21; - i++) { - switch(instance->data_array[i]) { - case SECPLUS_V1_BIT_0: - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); - break; - case SECPLUS_V1_BIT_1: - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); - break; - case SECPLUS_V1_BIT_2: - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); - break; - - default: - FURI_LOG_E(TAG, "Encoder error, wrong bit type"); - return false; - break; - } - } - - //Send header packet 2 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116)); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); - - //Send data packet 2 - for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21; - i++) { - switch(instance->data_array[i]) { - case SECPLUS_V1_BIT_0: - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short); - break; - case SECPLUS_V1_BIT_1: - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2); - break; - case SECPLUS_V1_BIT_2: - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3); - break; - - default: - FURI_LOG_E(TAG, "Encoder error, wrong bit type."); - return false; - break; - } - } - - return true; -} - -/** - * Security+ 1.0 message encoding - * @param instance SubGhzProtocolEncoderSecPlus_v1* - */ - -static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) { - uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; - uint32_t rolling = instance->generic.data & 0xFFFFFFFF; - - uint8_t rolling_array[20] = {0}; - uint8_t fixed_array[20] = {0}; - uint32_t acc = 0; - - //increment the counter - rolling += 2; - - //update data - instance->generic.data &= 0xFFFFFFFF00000000; - instance->generic.data |= rolling; - - if(rolling == 0xFFFFFFFF) { - rolling = 0xE6000000; - } - if(fixed > 0xCFD41B90) { - FURI_LOG_E("TAG", "Encode wrong fixed data"); - return false; - } - - rolling = subghz_protocol_blocks_reverse_key(rolling, 32); - - for(int i = 19; i > -1; i--) { - rolling_array[i] = rolling % 3; - rolling /= 3; - fixed_array[i] = fixed % 3; - fixed /= 3; - } - - instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER; - instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER; - - //encode packet 1 - for(uint8_t i = 1; i < 11; i++) { - acc += rolling_array[i - 1]; - instance->data_array[i * 2 - 1] = rolling_array[i - 1]; - acc += fixed_array[i - 1]; - instance->data_array[i * 2] = acc % 3; - } - - acc = 0; - //encode packet 2 - for(uint8_t i = 11; i < 21; i++) { - acc += rolling_array[i - 1]; - instance->data_array[i * 2] = rolling_array[i - 1]; - acc += fixed_array[i - 1]; - instance->data_array[i * 2 + 1] = acc % 3; - } - - return true; -} - -bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderSecPlus_v1* 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 != - 2 * subghz_protocol_secplus_v1_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); - - if(!subghz_protocol_secplus_v1_encode(instance)) { - break; - } - if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) { - break; - } - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_secplus_v1_stop(void* context) { - SubGhzProtocolEncoderSecPlus_v1* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) { - SubGhzProtocolEncoderSecPlus_v1* 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_secplus_v1_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1)); - instance->base.protocol = &subghz_protocol_secplus_v1; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_secplus_v1_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - free(instance); -} - -void subghz_protocol_decoder_secplus_v1_reset(void* context) { - furi_assert(context); - // SubGhzProtocolDecoderSecPlus_v1* instance = context; - // does not reset the decoder because you need to get 2 parts of the package -} - -/** - * Security+ 1.0 message decoding - * @param instance SubGhzProtocolDecoderSecPlus_v1* - */ - -static void subghz_protocol_secplus_v1_decode(SubGhzProtocolDecoderSecPlus_v1* instance) { - uint32_t rolling = 0; - uint32_t fixed = 0; - uint32_t acc = 0; - uint8_t digit = 0; - - //decode packet 1 - for(uint8_t i = 1; i < 21; i += 2) { - digit = instance->data_array[i]; - rolling = (rolling * 3) + digit; - acc += digit; - - digit = (60 + instance->data_array[i + 1] - acc) % 3; - fixed = (fixed * 3) + digit; - acc += digit; - } - - acc = 0; - //decode packet 2 - for(uint8_t i = 22; i < 42; i += 2) { - digit = instance->data_array[i]; - rolling = (rolling * 3) + digit; - acc += digit; - - digit = (60 + instance->data_array[i + 1] - acc) % 3; - fixed = (fixed * 3) + digit; - acc += digit; - } - - rolling = subghz_protocol_blocks_reverse_key(rolling, 32); - instance->generic.data = (uint64_t)fixed << 32 | rolling; - - instance->generic.data_count_bit = - subghz_protocol_secplus_v1_const.min_count_bit_for_found * 2; -} - -void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - - switch(instance->decoder.parser_step) { - case SecPlus_v1DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < - subghz_protocol_secplus_v1_const.te_delta * 120)) { - //Found header Security+ 1.0 - instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->packet_accepted = 0; - memset(instance->data_array, 0, sizeof(instance->data_array)); - } - break; - case SecPlus_v1DecoderStepSearchStartBit: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < - subghz_protocol_secplus_v1_const.te_delta) { - instance->base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE; - instance - ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = - SECPLUS_V1_BIT_0; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; - } else if( - DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_long) < - subghz_protocol_secplus_v1_const.te_delta) { - instance->base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE; - instance - ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = - SECPLUS_V1_BIT_2; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - } else { - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - break; - case SecPlus_v1DecoderStepSaveDuration: - if(!level) { //save interval - if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) < - subghz_protocol_secplus_v1_const.te_delta * 120) { - if(instance->decoder.decode_count_bit == - subghz_protocol_secplus_v1_const.min_count_bit_for_found) { - if(instance->base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE) - instance->packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED; - if(instance->base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE) - instance->packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED; - - if(instance->packet_accepted == - (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) { - subghz_protocol_secplus_v1_decode(instance); - - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - } - instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = SecPlus_v1DecoderStepDecoderData; - } - } else { - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - break; - case SecPlus_v1DecoderStepDecoderData: - if(level && (instance->decoder.decode_count_bit <= - subghz_protocol_secplus_v1_const.min_count_bit_for_found)) { - if((DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 3) < - subghz_protocol_secplus_v1_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) < - subghz_protocol_secplus_v1_const.te_delta)) { - instance - ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = - SECPLUS_V1_BIT_0; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 2) < - subghz_protocol_secplus_v1_const.te_delta * 2) && - (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 2) < - subghz_protocol_secplus_v1_const.te_delta * 2)) { - instance - ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = - SECPLUS_V1_BIT_1; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; - } else if( - (DURATION_DIFF( - instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short) < - subghz_protocol_secplus_v1_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 3) < - subghz_protocol_secplus_v1_const.te_delta * 3)) { - instance - ->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] = - SECPLUS_V1_BIT_2; - instance->decoder.decode_count_bit++; - instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - } else { - instance->decoder.parser_step = SecPlus_v1DecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_secplus_v1_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - 2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - ret = true; - } while(false); - return ret; -} - -bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) { - //uint8_t id0 = (fixed / 3) % 3; - uint8_t id1 = (fixed / 9) % 3; - uint8_t btn = fixed % 3; - - do { - if(id1 == 0) return false; - if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560 - } while(false); - return true; -} - -void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v1* instance = context; - - uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF; - instance->generic.cnt = instance->generic.data & 0xFFFFFFFF; - - instance->generic.btn = fixed % 3; - uint8_t id0 = (fixed / 3) % 3; - uint8_t id1 = (fixed / 9) % 3; - uint16_t pin = 0; - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "id1:%d id0:%d", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - id1, - id0); - - if(id1 == 0) { - // (fixed // 3**3) % (3**7) 3^3=27 3^73=72187 - - instance->generic.serial = (fixed / 27) % 2187; - // pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683 - pin = (fixed / 59049) % 19683; - - if(pin <= 9999) { - furi_string_cat_printf(output, " pin:%d", pin); - } else if(pin <= 11029) { - furi_string_cat_printf(output, " pin:enter"); - } - - int pin_suffix = 0; - // pin_suffix = (fixed // 3**19) % 3 3^19=1162261467 - pin_suffix = (fixed / 1162261467) % 3; - - if(pin_suffix == 1) { - furi_string_cat_printf(output, " #\r\n"); - } else if(pin_suffix == 2) { - furi_string_cat_printf(output, " *\r\n"); - } else { - furi_string_cat_printf(output, "\r\n"); - } - furi_string_cat_printf( - output, - "Sn:0x%08lX\r\n" - "Cnt:0x%03lX " - "Sw_id:0x%X\r\n", - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); - } else { - //id = fixed / 27; - instance->generic.serial = fixed / 27; - if(instance->generic.btn == 1) { - furi_string_cat_printf(output, " Btn:left\r\n"); - } else if(instance->generic.btn == 0) { - furi_string_cat_printf(output, " Btn:middle\r\n"); - } else if(instance->generic.btn == 2) { //-V547 - furi_string_cat_printf(output, " Btn:right\r\n"); - } - - furi_string_cat_printf( - output, - "Sn:0x%08lX\r\n" - "Cnt:0x%03lX " - "Sw_id:0x%X\r\n", - instance->generic.serial, - instance->generic.cnt, - instance->generic.btn); - } -} diff --git a/applications/main/subghz/protocols/secplus_v1.h b/applications/main/subghz/protocols/secplus_v1.h deleted file mode 100644 index 99480b62b..000000000 --- a/applications/main/subghz/protocols/secplus_v1.h +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once -#include "base.h" - -#define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME "Security+ 1.0" - -typedef struct SubGhzProtocolDecoderSecPlus_v1 SubGhzProtocolDecoderSecPlus_v1; -typedef struct SubGhzProtocolEncoderSecPlus_v1 SubGhzProtocolEncoderSecPlus_v1; - -extern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder; -extern const SubGhzProtocol subghz_protocol_secplus_v1; - -/** - * Allocate SubGhzProtocolEncoderSecPlus_v1. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - */ -void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderSecPlus_v1. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - */ -void subghz_protocol_encoder_secplus_v1_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - */ -void subghz_protocol_encoder_secplus_v1_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderSecPlus_v1. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderSecPlus_v1* pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - */ -void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderSecPlus_v1. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - */ -void subghz_protocol_decoder_secplus_v1_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderSecPlus_v1. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - */ -void subghz_protocol_decoder_secplus_v1_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderSecPlus_v1. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_secplus_v1_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderSecPlus_v1. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1. - * @param fixed fixed parts - * @return true On success - */ -bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/secplus_v2.c b/applications/main/subghz/protocols/secplus_v2.c deleted file mode 100644 index bcef90dad..000000000 --- a/applications/main/subghz/protocols/secplus_v2.c +++ /dev/null @@ -1,833 +0,0 @@ -#include "secplus_v2.h" -#include -#include -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* -* Help -* https://github.com/argilo/secplus -* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c -*/ - -#define TAG "SubGhzProtocoSecPlus_v2" - -#define SECPLUS_V2_HEADER 0x3C0000000000 -#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000 -#define SECPLUS_V2_PACKET_1 0x000000000000 -#define SECPLUS_V2_PACKET_2 0x010000000000 -#define SECPLUS_V2_PACKET_MASK 0x30000000000 - -static const SubGhzBlockConst subghz_protocol_secplus_v2_const = { - .te_short = 250, - .te_long = 500, - .te_delta = 110, - .min_count_bit_for_found = 62, -}; - -struct SubGhzProtocolDecoderSecPlus_v2 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - ManchesterState manchester_saved_state; - uint64_t secplus_packet_1; -}; - -struct SubGhzProtocolEncoderSecPlus_v2 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - uint64_t secplus_packet_1; -}; - -typedef enum { - SecPlus_v2DecoderStepReset = 0, - SecPlus_v2DecoderStepDecoderData, -} SecPlus_v2DecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = { - .alloc = subghz_protocol_decoder_secplus_v2_alloc, - .free = subghz_protocol_decoder_secplus_v2_free, - - .feed = subghz_protocol_decoder_secplus_v2_feed, - .reset = subghz_protocol_decoder_secplus_v2_reset, - - .get_hash_data = subghz_protocol_decoder_secplus_v2_get_hash_data, - .serialize = subghz_protocol_decoder_secplus_v2_serialize, - .deserialize = subghz_protocol_decoder_secplus_v2_deserialize, - .get_string = subghz_protocol_decoder_secplus_v2_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = { - .alloc = subghz_protocol_encoder_secplus_v2_alloc, - .free = subghz_protocol_encoder_secplus_v2_free, - - .deserialize = subghz_protocol_encoder_secplus_v2_deserialize, - .stop = subghz_protocol_encoder_secplus_v2_stop, - .yield = subghz_protocol_encoder_secplus_v2_yield, -}; - -const SubGhzProtocol subghz_protocol_secplus_v2 = { - .name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_secplus_v2_decoder, - .encoder = &subghz_protocol_secplus_v2_encoder, -}; - -void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2)); - - instance->base.protocol = &subghz_protocol_secplus_v2; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_secplus_v2_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderSecPlus_v2* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) { - // selectively invert buffers - switch(invert) { - case 0x00: // 0b0000 (True, True, False), - p[0] = ~p[0] & 0x03FF; - p[1] = ~p[1] & 0x03FF; - break; - case 0x01: // 0b0001 (False, True, False), - p[1] = ~p[1] & 0x03FF; - break; - case 0x02: // 0b0010 (False, False, True), - p[2] = ~p[2] & 0x03FF; - break; - case 0x04: // 0b0100 (True, True, True), - p[0] = ~p[0] & 0x03FF; - p[1] = ~p[1] & 0x03FF; - p[2] = ~p[2] & 0x03FF; - break; - case 0x05: // 0b0101 (True, False, True), - case 0x0a: // 0b1010 (True, False, True), - p[0] = ~p[0] & 0x03FF; - p[2] = ~p[2] & 0x03FF; - break; - case 0x06: // 0b0110 (False, True, True), - p[1] = ~p[1] & 0x03FF; - p[2] = ~p[2] & 0x03FF; - break; - case 0x08: // 0b1000 (True, False, False), - p[0] = ~p[0] & 0x03FF; - break; - case 0x09: // 0b1001 (False, False, False), - break; - default: - FURI_LOG_E(TAG, "Invert FAIL"); - return false; - } - return true; -} - -static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) { - uint16_t a = p[0], b = p[1], c = p[2]; - - // selectively reorder buffers - switch(order) { - case 0x06: // 0b0110 2, 1, 0], - case 0x09: // 0b1001 2, 1, 0], - p[2] = a; - // p[1]: no change - p[0] = c; - break; - case 0x08: // 0b1000 1, 2, 0], - case 0x04: // 0b0100 1, 2, 0], - p[1] = a; - p[2] = b; - p[0] = c; - break; - case 0x01: // 0b0001 2, 0, 1], - p[2] = a; - p[0] = b; - p[1] = c; - break; - case 0x00: // 0b0000 0, 2, 1], - // p[0]: no change - p[2] = b; - p[1] = c; - break; - case 0x05: // 0b0101 1, 0, 2], - p[1] = a; - p[0] = b; - // p[2]: no change - break; - case 0x02: // 0b0010 0, 1, 2], - case 0x0A: // 0b1010 0, 1, 2], - // no reordering - break; - default: - FURI_LOG_E(TAG, "Order FAIL"); - return false; - } - return true; -} - -static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) { - uint16_t a, b, c; - - // selectively reorder buffers - switch(order) { - case 0x06: // 0b0110 2, 1, 0], - case 0x09: // 0b1001 2, 1, 0], - a = p[2]; - b = p[1]; - c = p[0]; - break; - case 0x08: // 0b1000 1, 2, 0], - case 0x04: // 0b0100 1, 2, 0], - a = p[1]; - b = p[2]; - c = p[0]; - break; - case 0x01: // 0b0001 2, 0, 1], - a = p[2]; - b = p[0]; - c = p[1]; - break; - case 0x00: // 0b0000 0, 2, 1], - a = p[0]; - b = p[2]; - c = p[1]; - break; - case 0x05: // 0b0101 1, 0, 2], - a = p[1]; - b = p[0]; - c = p[2]; - break; - case 0x02: // 0b0010 0, 1, 2], - case 0x0A: // 0b1010 0, 1, 2], - a = p[0]; - b = p[1]; - c = p[2]; - break; - default: - FURI_LOG_E(TAG, "Order FAIL"); - return false; - } - - p[0] = a; - p[1] = b; - p[2] = c; - return true; -} - -/** - * Security+ 2.0 half-message decoding - * @param data data - * @param roll_array[] return roll_array part - * @param fixed[] return fixed part - * @return true On success - */ - -static bool - subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) { - uint8_t order = (data >> 34) & 0x0f; - uint8_t invert = (data >> 30) & 0x0f; - uint16_t p[3] = {0}; - - for(int i = 29; i >= 0; i -= 3) { - p[0] = p[0] << 1 | bit_read(data, i); - p[1] = p[1] << 1 | bit_read(data, i - 1); - p[2] = p[2] << 1 | bit_read(data, i - 2); - } - - if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false; - if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false; - - data = order << 4 | invert; - int k = 0; - for(int i = 6; i >= 0; i -= 2) { - roll_array[k] = (data >> i) & 0x03; - if(roll_array[k++] == 3) { - FURI_LOG_E(TAG, "Roll_Array FAIL"); - return false; - } - } - - for(int i = 8; i >= 0; i -= 2) { - roll_array[k] = (p[2] >> i) & 0x03; - if(roll_array[k++] == 3) { - FURI_LOG_E(TAG, "Roll_Array FAIL"); - return false; - } - } - - fixed[0] = p[0] << 10 | p[1]; - return true; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param packet_1 first part of the message - */ -static void - subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) { - uint32_t fixed_1[1]; - uint8_t roll_1[9] = {0}; - uint32_t fixed_2[1]; - uint8_t roll_2[9] = {0}; - uint8_t rolling_digits[18] = {0}; - - if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) && - subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) { - rolling_digits[0] = roll_2[8]; - rolling_digits[1] = roll_1[8]; - - rolling_digits[2] = roll_2[4]; - rolling_digits[3] = roll_2[5]; - rolling_digits[4] = roll_2[6]; - rolling_digits[5] = roll_2[7]; - - rolling_digits[6] = roll_1[4]; - rolling_digits[7] = roll_1[5]; - rolling_digits[8] = roll_1[6]; - rolling_digits[9] = roll_1[7]; - - rolling_digits[10] = roll_2[0]; - rolling_digits[11] = roll_2[1]; - rolling_digits[12] = roll_2[2]; - rolling_digits[13] = roll_2[3]; - - rolling_digits[14] = roll_1[0]; - rolling_digits[15] = roll_1[1]; - rolling_digits[16] = roll_1[2]; - rolling_digits[17] = roll_1[3]; - - uint32_t rolling = 0; - for(int i = 0; i < 18; i++) { - rolling = (rolling * 3) + rolling_digits[i]; - } - // Max value = 2^28 (268435456) - if(rolling >= 0x10000000) { - FURI_LOG_E(TAG, "Rolling FAIL"); - instance->cnt = 0; - instance->btn = 0; - instance->serial = 0; - } else { - instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28); - instance->btn = fixed_1[0] >> 12; - instance->serial = fixed_1[0] << 20 | fixed_2[0]; - } - } else { - instance->cnt = 0; - instance->btn = 0; - instance->serial = 0; - } -} - -/** - * Security+ 2.0 half-message encoding - * @param roll_array[] roll_array part - * @param fixed[] fixed part - * @return return data - */ - -static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) { - uint64_t data = 0; - uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0}; - uint8_t order = roll_array[0] << 2 | roll_array[1]; - uint8_t invert = roll_array[2] << 2 | roll_array[3]; - p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 | - roll_array[7] << 2 | roll_array[8]; - - if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0; - if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0; - - for(int i = 0; i < 10; i++) { - data <<= 3; - data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i); - } - data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30; - - return data; -} - -/** - * Security+ 2.0 message encoding - * @param instance SubGhzProtocolEncoderSecPlus_v2* - */ - -static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) { - uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20}; - uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF}; - uint8_t rolling_digits[18] = {0}; - uint8_t roll_1[9] = {0}; - uint8_t roll_2[9] = {0}; - - instance->generic.cnt++; - //ToDo it is not known what value the counter starts - if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000; - uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28); - - for(int8_t i = 17; i > -1; i--) { - rolling_digits[i] = rolling % 3; - rolling /= 3; - } - - roll_2[8] = rolling_digits[0]; - roll_1[8] = rolling_digits[1]; - - roll_2[4] = rolling_digits[2]; - roll_2[5] = rolling_digits[3]; - roll_2[6] = rolling_digits[4]; - roll_2[7] = rolling_digits[5]; - - roll_1[4] = rolling_digits[6]; - roll_1[5] = rolling_digits[7]; - roll_1[6] = rolling_digits[8]; - roll_1[7] = rolling_digits[9]; - - roll_2[0] = rolling_digits[10]; - roll_2[1] = rolling_digits[11]; - roll_2[2] = rolling_digits[12]; - roll_2[3] = rolling_digits[13]; - - roll_1[0] = rolling_digits[14]; - roll_1[1] = rolling_digits[15]; - roll_1[2] = rolling_digits[16]; - roll_1[3] = rolling_digits[17]; - - instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 | - subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]); - instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 | - subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]); -} - -static LevelDuration - subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) { - LevelDuration data = {.duration = 0, .level = 0}; - switch(result) { - case ManchesterEncoderResultShortLow: - data.duration = subghz_protocol_secplus_v2_const.te_short; - data.level = false; - break; - case ManchesterEncoderResultLongLow: - data.duration = subghz_protocol_secplus_v2_const.te_long; - data.level = false; - break; - case ManchesterEncoderResultLongHigh: - data.duration = subghz_protocol_secplus_v2_const.te_long; - data.level = true; - break; - case ManchesterEncoderResultShortHigh: - data.duration = subghz_protocol_secplus_v2_const.te_short; - data.level = true; - break; - - default: - furi_crash("SubGhz: ManchesterEncoderResult is incorrect."); - break; - } - return level_duration_make(data.level, data.duration); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - */ -static void - subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) { - furi_assert(instance); - size_t index = 0; - - ManchesterEncoderState enc_state; - manchester_encoder_reset(&enc_state); - ManchesterEncoderResult result; - - //Send data packet 1 - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(!manchester_encoder_advance( - &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, bit_read(instance->secplus_packet_1, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); - } - instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); - - //Send data packet 2 - manchester_encoder_reset(&enc_state); - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(!manchester_encoder_advance( - &enc_state, bit_read(instance->generic.data, i - 1), &result)) { - instance->encoder.upload[index++] = - subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); - manchester_encoder_advance( - &enc_state, bit_read(instance->generic.data, i - 1), &result); - } - instance->encoder.upload[index++] = - subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result); - } - instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload( - manchester_encoder_finish(&enc_state)); - if(level_duration_get_level(instance->encoder.upload[index])) { - index++; - } - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136); - - instance->encoder.size_upload = index; -} - -bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderSecPlus_v2* 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_secplus_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex( - flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Secplus_packet_1"); - break; - } - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; - } - - subghz_protocol_secplus_v2_remote_controller( - &instance->generic, instance->secplus_packet_1); - subghz_protocol_secplus_v2_encode(instance); - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - subghz_protocol_encoder_secplus_v2_get_upload(instance); - - //update data - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> (i * 8)) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; - } - if(!flipper_format_update_hex( - flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_secplus_v2_stop(void* context) { - SubGhzProtocolEncoderSecPlus_v2* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) { - SubGhzProtocolEncoderSecPlus_v2* 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; -} - -bool subghz_protocol_secplus_v2_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint32_t cnt, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderSecPlus_v2* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->generic.btn = btn; - instance->generic.data_count_bit = - (uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found; - subghz_protocol_secplus_v2_encode(instance); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; - } - - if(res && - !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - res = false; - } - return res; -} - -void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2)); - instance->base.protocol = &subghz_protocol_secplus_v2; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_secplus_v2_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* instance = context; - free(instance); -} - -void subghz_protocol_decoder_secplus_v2_reset(void* context) { - furi_assert(context); - // SubGhzProtocolDecoderSecPlus_v2* instance = context; - // does not reset the decoder because you need to get 2 parts of the package -} - -static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) { - if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) { - if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) { - instance->secplus_packet_1 = instance->decoder.decode_data; - } else if( - ((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) && - (instance->secplus_packet_1)) { - return true; - } - } - return false; -} - -void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* instance = context; - - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case SecPlus_v2DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) < - subghz_protocol_secplus_v2_const.te_delta * 100)) { - //Found header Security+ 2.0 - instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->secplus_packet_1 = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } - break; - case SecPlus_v2DecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < - subghz_protocol_secplus_v2_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < - subghz_protocol_secplus_v2_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL + - subghz_protocol_secplus_v2_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_secplus_v2_const.min_count_bit_for_found) { - instance->generic.data = instance->decoder.decode_data; - instance->generic.data_count_bit = instance->decoder.decode_count_bit; - if(subghz_protocol_secplus_v2_check_packet(instance)) { - if(instance->base.callback) - instance->base.callback(&instance->base, instance->base.context); - instance->decoder.parser_step = SecPlus_v2DecoderStepReset; - } - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventShortLow, - &instance->manchester_saved_state, - NULL); - } else { - instance->decoder.parser_step = SecPlus_v2DecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) < - subghz_protocol_secplus_v2_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) < - subghz_protocol_secplus_v2_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = SecPlus_v2DecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_secplus_v2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF; - } - - if(res && - !flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Secplus_packet_1"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* 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_secplus_v2_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - if(!flipper_format_read_hex( - flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Missing Secplus_packet_1"); - break; - } - for(uint8_t i = 0; i < sizeof(uint64_t); i++) { - instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i]; - } - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderSecPlus_v2* instance = context; - subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1); - - furi_string_cat_printf( - output, - "%s %db\r\n" - "Pk1:0x%lX%08lX\r\n" - "Pk2:0x%lX%08lX\r\n" - "Sn:0x%08lX Btn:0x%01X\r\n" - "Cnt:0x%03lX\r\n", - - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->secplus_packet_1 >> 32), - (uint32_t)instance->secplus_packet_1, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); -} diff --git a/applications/main/subghz/protocols/secplus_v2.h b/applications/main/subghz/protocols/secplus_v2.h deleted file mode 100644 index bea8cdb5d..000000000 --- a/applications/main/subghz/protocols/secplus_v2.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once -#include "base.h" - -#define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME "Security+ 2.0" - -typedef struct SubGhzProtocolDecoderSecPlus_v2 SubGhzProtocolDecoderSecPlus_v2; -typedef struct SubGhzProtocolEncoderSecPlus_v2 SubGhzProtocolEncoderSecPlus_v2; - -extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder; -extern const SubGhzProtocol subghz_protocol_secplus_v2; - -/** - * Allocate SubGhzProtocolEncoderSecPlus_v2. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - */ -void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderSecPlus_v2. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - */ -void subghz_protocol_encoder_secplus_v2_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - */ -void subghz_protocol_encoder_secplus_v2_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 32 bit - * @param btn Button number, 8 bit - * @param cnt Container value, 28 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_secplus_v2_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint32_t cnt, - SubGhzRadioPreset* preset); - -/** - * Allocate SubGhzProtocolDecoderSecPlus_v2. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderSecPlus_v2* pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - */ -void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderSecPlus_v2. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - */ -void subghz_protocol_decoder_secplus_v2_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderSecPlus_v2. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - */ -void subghz_protocol_decoder_secplus_v2_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderSecPlus_v2. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_secplus_v2_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderSecPlus_v2. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/smc5326.c b/applications/main/subghz/protocols/smc5326.c deleted file mode 100644 index 9c9b5d4fd..000000000 --- a/applications/main/subghz/protocols/smc5326.c +++ /dev/null @@ -1,387 +0,0 @@ -#include "smc5326.h" - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -/* - * Help - * https://datasheetspdf.com/pdf-file/532079/Aslic/AX5326-4/1 - * - */ - -#define TAG "SubGhzProtocolSMC5326" - -#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_smc5326_const = { - .te_short = 300, - .te_long = 900, - .te_delta = 200, - .min_count_bit_for_found = 25, -}; - -struct SubGhzProtocolDecoderSMC5326 { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint32_t te; - uint32_t last_data; -}; - -struct SubGhzProtocolEncoderSMC5326 { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - uint32_t te; -}; - -typedef enum { - SMC5326DecoderStepReset = 0, - SMC5326DecoderStepSaveDuration, - SMC5326DecoderStepCheckDuration, -} SMC5326DecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder = { - .alloc = subghz_protocol_decoder_smc5326_alloc, - .free = subghz_protocol_decoder_smc5326_free, - - .feed = subghz_protocol_decoder_smc5326_feed, - .reset = subghz_protocol_decoder_smc5326_reset, - - .get_hash_data = subghz_protocol_decoder_smc5326_get_hash_data, - .serialize = subghz_protocol_decoder_smc5326_serialize, - .deserialize = subghz_protocol_decoder_smc5326_deserialize, - .get_string = subghz_protocol_decoder_smc5326_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder = { - .alloc = subghz_protocol_encoder_smc5326_alloc, - .free = subghz_protocol_encoder_smc5326_free, - - .deserialize = subghz_protocol_encoder_smc5326_deserialize, - .stop = subghz_protocol_encoder_smc5326_stop, - .yield = subghz_protocol_encoder_smc5326_yield, -}; - -const SubGhzProtocol subghz_protocol_smc5326 = { - .name = SUBGHZ_PROTOCOL_SMC5326_NAME, - .type = SubGhzProtocolTypeStatic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 | - SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | - SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_smc5326_decoder, - .encoder = &subghz_protocol_smc5326_encoder, -}; - -void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderSMC5326* instance = malloc(sizeof(SubGhzProtocolEncoderSMC5326)); - - instance->base.protocol = &subghz_protocol_smc5326; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 128; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - return instance; -} - -void subghz_protocol_encoder_smc5326_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderSMC5326* instance = context; - free(instance->encoder.upload); - free(instance); -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderSMC5326 instance - * @return true On success - */ -static bool subghz_protocol_encoder_smc5326_get_upload(SubGhzProtocolEncoderSMC5326* instance) { - furi_assert(instance); - - size_t index = 0; - size_t size_upload = (instance->generic.data_count_bit * 2) + 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; - } - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)instance->te * 3); - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te); - } else { - //send bit 0 - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)instance->te * 3); - } - } - - //Send Stop bit - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te); - //Send PT_GUARD - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 25); - - return true; -} - -bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderSMC5326* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_smc5326_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); - - if(!subghz_protocol_encoder_smc5326_get_upload(instance)) break; - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_smc5326_stop(void* context) { - SubGhzProtocolEncoderSMC5326* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_smc5326_yield(void* context) { - SubGhzProtocolEncoderSMC5326* 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_smc5326_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderSMC5326* instance = malloc(sizeof(SubGhzProtocolDecoderSMC5326)); - instance->base.protocol = &subghz_protocol_smc5326; - instance->generic.protocol_name = instance->base.protocol->name; - return instance; -} - -void subghz_protocol_decoder_smc5326_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - free(instance); -} - -void subghz_protocol_decoder_smc5326_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - instance->decoder.parser_step = SMC5326DecoderStepReset; - instance->last_data = 0; -} - -void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - - switch(instance->decoder.parser_step) { - case SMC5326DecoderStepReset: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short * 24) < - subghz_protocol_smc5326_const.te_delta * 12)) { - //Found Preambula - instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = 0; - } - break; - case SMC5326DecoderStepSaveDuration: - //save duration - if(level) { - instance->decoder.te_last = duration; - instance->te += duration; - instance->decoder.parser_step = SMC5326DecoderStepCheckDuration; - } - break; - case SMC5326DecoderStepCheckDuration: - if(!level) { - if(duration >= ((uint32_t)subghz_protocol_smc5326_const.te_long * 2)) { - instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; - if(instance->decoder.decode_count_bit == - subghz_protocol_smc5326_const.min_count_bit_for_found) { - if((instance->last_data == instance->decoder.decode_data) && - instance->last_data) { - instance->te /= (instance->decoder.decode_count_bit * 4 + 1); - - 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->last_data = instance->decoder.decode_data; - } - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->te = 0; - break; - } - - instance->te += duration; - - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_short) < - subghz_protocol_smc5326_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_long) < - subghz_protocol_smc5326_const.te_delta * 3)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_smc5326_const.te_long) < - subghz_protocol_smc5326_const.te_delta * 3) && - (DURATION_DIFF(duration, subghz_protocol_smc5326_const.te_short) < - subghz_protocol_smc5326_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = SMC5326DecoderStepSaveDuration; - } else { - instance->decoder.parser_step = SMC5326DecoderStepReset; - } - } else { - instance->decoder.parser_step = SMC5326DecoderStepReset; - } - break; - } -} - -uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_smc5326_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) { - FURI_LOG_E(TAG, "Unable to add TE"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* 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_smc5326_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) { - FURI_LOG_E(TAG, "Missing TE"); - break; - } - res = true; - } while(false); - - return res; -} - -static void subghz_protocol_smc5326_get_event_serialize(uint8_t event, FuriString* output) { - furi_string_cat_printf( - output, - "%s%s%s%s\r\n", - (((event >> 6) & 0x3) == 0x3 ? "B1 " : ""), - (((event >> 4) & 0x3) == 0x3 ? "B2 " : ""), - (((event >> 2) & 0x3) == 0x3 ? "B3 " : ""), - (((event >> 0) & 0x3) == 0x3 ? "B4 " : "")); -} - -void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderSMC5326* instance = context; - uint32_t data = (uint32_t)((instance->generic.data >> 9) & 0xFFFF); - - furi_string_cat_printf( - output, - "%s %ubit\r\n" - "Key:%07lX Te:%luus\r\n" - " +: " DIP_PATTERN "\r\n" - " o: " DIP_PATTERN " ", - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data & 0x1FFFFFF), - instance->te, - SHOW_DIP_P(data, DIP_P), - SHOW_DIP_P(data, DIP_O)); - subghz_protocol_smc5326_get_event_serialize(instance->generic.data >> 1, output); - furi_string_cat_printf(output, " -: " DIP_PATTERN "\r\n", SHOW_DIP_P(data, DIP_N)); -} diff --git a/applications/main/subghz/protocols/smc5326.h b/applications/main/subghz/protocols/smc5326.h deleted file mode 100644 index ddc954bd5..000000000 --- a/applications/main/subghz/protocols/smc5326.h +++ /dev/null @@ -1,107 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_SMC5326_NAME "SMC5326" - -typedef struct SubGhzProtocolDecoderSMC5326 SubGhzProtocolDecoderSMC5326; -typedef struct SubGhzProtocolEncoderSMC5326 SubGhzProtocolEncoderSMC5326; - -extern const SubGhzProtocolDecoder subghz_protocol_smc5326_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_smc5326_encoder; -extern const SubGhzProtocol subghz_protocol_smc5326; - -/** - * Allocate SubGhzProtocolEncoderSMC5326. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderSMC5326* pointer to a SubGhzProtocolEncoderSMC5326 instance - */ -void* subghz_protocol_encoder_smc5326_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderSMC5326. - * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance - */ -void subghz_protocol_encoder_smc5326_free(void* context); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance - */ -void subghz_protocol_encoder_smc5326_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderSMC5326 instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_smc5326_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderSMC5326. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderSMC5326* pointer to a SubGhzProtocolDecoderSMC5326 instance - */ -void* subghz_protocol_decoder_smc5326_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderSMC5326. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - */ -void subghz_protocol_decoder_smc5326_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderSMC5326. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - */ -void subghz_protocol_decoder_smc5326_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_smc5326_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_smc5326_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderSMC5326. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_smc5326_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderSMC5326. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_smc5326_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderSMC5326 instance - * @param output Resulting text - */ -void subghz_protocol_decoder_smc5326_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_keytis.c b/applications/main/subghz/protocols/somfy_keytis.c deleted file mode 100644 index ab9202cc3..000000000 --- a/applications/main/subghz/protocols/somfy_keytis.c +++ /dev/null @@ -1,797 +0,0 @@ -#include "somfy_keytis.h" -#include - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolSomfyKeytis" - -static const SubGhzBlockConst subghz_protocol_somfy_keytis_const = { - .te_short = 640, - .te_long = 1280, - .te_delta = 250, - .min_count_bit_for_found = 80, -}; - -struct SubGhzProtocolDecoderSomfyKeytis { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - ManchesterState manchester_saved_state; - uint32_t press_duration_counter; -}; - -struct SubGhzProtocolEncoderSomfyKeytis { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - SomfyKeytisDecoderStepReset = 0, - SomfyKeytisDecoderStepCheckPreambula, - SomfyKeytisDecoderStepFoundPreambula, - SomfyKeytisDecoderStepStartDecode, - SomfyKeytisDecoderStepDecoderData, -} SomfyKeytisDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder = { - .alloc = subghz_protocol_decoder_somfy_keytis_alloc, - .free = subghz_protocol_decoder_somfy_keytis_free, - - .feed = subghz_protocol_decoder_somfy_keytis_feed, - .reset = subghz_protocol_decoder_somfy_keytis_reset, - - .get_hash_data = subghz_protocol_decoder_somfy_keytis_get_hash_data, - .serialize = subghz_protocol_decoder_somfy_keytis_serialize, - .deserialize = subghz_protocol_decoder_somfy_keytis_deserialize, - .get_string = subghz_protocol_decoder_somfy_keytis_get_string, -}; - -const SubGhzProtocol subghz_protocol_somfy_keytis = { - .name = SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_somfy_keytis_decoder, - .encoder = &subghz_protocol_somfy_keytis_encoder, -}; - -const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder = { - .alloc = subghz_protocol_encoder_somfy_keytis_alloc, - .free = subghz_protocol_encoder_somfy_keytis_free, - - .deserialize = subghz_protocol_encoder_somfy_keytis_deserialize, - .stop = subghz_protocol_encoder_somfy_keytis_stop, - .yield = subghz_protocol_encoder_somfy_keytis_yield, -}; - -void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyKeytis)); - - instance->base.protocol = &subghz_protocol_somfy_keytis; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 512; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - - return instance; -} - -void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderSomfyKeytis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyKeytis)); - instance->base.protocol = &subghz_protocol_somfy_keytis; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_encoder_somfy_keytis_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderSomfyKeytis* instance = context; - free(instance->encoder.upload); - free(instance); -} - -void subghz_protocol_decoder_somfy_keytis_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - free(instance); -} - -void subghz_protocol_decoder_somfy_keytis_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - instance->decoder.parser_step = SomfyKeytisDecoderStepReset; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -static bool - subghz_protocol_somfy_keytis_gen_data(SubGhzProtocolEncoderSomfyKeytis* instance, uint8_t btn) { - UNUSED(btn); - uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); - instance->generic.btn = (data >> 48) & 0xF; - instance->generic.cnt = (data >> 24) & 0xFFFF; - instance->generic.serial = data & 0xFFFFFF; - - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - - uint8_t frame[10]; - frame[0] = (0xA << 4) | instance->generic.btn; - frame[1] = 0xF << 4; - frame[2] = instance->generic.cnt >> 8; - frame[3] = instance->generic.cnt; - frame[4] = instance->generic.serial >> 16; - frame[5] = instance->generic.serial >> 8; - frame[6] = instance->generic.serial; - frame[7] = 0xC4; - frame[8] = 0x00; - frame[9] = 0x19; - - uint8_t checksum = 0; - for(uint8_t i = 0; i < 7; i++) { - checksum = checksum ^ frame[i] ^ (frame[i] >> 4); - } - checksum &= 0xF; - - frame[1] |= checksum; - - for(uint8_t i = 1; i < 7; i++) { - frame[i] ^= frame[i - 1]; - } - data = 0; - for(uint8_t i = 0; i < 7; ++i) { - data <<= 8; - data |= frame[i]; - } - instance->generic.data = data; - data = 0; - for(uint8_t i = 7; i < 10; ++i) { - data <<= 8; - data |= frame[i]; - } - instance->generic.data_2 = data; - return true; -} - -bool subghz_protocol_somfy_keytis_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderSomfyKeytis* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->generic.data_count_bit = 80; - bool res = subghz_protocol_somfy_keytis_gen_data(instance, btn); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return true On success - */ -static bool subghz_protocol_encoder_somfy_keytis_get_upload( - SubGhzProtocolEncoderSomfyKeytis* instance, - uint8_t btn) { - furi_assert(instance); - - //gen new key - if(subghz_protocol_somfy_keytis_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display - } else { - return false; - } - - size_t index = 0; - - //Send header - //Wake up - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 - //Hardware sync - for(uint8_t i = 0; i < 12; ++i) { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 - } - //Software sync - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - - //Send key data MSB manchester - - for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } - } - } - - for(uint8_t i = 24; i > 0; i--) { - if(bit_read(instance->generic.data_2, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } - } - } - - //Inter-frame silence - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration += - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; - } else { - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); - } - - for(uint8_t i = 0; i < 2; ++i) { - //Hardware sync - for(uint8_t i = 0; i < 6; ++i) { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 4); // 0 - } - //Software sync - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - - //Send key data MSB manchester - - for(uint8_t i = instance->generic.data_count_bit - 24; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } - } - } - - for(uint8_t i = 24; i > 0; i--) { - if(bit_read(instance->generic.data_2, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short); // 0 - } - } - } - //Inter-frame silence - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration += - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3); - } - } - - //Inter-frame silence - instance->encoder.upload[index - 1].duration += - (uint32_t)30415 - (uint32_t)subghz_protocol_somfy_keytis_const.te_short * 3; - - size_t size_upload = index; - - 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; - } - return true; -} - -bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderSomfyKeytis* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_encoder_somfy_keytis_get_upload(instance, instance->generic.btn); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_somfy_keytis_stop(void* context) { - SubGhzProtocolEncoderSomfyKeytis* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context) { - SubGhzProtocolEncoderSomfyKeytis* 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; -} - -/** - * Сhecksum calculation. - * @param data Вata for checksum calculation - * @return CRC - */ -static uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) { - uint8_t crc = 0; - data &= 0xFFF0FFFFFFFFFF; - for(uint8_t i = 0; i < 56; i += 8) { - crc = crc ^ data >> i ^ (data >> (i + 4)); - } - return crc & 0xf; -} - -void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case SomfyKeytisDecoderStepReset: - if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < - subghz_protocol_somfy_keytis_const.te_delta * 4) { - instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; - instance->header_count++; - } - break; - case SomfyKeytisDecoderStepFoundPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < - subghz_protocol_somfy_keytis_const.te_delta * 4)) { - instance->decoder.parser_step = SomfyKeytisDecoderStepCheckPreambula; - } else { - instance->header_count = 0; - instance->decoder.parser_step = SomfyKeytisDecoderStepReset; - } - break; - case SomfyKeytisDecoderStepCheckPreambula: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 4) < - subghz_protocol_somfy_keytis_const.te_delta * 4) { - instance->decoder.parser_step = SomfyKeytisDecoderStepFoundPreambula; - instance->header_count++; - } else if( - (instance->header_count > 1) && - (DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short * 7) < - subghz_protocol_somfy_keytis_const.te_delta * 4)) { - instance->decoder.parser_step = SomfyKeytisDecoderStepDecoderData; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->press_duration_counter = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - } - } - - break; - - case SomfyKeytisDecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < - subghz_protocol_somfy_keytis_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < - subghz_protocol_somfy_keytis_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= (subghz_protocol_somfy_keytis_const.te_long + - subghz_protocol_somfy_keytis_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_somfy_keytis_const.min_count_bit_for_found) { - //check crc - uint64_t data_tmp = instance->generic.data ^ (instance->generic.data >> 8); - if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) { - 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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - instance->decoder.parser_step = SomfyKeytisDecoderStepReset; - } else { - instance->decoder.parser_step = SomfyKeytisDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_short) < - subghz_protocol_somfy_keytis_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_somfy_keytis_const.te_long) < - subghz_protocol_somfy_keytis_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = SomfyKeytisDecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - if(instance->decoder.decode_count_bit < 56) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; - } else { - instance->press_duration_counter = (instance->press_duration_counter << 1) | - data; - } - - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_somfy_keytis_check_remote_controller(SubGhzBlockGeneric* instance) { - //https://pushstack.wordpress.com/somfy-rts-protocol/ - /* - * 604 us - * / - * | 2416us | 2416us | 2416us | 2416us | 4550 us | | - * - * +--------+ +--------+ +---...---+ - * + +--------+ +--------+ +--+XXXX...XXX+ - * - * | hw. sync. | soft. | | - * | | sync. | data | - * - * - * encrypt | decrypt - * - * package 80 bit pdc key btn crc cnt serial - * - * 0xA453537C4B9855 C40019 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 C80026 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 CC0033 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 D00049 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 D4005C => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 D80063 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 DC0076 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 E00086 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 E40093 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 E800AC => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 EC00B9 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 F000C3 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 F400D6 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 F800E9 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 FC00FC => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 FC0102 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 FC0113 => 0xA 4 F 7 002F 37D3CD - * 0xA453537C4B9855 FC0120 => 0xA 4 F 7 002F 37D3CD - * .......... - * 0xA453537C4B9855 FC048F => 0xA 4 F 7 002F 37D3CD - * - * Pdc: "Press Duration Counter" the total delay of the button is sent 72 parcels, - * pdc cnt4b cnt8b pdc_crc - * C40019 => 11 0001 00 0000 00000001 1001 - * C80026 => 11 0010 00 0000 00000010 0110 - * CC0033 => 11 0011 00 0000 00000011 0011 - * D00049 => 11 0100 00 0000 00000100 1001 - * D4005C => 11 0101 00 0000 00000101 1100 - * D80063 => 11 0110 00 0000 00000110 0011 - * DC0076 => 11 0111 00 0000 00000111 0110 - * E00086 => 11 1000 00 0000 00001000 0110 - * E40093 => 11 1001 00 0000 00001001 0011 - * E800AC => 11 1010 00 0000 00001010 1100 - * EC00B9 => 11 1011 00 0000 00001011 1001 - * F000C3 => 11 1100 00 0000 00001100 0011 - * F400D6 => 11 1101 00 0000 00001101 0110 - * F800E9 => 11 1110 00 0000 00001110 1001 - * FC00FC => 11 1111 00 0000 00001111 1100 - * FC0102 => 11 1111 00 0000 00010000 0010 - * FC0113 => 11 1111 00 0000 00010001 0011 - * FC0120 => 11 1111 00 0000 00010010 0000 - * - * Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15 - * Cnt8b: 8-bit counter changes from 1 to 72 (0x48) - * Ppdc_crc: - * uint8_t crc=0; - * for(i=4; i<24; i+=4){ - * crc ^=(pdc>>i); - * } - * return crc; - * example: crc = 1^0^0^4^C = 9 - * 11, 00, 0000: const - * - * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is - * a linear counter. In the Smoove Origin this counter is increased together with the - * rolling code. But leaving this on a constant value also works. Gerardwr notes that - * for some other types of remotes the MSB is not constant. - * Btn: 4-bit Control codes, this indicates the button that is pressed - * CRC: 4-bit Checksum. - * Ctn: 16-bit rolling code (big-endian) increased with every button press. - * Serial: 24-bit identifier of sending device (little-endian) - * - * - * Decrypt - * - * uint8_t frame[7]; - * for (i=1; i < 7; i++) { - * frame[i] = frame[i] ^ frame[i-1]; - * } - * or - * uint64 Decrypt = frame ^ (frame>>8); - * - * CRC - * - * uint8_t frame[7]; - * for (i=0; i < 7; i++) { - * crc = crc ^ frame[i] ^ (frame[i] >> 4); - * } - * crc = crc & 0xf; - * - */ - - uint64_t data = instance->data ^ (instance->data >> 8); - instance->btn = (data >> 48) & 0xF; - instance->cnt = (data >> 24) & 0xFFFF; - instance->serial = data & 0xFFFFFF; -} - -/** - * Get button name. - * @param btn Button number, 4 bit - */ -static const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) { - const char* name_btn[0x10] = { - "Unknown", - "0x01", - "0x02", - "Prog", - "Key_1", - "0x05", - "0x06", - "0x07", - "0x08", - "0x09", - "0x0A", - "0x0B", - "0x0C", - "0x0D", - "0x0E", - "0x0F"}; - return btn <= 0xf ? name_btn[btn] : name_btn[0]; -} - -uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_somfy_keytis_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - if(res && !flipper_format_write_uint32( - flipper_format, "Duration_Counter", &instance->press_duration_counter, 1)) { - FURI_LOG_E(TAG, "Unable to add Duration_Counter"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* 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_somfy_keytis_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - break; - } - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(!flipper_format_read_uint32( - flipper_format, - "Duration_Counter", - (uint32_t*)&instance->press_duration_counter, - 1)) { - FURI_LOG_E(TAG, "Missing Duration_Counter"); - break; - } - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderSomfyKeytis* instance = context; - - subghz_protocol_somfy_keytis_check_remote_controller(&instance->generic); - - furi_string_cat_printf( - output, - "%s %db\r\n" - "%lX%08lX%06lX\r\n" - "Sn:0x%06lX \r\n" - "Cnt:0x%04lX\r\n" - "Btn:%s\r\n", - - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - instance->press_duration_counter, - instance->generic.serial, - instance->generic.cnt, - subghz_protocol_somfy_keytis_get_name_button(instance->generic.btn)); -} diff --git a/applications/main/subghz/protocols/somfy_keytis.h b/applications/main/subghz/protocols/somfy_keytis.h deleted file mode 100644 index b08da3641..000000000 --- a/applications/main/subghz/protocols/somfy_keytis.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_SOMFY_KEYTIS_NAME "Somfy Keytis" - -typedef struct SubGhzProtocolDecoderSomfyKeytis SubGhzProtocolDecoderSomfyKeytis; -typedef struct SubGhzProtocolEncoderSomfyKeytis SubGhzProtocolEncoderSomfyKeytis; - -extern const SubGhzProtocolDecoder subghz_protocol_somfy_keytis_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_somfy_keytis_encoder; -extern const SubGhzProtocol subghz_protocol_somfy_keytis; - -/** - * Allocate SubGhzProtocolEncoderSomfyKeytis. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderSomfyKeytis* pointer to a SubGhzProtocolEncoderSomfyKeytis instance - */ -void* subghz_protocol_encoder_somfy_keytis_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderSomfyKeytis. - * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance - */ -void subghz_protocol_encoder_somfy_keytis_free(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 24 bit - * @param btn Button number, 8 bit - * @param cnt Counter value, 16 bit - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_somfy_keytis_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance - */ -void subghz_protocol_encoder_somfy_keytis_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderSomfyKeytis instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_somfy_keytis_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderSomfyKeytis. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderSomfyKeytis* pointer to a SubGhzProtocolDecoderSomfyKeytis instance - */ -void* subghz_protocol_decoder_somfy_keytis_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderSomfyKeytis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - */ -void subghz_protocol_decoder_somfy_keytis_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderSomfyKeytis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - */ -void subghz_protocol_decoder_somfy_keytis_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_somfy_keytis_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_somfy_keytis_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderSomfyKeytis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_somfy_keytis_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderSomfyKeytis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_somfy_keytis_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderSomfyKeytis instance - * @param output Resulting text - */ -void subghz_protocol_decoder_somfy_keytis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/somfy_telis.c b/applications/main/subghz/protocols/somfy_telis.c deleted file mode 100644 index 96997c581..000000000 --- a/applications/main/subghz/protocols/somfy_telis.c +++ /dev/null @@ -1,663 +0,0 @@ -#include "somfy_telis.h" -#include - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolSomfyTelis" - -static const SubGhzBlockConst subghz_protocol_somfy_telis_const = { - .te_short = 640, - .te_long = 1280, - .te_delta = 250, - .min_count_bit_for_found = 56, -}; - -struct SubGhzProtocolDecoderSomfyTelis { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - ManchesterState manchester_saved_state; -}; - -struct SubGhzProtocolEncoderSomfyTelis { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; -}; - -typedef enum { - SomfyTelisDecoderStepReset = 0, - SomfyTelisDecoderStepCheckPreambula, - SomfyTelisDecoderStepFoundPreambula, - SomfyTelisDecoderStepStartDecode, - SomfyTelisDecoderStepDecoderData, -} SomfyTelisDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder = { - .alloc = subghz_protocol_decoder_somfy_telis_alloc, - .free = subghz_protocol_decoder_somfy_telis_free, - - .feed = subghz_protocol_decoder_somfy_telis_feed, - .reset = subghz_protocol_decoder_somfy_telis_reset, - - .get_hash_data = subghz_protocol_decoder_somfy_telis_get_hash_data, - .serialize = subghz_protocol_decoder_somfy_telis_serialize, - .deserialize = subghz_protocol_decoder_somfy_telis_deserialize, - .get_string = subghz_protocol_decoder_somfy_telis_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder = { - .alloc = subghz_protocol_encoder_somfy_telis_alloc, - .free = subghz_protocol_encoder_somfy_telis_free, - - .deserialize = subghz_protocol_encoder_somfy_telis_deserialize, - .stop = subghz_protocol_encoder_somfy_telis_stop, - .yield = subghz_protocol_encoder_somfy_telis_yield, -}; - -const SubGhzProtocol subghz_protocol_somfy_telis = { - .name = SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | - SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_somfy_telis_decoder, - .encoder = &subghz_protocol_somfy_telis_encoder, -}; - -void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolEncoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolEncoderSomfyTelis)); - - instance->base.protocol = &subghz_protocol_somfy_telis; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 512; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - - return instance; -} - -void subghz_protocol_encoder_somfy_telis_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderSomfyTelis* instance = context; - free(instance->encoder.upload); - free(instance); -} - -static bool - subghz_protocol_somfy_telis_gen_data(SubGhzProtocolEncoderSomfyTelis* instance, uint8_t btn) { - UNUSED(btn); - uint64_t data = instance->generic.data ^ (instance->generic.data >> 8); - instance->generic.btn = (data >> 44) & 0xF; // ctrl - instance->generic.cnt = (data >> 24) & 0xFFFF; // rolling code - instance->generic.serial = data & 0xFFFFFF; // address - - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - - uint8_t frame[7]; - frame[0] = data >> 48; - frame[1] = instance->generic.btn << 4; - frame[2] = instance->generic.cnt >> 8; - frame[3] = instance->generic.cnt; - frame[4] = instance->generic.serial >> 16; - frame[5] = instance->generic.serial >> 8; - frame[6] = instance->generic.serial; - - uint8_t checksum = 0; - for(uint8_t i = 0; i < 7; i++) { - checksum = checksum ^ frame[i] ^ (frame[i] >> 4); - } - checksum &= 0xF; - - frame[1] |= checksum; - - for(uint8_t i = 1; i < 7; i++) { - frame[i] ^= frame[i - 1]; - } - data = 0; - for(uint8_t i = 0; i < 7; ++i) { - data <<= 8; - data |= frame[i]; - } - instance->generic.data = data; - return true; -} - -bool subghz_protocol_somfy_telis_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderSomfyTelis* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->generic.data_count_bit = 56; - bool res = subghz_protocol_somfy_telis_gen_data(instance, btn); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return true On success - */ -static bool subghz_protocol_encoder_somfy_telis_get_upload( - SubGhzProtocolEncoderSomfyTelis* instance, - uint8_t btn) { - furi_assert(instance); - - //gen new key - if(subghz_protocol_somfy_telis_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display - } else { - return false; - } - - size_t index = 0; - - //Send header - //Wake up - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)9415); // 1 - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)89565); // 0 - //Hardware sync - for(uint8_t i = 0; i < 2; ++i) { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 - } - //Software sync - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - - //Send key data MSB manchester - - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - } - } - } - - //Inter-frame silence - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration += (uint32_t)30415; - } else { - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); - } - - //Retransmission - for(uint8_t i = 0; i < 2; i++) { - //Hardware sync - for(uint8_t i = 0; i < 7; ++i) { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short * 4); // 0 - } - //Software sync - instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)4550); // 1 - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - - //Send key data MSB manchester - - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration *= 2; // 00 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - } else { - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - } - - } else { - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_HIGH) { - instance->encoder.upload[index - 1].duration *= 2; // 11 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - } else { - instance->encoder.upload[index++] = level_duration_make( - true, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 1 - instance->encoder.upload[index++] = level_duration_make( - false, (uint32_t)subghz_protocol_somfy_telis_const.te_short); // 0 - } - } - } - - //Inter-frame silence - if(instance->encoder.upload[index - 1].level == LEVEL_DURATION_LEVEL_LOW) { - instance->encoder.upload[index - 1].duration += (uint32_t)30415; - } else { - instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)30415); - } - } - - size_t size_upload = index; - - 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; - } - return true; -} - -bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderSomfyTelis* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_encoder_somfy_telis_get_upload(instance, instance->generic.btn); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_somfy_telis_stop(void* context) { - SubGhzProtocolEncoderSomfyTelis* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context) { - SubGhzProtocolEncoderSomfyTelis* 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_somfy_telis_alloc(SubGhzEnvironment* environment) { - UNUSED(environment); - SubGhzProtocolDecoderSomfyTelis* instance = malloc(sizeof(SubGhzProtocolDecoderSomfyTelis)); - instance->base.protocol = &subghz_protocol_somfy_telis; - instance->generic.protocol_name = instance->base.protocol->name; - - return instance; -} - -void subghz_protocol_decoder_somfy_telis_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - free(instance); -} - -void subghz_protocol_decoder_somfy_telis_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - instance->decoder.parser_step = SomfyTelisDecoderStepReset; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); -} - -/** - * Сhecksum calculation. - * @param data Вata for checksum calculation - * @return CRC - */ -static uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) { - uint8_t crc = 0; - data &= 0xFFF0FFFFFFFFFF; - for(uint8_t i = 0; i < 56; i += 8) { - crc = crc ^ data >> i ^ (data >> (i + 4)); - } - return crc & 0xf; -} - -void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - - ManchesterEvent event = ManchesterEventReset; - switch(instance->decoder.parser_step) { - case SomfyTelisDecoderStepReset: - if((level) && DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < - subghz_protocol_somfy_telis_const.te_delta * 4) { - instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; - instance->header_count++; - } - break; - case SomfyTelisDecoderStepFoundPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < - subghz_protocol_somfy_telis_const.te_delta * 4)) { - instance->decoder.parser_step = SomfyTelisDecoderStepCheckPreambula; - } else { - instance->header_count = 0; - instance->decoder.parser_step = SomfyTelisDecoderStepReset; - } - break; - case SomfyTelisDecoderStepCheckPreambula: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 4) < - subghz_protocol_somfy_telis_const.te_delta * 4) { - instance->decoder.parser_step = SomfyTelisDecoderStepFoundPreambula; - instance->header_count++; - } else if( - (instance->header_count > 1) && - (DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short * 7) < - subghz_protocol_somfy_telis_const.te_delta * 4)) { - instance->decoder.parser_step = SomfyTelisDecoderStepDecoderData; - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - } - } - - break; - - case SomfyTelisDecoderStepDecoderData: - if(!level) { - if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < - subghz_protocol_somfy_telis_const.te_delta) { - event = ManchesterEventShortLow; - } else if( - DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < - subghz_protocol_somfy_telis_const.te_delta) { - event = ManchesterEventLongLow; - } else if( - duration >= (subghz_protocol_somfy_telis_const.te_long + - subghz_protocol_somfy_telis_const.te_delta)) { - if(instance->decoder.decode_count_bit == - subghz_protocol_somfy_telis_const.min_count_bit_for_found) { - //check crc - uint64_t data_tmp = instance->decoder.decode_data ^ - (instance->decoder.decode_data >> 8); - if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) { - 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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - manchester_advance( - instance->manchester_saved_state, - ManchesterEventReset, - &instance->manchester_saved_state, - NULL); - manchester_advance( - instance->manchester_saved_state, - ManchesterEventLongHigh, - &instance->manchester_saved_state, - NULL); - instance->decoder.parser_step = SomfyTelisDecoderStepReset; - } else { - instance->decoder.parser_step = SomfyTelisDecoderStepReset; - } - } else { - if(DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_short) < - subghz_protocol_somfy_telis_const.te_delta) { - event = ManchesterEventShortHigh; - } else if( - DURATION_DIFF(duration, subghz_protocol_somfy_telis_const.te_long) < - subghz_protocol_somfy_telis_const.te_delta) { - event = ManchesterEventLongHigh; - } else { - instance->decoder.parser_step = SomfyTelisDecoderStepReset; - } - } - if(event != ManchesterEventReset) { - bool data; - bool data_ok = manchester_advance( - instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); - - if(data_ok) { - instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data; - instance->decoder.decode_count_bit++; - } - } - break; - } -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - */ -static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) { - //https://pushstack.wordpress.com/somfy-rts-protocol/ - /* - * 604 us - * / - * | 2416us | 2416us | 2416us | 2416us | 4550 us | | 67648 us | 30415 us | - * - * +--------+ +--------+ +---...---+ - * + +--------+ +--------+ +--+XXXX...XXX+-----...----- - * - * | hw. sync. | soft. | | Inter-frame - * | | sync. | data | gap - * - * - * encrypt | decrypt - * - * package 56 bit cnt key btn|crc cnt serial - * 0xA7232323312222 - 0 => A7 8 0 | 00 00 | 12 13 00 - * 0xA7222223312222 - 1 => A7 8 5 | 00 01 | 12 13 00 - * 0xA7212123312222 - 2 => A7 8 6 | 00 02 | 12 13 00 - * - * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is - * a linear counter. In the Smoove Origin this counter is increased together with the - * rolling code. But leaving this on a constant value also works. Gerardwr notes that - * for some other types of remotes the MSB is not constant. - * Btn: 4-bit Control codes, this indicates the button that is pressed - * CRC: 4-bit Checksum. - * Ctn: 16-bit rolling code (big-endian) increased with every button press. - * Serial: 24-bit identifier of sending device (little-endian) - * - * - * Decrypt - * - * uint8_t frame[7]; - * for (i=1; i < 7; i++) { - * frame[i] = frame[i] ^ frame[i-1]; - * } - * or - * uint64 Decrypt = frame ^ (frame>>8); - * - * Btn - * - * Value Button(s) Description - * 0x1 My Stop or move to favourite position - * 0x2 Up Move up - * 0x3 My + Up Set upper motor limit in initial programming mode - * 0x4 Down Move down - * 0x5 My + Down Set lower motor limit in initial programming mode - * 0x6 Up + Down Change motor limit and initial programming mode - * 0x8 Prog Used for (de-)registering remotes, see below - * 0x9 Sun + Flag Enable sun and wind detector (SUN and FLAG symbol on the Telis Soliris RC) - * 0xA Flag Disable sun detector (FLAG symbol on the Telis Soliris RC) - * - * CRC - * - * uint8_t frame[7]; - * for (i=0; i < 7; i++) { - * cksum = cksum ^ frame[i] ^ (frame[i] >> 4); - * } - * cksum = cksum & 0xf; - * - */ - - uint64_t data = instance->data ^ (instance->data >> 8); - instance->btn = (data >> 44) & 0xF; // ctrl - instance->cnt = (data >> 24) & 0xFFFF; // rolling code - instance->serial = data & 0xFFFFFF; // address -} - -/** - * Get button name. - * @param btn Button number, 4 bit - */ -static const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { - const char* name_btn[16] = { - "Unknown", - "My", - "Up", - "My+Up", - "Down", - "My+Down", - "Up+Down", - "0x07", - "Prog", - "Sun+Flag", - "Flag", - "0x0B", - "0x0C", - "0x0D", - "0x0E", - "0x0F"}; - return btn <= 0xf ? name_btn[btn] : name_btn[0]; -} - -uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_somfy_telis_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); -} - -bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - bool ret = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - break; - } - if(instance->generic.data_count_bit != - subghz_protocol_somfy_telis_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_somfy_telis_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderSomfyTelis* instance = context; - - subghz_protocol_somfy_telis_check_remote_controller(&instance->generic); - furi_string_cat_printf( - output, - "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%06lX \r\n" - "Cnt:0x%04lX\r\n" - "Btn:%s\r\n", - - instance->generic.protocol_name, - instance->generic.data_count_bit, - (uint32_t)(instance->generic.data >> 32), - (uint32_t)instance->generic.data, - instance->generic.serial, - instance->generic.cnt, - subghz_protocol_somfy_telis_get_name_button(instance->generic.btn)); -} diff --git a/applications/main/subghz/protocols/somfy_telis.h b/applications/main/subghz/protocols/somfy_telis.h deleted file mode 100644 index b5e989866..000000000 --- a/applications/main/subghz/protocols/somfy_telis.h +++ /dev/null @@ -1,125 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_SOMFY_TELIS_NAME "Somfy Telis" - -typedef struct SubGhzProtocolDecoderSomfyTelis SubGhzProtocolDecoderSomfyTelis; -typedef struct SubGhzProtocolEncoderSomfyTelis SubGhzProtocolEncoderSomfyTelis; - -extern const SubGhzProtocolDecoder subghz_protocol_somfy_telis_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_somfy_telis_encoder; -extern const SubGhzProtocol subghz_protocol_somfy_telis; - -/** - * Allocate SubGhzProtocolEncoderSomfyTelis. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderSomfyTelis* pointer to a SubGhzProtocolEncoderSomfyTelis instance - */ -void* subghz_protocol_encoder_somfy_telis_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderSomfyTelis. - * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance - */ -void subghz_protocol_encoder_somfy_telis_free(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 24 bit - * @param btn Button number, 8 bit - * @param cnt Counter value, 16 bit - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_somfy_telis_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - SubGhzRadioPreset* preset); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance - */ -void subghz_protocol_encoder_somfy_telis_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderSomfyTelis instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_somfy_telis_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderSomfyTelis. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderSomfyTelis* pointer to a SubGhzProtocolDecoderSomfyTelis instance - */ -void* subghz_protocol_decoder_somfy_telis_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderSomfyTelis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - */ -void subghz_protocol_decoder_somfy_telis_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderSomfyTelis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - */ -void subghz_protocol_decoder_somfy_telis_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_somfy_telis_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_somfy_telis_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderSomfyTelis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_somfy_telis_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderSomfyTelis. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_somfy_telis_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderSomfyTelis instance - * @param output Resulting text - */ -void subghz_protocol_decoder_somfy_telis_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/protocols/star_line.c b/applications/main/subghz/protocols/star_line.c deleted file mode 100644 index 3066c6e2b..000000000 --- a/applications/main/subghz/protocols/star_line.c +++ /dev/null @@ -1,780 +0,0 @@ -#include "star_line.h" -#include "keeloq_common.h" - -#include "../subghz_keystore.h" -#include - -#include "../blocks/const.h" -#include "../blocks/decoder.h" -#include "../blocks/encoder.h" -#include "../blocks/generic.h" -#include "../blocks/math.h" - -#define TAG "SubGhzProtocolStarLine" - -static const SubGhzBlockConst subghz_protocol_star_line_const = { - .te_short = 250, - .te_long = 500, - .te_delta = 120, - .min_count_bit_for_found = 64, -}; - -struct SubGhzProtocolDecoderStarLine { - SubGhzProtocolDecoderBase base; - - SubGhzBlockDecoder decoder; - SubGhzBlockGeneric generic; - - uint16_t header_count; - SubGhzKeystore* keystore; - const char* manufacture_name; - - FuriString* manufacture_from_file; -}; - -struct SubGhzProtocolEncoderStarLine { - SubGhzProtocolEncoderBase base; - - SubGhzProtocolBlockEncoder encoder; - SubGhzBlockGeneric generic; - - SubGhzKeystore* keystore; - const char* manufacture_name; - - FuriString* manufacture_from_file; -}; - -typedef enum { - StarLineDecoderStepReset = 0, - StarLineDecoderStepCheckPreambula, - StarLineDecoderStepSaveDuration, - StarLineDecoderStepCheckDuration, -} StarLineDecoderStep; - -const SubGhzProtocolDecoder subghz_protocol_star_line_decoder = { - .alloc = subghz_protocol_decoder_star_line_alloc, - .free = subghz_protocol_decoder_star_line_free, - - .feed = subghz_protocol_decoder_star_line_feed, - .reset = subghz_protocol_decoder_star_line_reset, - - .get_hash_data = subghz_protocol_decoder_star_line_get_hash_data, - .serialize = subghz_protocol_decoder_star_line_serialize, - .deserialize = subghz_protocol_decoder_star_line_deserialize, - .get_string = subghz_protocol_decoder_star_line_get_string, -}; - -const SubGhzProtocolEncoder subghz_protocol_star_line_encoder = { - .alloc = subghz_protocol_encoder_star_line_alloc, - .free = subghz_protocol_encoder_star_line_free, - - .deserialize = subghz_protocol_encoder_star_line_deserialize, - .stop = subghz_protocol_encoder_star_line_stop, - .yield = subghz_protocol_encoder_star_line_yield, -}; - -const SubGhzProtocol subghz_protocol_star_line = { - .name = SUBGHZ_PROTOCOL_STAR_LINE_NAME, - .type = SubGhzProtocolTypeDynamic, - .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, - - .decoder = &subghz_protocol_star_line_decoder, - .encoder = &subghz_protocol_star_line_encoder, -}; - -static const char* mfname; - -static int kl_type; - -void star_line_reset_mfname() { - mfname = ""; -} - -void star_line_reset_kl_type() { - kl_type = 0; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - */ -static void subghz_protocol_star_line_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name); - -void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolEncoderStarLine* instance = malloc(sizeof(SubGhzProtocolEncoderStarLine)); - - instance->base.protocol = &subghz_protocol_star_line; - instance->generic.protocol_name = instance->base.protocol->name; - instance->keystore = subghz_environment_get_keystore(environment); - - instance->manufacture_from_file = furi_string_alloc(); - - instance->encoder.repeat = 10; - instance->encoder.size_upload = 256; - instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); - instance->encoder.is_running = false; - - return instance; -} - -void subghz_protocol_encoder_star_line_free(void* context) { - furi_assert(context); - SubGhzProtocolEncoderStarLine* instance = context; - furi_string_free(instance->manufacture_from_file); - free(instance->encoder.upload); - free(instance); -} - -/** - * Key generation from simple data - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq* instance - * @param btn Button number, 4 bit - */ -static bool - subghz_protocol_star_line_gen_data(SubGhzProtocolEncoderStarLine* instance, uint8_t btn) { - if(instance->generic.cnt < 0xFFFF) { - instance->generic.cnt++; - } else if(instance->generic.cnt >= 0xFFFF) { - instance->generic.cnt = 0; - } - uint32_t fix = btn << 24 | instance->generic.serial; - uint32_t decrypt = btn << 24 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; - uint32_t hop = 0; - uint64_t man = 0; - uint64_t code_found_reverse; - int res = 0; - - if(instance->manufacture_name == 0x0) { - instance->manufacture_name = ""; - } - - if(strcmp(instance->manufacture_name, "Unknown") == 0) { - code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - hop = code_found_reverse & 0x00000000ffffffff; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - //Simple Learning - hop = subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - break; - case KEELOQ_LEARNING_NORMAL: - //Normal Learning - man = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - break; - case KEELOQ_LEARNING_UNKNOWN: - if(kl_type == 1) { - hop = - subghz_protocol_keeloq_common_encrypt(decrypt, manufacture_code->key); - } - if(kl_type == 2) { - man = subghz_protocol_keeloq_common_normal_learning( - fix, manufacture_code->key); - hop = subghz_protocol_keeloq_common_encrypt(decrypt, man); - } - break; - } - break; - } - } - } - if(hop) { - uint64_t yek = (uint64_t)fix << 32 | hop; - instance->generic.data = - subghz_protocol_blocks_reverse_key(yek, instance->generic.data_count_bit); - return true; - } else { - instance->manufacture_name = "Unknown"; - return false; - } -} - -bool subghz_protocol_star_line_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - const char* manufacture_name, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolEncoderStarLine* instance = context; - instance->generic.serial = serial; - instance->generic.cnt = cnt; - instance->manufacture_name = manufacture_name; - instance->generic.data_count_bit = 64; - bool res = subghz_protocol_star_line_gen_data(instance, btn); - if(res) { - res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - } - return res; -} - -/** - * Generating an upload from data. - * @param instance Pointer to a SubGhzProtocolEncoderKeeloq instance - * @return true On success - */ -static bool subghz_protocol_encoder_star_line_get_upload( - SubGhzProtocolEncoderStarLine* instance, - uint8_t btn) { - furi_assert(instance); - - //gen new key - if(subghz_protocol_star_line_gen_data(instance, btn)) { - //ToDo if you need to add a callback to automatically update the data on the display - } else { - return false; - } - - size_t index = 0; - size_t size_upload = 6 * 2 + (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; - } - - //Send header - for(uint8_t i = 6; i > 0; i--) { - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long * 2); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long * 2); - } - - //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { - //send bit 1 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_long); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_long); - } else { - //send bit 0 - instance->encoder.upload[index++] = - level_duration_make(true, (uint32_t)subghz_protocol_star_line_const.te_short); - instance->encoder.upload[index++] = - level_duration_make(false, (uint32_t)subghz_protocol_star_line_const.te_short); - } - } - - return true; -} - -bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolEncoderStarLine* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "ENCODER: Missing Manufacture"); - } - - subghz_protocol_star_line_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - //optional parameter parameter - flipper_format_read_uint32( - flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); - - subghz_protocol_encoder_star_line_get_upload(instance, instance->generic.btn); - - if(!flipper_format_rewind(flipper_format)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - uint8_t key_data[sizeof(uint64_t)] = {0}; - for(size_t i = 0; i < sizeof(uint64_t); i++) { - key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; - } - if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { - FURI_LOG_E(TAG, "Unable to add Key"); - break; - } - - instance->encoder.is_running = true; - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_encoder_star_line_stop(void* context) { - SubGhzProtocolEncoderStarLine* instance = context; - instance->encoder.is_running = false; -} - -LevelDuration subghz_protocol_encoder_star_line_yield(void* context) { - SubGhzProtocolEncoderStarLine* 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_star_line_alloc(SubGhzEnvironment* environment) { - SubGhzProtocolDecoderStarLine* instance = malloc(sizeof(SubGhzProtocolDecoderStarLine)); - instance->base.protocol = &subghz_protocol_star_line; - instance->generic.protocol_name = instance->base.protocol->name; - - instance->manufacture_from_file = furi_string_alloc(); - - instance->keystore = subghz_environment_get_keystore(environment); - - return instance; -} - -void subghz_protocol_decoder_star_line_free(void* context) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - furi_string_free(instance->manufacture_from_file); - - free(instance); -} - -void subghz_protocol_decoder_star_line_reset(void* context) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - instance->decoder.parser_step = StarLineDecoderStepReset; - mfname = ""; - kl_type = 0; -} - -void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - - switch(instance->decoder.parser_step) { - case StarLineDecoderStepReset: - if(level) { - if(DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < - subghz_protocol_star_line_const.te_delta * 2) { - instance->decoder.parser_step = StarLineDecoderStepCheckPreambula; - instance->header_count++; - } else if(instance->header_count > 4) { - instance->decoder.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->decoder.te_last = duration; - instance->decoder.parser_step = StarLineDecoderStepCheckDuration; - } - } else { - instance->header_count = 0; - } - break; - case StarLineDecoderStepCheckPreambula: - if((!level) && (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long * 2) < - subghz_protocol_star_line_const.te_delta * 2)) { - //Found Preambula - instance->decoder.parser_step = StarLineDecoderStepReset; - } else { - instance->header_count = 0; - instance->decoder.parser_step = StarLineDecoderStepReset; - } - break; - case StarLineDecoderStepSaveDuration: - if(level) { - if(duration >= (subghz_protocol_star_line_const.te_long + - subghz_protocol_star_line_const.te_delta)) { - instance->decoder.parser_step = StarLineDecoderStepReset; - if(instance->decoder.decode_count_bit >= - subghz_protocol_star_line_const.min_count_bit_for_found) { - if(instance->generic.data != instance->decoder.decode_data) { - 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.decode_data = 0; - instance->decoder.decode_count_bit = 0; - instance->header_count = 0; - break; - } else { - instance->decoder.te_last = duration; - instance->decoder.parser_step = StarLineDecoderStepCheckDuration; - } - - } else { - instance->decoder.parser_step = StarLineDecoderStepReset; - } - break; - case StarLineDecoderStepCheckDuration: - if(!level) { - if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_short) < - subghz_protocol_star_line_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_short) < - subghz_protocol_star_line_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 0); - instance->decoder.parser_step = StarLineDecoderStepSaveDuration; - } else if( - (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_star_line_const.te_long) < - subghz_protocol_star_line_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_star_line_const.te_long) < - subghz_protocol_star_line_const.te_delta)) { - subghz_protocol_blocks_add_bit(&instance->decoder, 1); - instance->decoder.parser_step = StarLineDecoderStepSaveDuration; - } else { - instance->decoder.parser_step = StarLineDecoderStepReset; - } - } else { - instance->decoder.parser_step = StarLineDecoderStepReset; - } - break; - } -} - -/** - * Validation of decrypt data. - * @param instance Pointer to a SubGhzBlockGeneric instance - * @param decrypt Decrypd data - * @param btn Button number, 4 bit - * @param end_serial decrement the last 10 bits of the serial number - * @return true On success - */ -static inline bool subghz_protocol_star_line_check_decrypt( - SubGhzBlockGeneric* instance, - uint32_t decrypt, - uint8_t btn, - uint32_t end_serial) { - furi_assert(instance); - if((decrypt >> 24 == btn) && ((((uint16_t)(decrypt >> 16)) & 0x00FF) == end_serial)) { - instance->cnt = decrypt & 0x0000FFFF; - return true; - } - return false; -} - -/** - * Checking the accepted code against the database manafacture key - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param fix Fix part of the parcel - * @param hop Hop encrypted part of the parcel - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - * @return true on successful search - */ -static uint8_t subghz_protocol_star_line_check_remote_controller_selector( - SubGhzBlockGeneric* instance, - uint32_t fix, - uint32_t hop, - SubGhzKeystore* keystore, - const char** manufacture_name) { - uint16_t end_serial = (uint16_t)(fix & 0xFF); - uint8_t btn = (uint8_t)(fix >> 24); - uint32_t decrypt = 0; - uint64_t man_normal_learning; - int res = 0; - if(mfname == 0x0) { - mfname = ""; - } - - if(strcmp(mfname, "") == 0) { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - // Check for mirrored man - man_normal_learning = subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt(instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - break; - } - } - } else if(strcmp(mfname, "Unknown") == 0) { - return 1; - } else { - for - M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) { - res = strcmp(furi_string_get_cstr(manufacture_code->name), mfname); - if(res == 0) { - switch(manufacture_code->type) { - case KEELOQ_LEARNING_SIMPLE: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_NORMAL: - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - return 1; - } - break; - case KEELOQ_LEARNING_UNKNOWN: - // Simple Learning - decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - // Check for mirrored man - uint64_t man_rev = 0; - uint64_t man_rev_byte = 0; - for(uint8_t i = 0; i < 64; i += 8) { - man_rev_byte = (uint8_t)(manufacture_code->key >> i); - man_rev = man_rev | man_rev_byte << (56 - i); - } - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_rev); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 1; - return 1; - } - //########################### - // Normal Learning - // https://phreakerclub.com/forum/showpost.php?p=43557&postcount=37 - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, manufacture_code->key); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - - // Check for mirrored man - man_normal_learning = - subghz_protocol_keeloq_common_normal_learning(fix, man_rev); - decrypt = subghz_protocol_keeloq_common_decrypt(hop, man_normal_learning); - if(subghz_protocol_star_line_check_decrypt( - instance, decrypt, btn, end_serial)) { - *manufacture_name = furi_string_get_cstr(manufacture_code->name); - mfname = *manufacture_name; - kl_type = 2; - return 1; - } - break; - } - } - } - } - - *manufacture_name = "Unknown"; - mfname = "Unknown"; - instance->cnt = 0; - - return 0; -} - -/** - * Analysis of received data - * @param instance Pointer to a SubGhzBlockGeneric* instance - * @param keystore Pointer to a SubGhzKeystore* instance - * @param manufacture_name - */ -static void subghz_protocol_star_line_check_remote_controller( - SubGhzBlockGeneric* instance, - SubGhzKeystore* keystore, - const char** manufacture_name) { - uint64_t key = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit); - uint32_t key_fix = key >> 32; - uint32_t key_hop = key & 0x00000000ffffffff; - - subghz_protocol_star_line_check_remote_controller_selector( - instance, key_fix, key_hop, keystore, manufacture_name); - - instance->serial = key_fix & 0x00FFFFFF; - instance->btn = key_fix >> 24; -} - -uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - return subghz_protocol_blocks_get_hash_data( - &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); -} - -bool subghz_protocol_decoder_star_line_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - subghz_protocol_star_line_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset); - - if(res && !flipper_format_write_string_cstr( - flipper_format, "Manufacture", instance->manufacture_name)) { - FURI_LOG_E(TAG, "Unable to add manufacture name"); - res = false; - } - if(res && instance->generic.data_count_bit != - subghz_protocol_star_line_const.min_count_bit_for_found) { - FURI_LOG_E(TAG, "Wrong number of bits in key"); - res = false; - } - return res; -} - -bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - bool res = false; - do { - if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) { - FURI_LOG_E(TAG, "Deserialize error"); - break; - } - - // Read manufacturer from file - if(flipper_format_read_string( - flipper_format, "Manufacture", instance->manufacture_from_file)) { - instance->manufacture_name = furi_string_get_cstr(instance->manufacture_from_file); - mfname = furi_string_get_cstr(instance->manufacture_from_file); - } else { - FURI_LOG_D(TAG, "DECODER: Missing Manufacture"); - } - - res = true; - } while(false); - - return res; -} - -void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output) { - furi_assert(context); - SubGhzProtocolDecoderStarLine* instance = context; - - subghz_protocol_star_line_check_remote_controller( - &instance->generic, instance->keystore, &instance->manufacture_name); - - uint32_t code_found_hi = instance->generic.data >> 32; - uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff; - - uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key( - instance->generic.data, instance->generic.data_count_bit); - uint32_t code_found_reverse_hi = code_found_reverse >> 32; - uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; - - furi_string_cat_printf( - output, - "%s %dbit\r\n" - "Key:%08lX%08lX\r\n" - "Fix:0x%08lX Cnt:%04lX\r\n" - "Hop:0x%08lX Btn:%02X\r\n" - "MF:%s\r\n", - instance->generic.protocol_name, - instance->generic.data_count_bit, - code_found_hi, - code_found_lo, - code_found_reverse_hi, - instance->generic.cnt, - code_found_reverse_lo, - instance->generic.btn, - instance->manufacture_name); -} diff --git a/applications/main/subghz/protocols/star_line.h b/applications/main/subghz/protocols/star_line.h deleted file mode 100644 index e8873d41a..000000000 --- a/applications/main/subghz/protocols/star_line.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include "base.h" - -#define SUBGHZ_PROTOCOL_STAR_LINE_NAME "Star Line" - -typedef struct SubGhzProtocolDecoderStarLine SubGhzProtocolDecoderStarLine; -typedef struct SubGhzProtocolEncoderStarLine SubGhzProtocolEncoderStarLine; - -extern const SubGhzProtocolDecoder subghz_protocol_star_line_decoder; -extern const SubGhzProtocolEncoder subghz_protocol_star_line_encoder; -extern const SubGhzProtocol subghz_protocol_star_line; - -void star_line_reset_mfname(); - -void star_line_reset_kl_type(); - -/** - * Allocate SubGhzProtocolEncoderStarLine. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolEncoderStarLine* pointer to a SubGhzProtocolEncoderStarLine instance - */ -void* subghz_protocol_encoder_star_line_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolEncoderStarLine. - * @param context Pointer to a SubGhzProtocolEncoderStarLine instance - */ -void subghz_protocol_encoder_star_line_free(void* context); - -/** - * Key generation from simple data. - * @param context Pointer to a SubGhzProtocolEncoderStarLine instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param serial Serial number, 24 bit - * @param btn Button number, 8 bit - * @param cnt Counter value, 16 bit - * @param manufacture_name Name of manufacturer's key - * @param preset Modulation, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_star_line_create_data( - void* context, - FlipperFormat* flipper_format, - uint32_t serial, - uint8_t btn, - uint16_t cnt, - const char* manufacture_name, - SubGhzRadioPreset* preset); - -/** - * Deserialize and generating an upload to send. - * @param context Pointer to a SubGhzProtocolEncoderStarLine instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_encoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Forced transmission stop. - * @param context Pointer to a SubGhzProtocolEncoderStarLine instance - */ -void subghz_protocol_encoder_star_line_stop(void* context); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzProtocolEncoderStarLine instance - * @return LevelDuration - */ -LevelDuration subghz_protocol_encoder_star_line_yield(void* context); - -/** - * Allocate SubGhzProtocolDecoderStarLine. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzProtocolDecoderStarLine* pointer to a SubGhzProtocolDecoderStarLine instance - */ -void* subghz_protocol_decoder_star_line_alloc(SubGhzEnvironment* environment); - -/** - * Free SubGhzProtocolDecoderStarLine. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - */ -void subghz_protocol_decoder_star_line_free(void* context); - -/** - * Reset decoder SubGhzProtocolDecoderStarLine. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - */ -void subghz_protocol_decoder_star_line_reset(void* context); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_protocol_decoder_star_line_feed(void* context, bool level, uint32_t duration); - -/** - * Getting the hash sum of the last randomly received parcel. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - * @return hash Hash sum - */ -uint8_t subghz_protocol_decoder_star_line_get_hash_data(void* context); - -/** - * Serialize data SubGhzProtocolDecoderStarLine. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - * @param flipper_format Pointer to a FlipperFormat instance - * @param preset The modulation on which the signal was received, SubGhzRadioPreset - * @return true On success - */ -bool subghz_protocol_decoder_star_line_serialize( - void* context, - FlipperFormat* flipper_format, - SubGhzRadioPreset* preset); - -/** - * Deserialize data SubGhzProtocolDecoderStarLine. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_protocol_decoder_star_line_deserialize(void* context, FlipperFormat* flipper_format); - -/** - * Getting a textual representation of the received data. - * @param context Pointer to a SubGhzProtocolDecoderStarLine instance - * @param output Resulting text - */ -void subghz_protocol_decoder_star_line_get_string(void* context, FuriString* output); diff --git a/applications/main/subghz/receiver.c b/applications/main/subghz/receiver.c deleted file mode 100644 index 698fe098e..000000000 --- a/applications/main/subghz/receiver.c +++ /dev/null @@ -1,124 +0,0 @@ -#include "receiver.h" - -#include "registry.h" -#include "protocols/protocol_items.h" - -#include - -typedef struct { - SubGhzProtocolEncoderBase* base; -} SubGhzReceiverSlot; - -ARRAY_DEF(SubGhzReceiverSlotArray, SubGhzReceiverSlot, M_POD_OPLIST); -#define M_OPL_SubGhzReceiverSlotArray_t() ARRAY_OPLIST(SubGhzReceiverSlotArray, M_POD_OPLIST) - -struct SubGhzReceiver { - SubGhzReceiverSlotArray_t slots; - SubGhzProtocolFlag filter; - - SubGhzReceiverCallback callback; - void* context; -}; - -SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment) { - SubGhzReceiver* instance = malloc(sizeof(SubGhzReceiver)); - SubGhzReceiverSlotArray_init(instance->slots); - const SubGhzProtocolRegistry* protocol_registry_items = - subghz_environment_get_protocol_registry(environment); - - for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry_items); ++i) { - const SubGhzProtocol* protocol = - subghz_protocol_registry_get_by_index(protocol_registry_items, i); - - if(protocol->decoder && protocol->decoder->alloc) { - SubGhzReceiverSlot* slot = SubGhzReceiverSlotArray_push_new(instance->slots); - slot->base = protocol->decoder->alloc(environment); - } - } - - instance->callback = NULL; - instance->context = NULL; - return instance; -} - -void subghz_receiver_free(SubGhzReceiver* instance) { - furi_assert(instance); - - instance->callback = NULL; - instance->context = NULL; - - // Release allocated slots - for - M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - slot->base->protocol->decoder->free(slot->base); - slot->base = NULL; - } - SubGhzReceiverSlotArray_clear(instance->slots); - - free(instance); -} - -void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration) { - furi_assert(instance); - furi_assert(instance->slots); - - for - M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - if((slot->base->protocol->flag & instance->filter) != 0) { - slot->base->protocol->decoder->feed(slot->base, level, duration); - } - } -} - -void subghz_receiver_reset(SubGhzReceiver* instance) { - furi_assert(instance); - furi_assert(instance->slots); - - for - M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - slot->base->protocol->decoder->reset(slot->base); - } -} - -static void subghz_receiver_rx_callback(SubGhzProtocolDecoderBase* decoder_base, void* context) { - SubGhzReceiver* instance = context; - if(instance->callback) { - instance->callback(instance, decoder_base, instance->context); - } -} - -void subghz_receiver_set_rx_callback( - SubGhzReceiver* instance, - SubGhzReceiverCallback callback, - void* context) { - furi_assert(instance); - - for - M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - subghz_protocol_decoder_base_set_decoder_callback( - (SubGhzProtocolDecoderBase*)slot->base, subghz_receiver_rx_callback, instance); - } - - instance->callback = callback; - instance->context = context; -} - -void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter) { - furi_assert(instance); - instance->filter = filter; -} - -SubGhzProtocolDecoderBase* subghz_receiver_search_decoder_base_by_name( - SubGhzReceiver* instance, - const char* decoder_name) { - SubGhzProtocolDecoderBase* result = NULL; - - for - M_EACH(slot, instance->slots, SubGhzReceiverSlotArray_t) { - if(strcmp(slot->base->protocol->name, decoder_name) == 0) { - result = (SubGhzProtocolDecoderBase*)slot->base; - break; - } - } - return result; -} diff --git a/applications/main/subghz/receiver.h b/applications/main/subghz/receiver.h deleted file mode 100644 index 2ef722d1f..000000000 --- a/applications/main/subghz/receiver.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include "types.h" -#include "protocols/base.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzReceiver SubGhzReceiver; - -typedef void (*SubGhzReceiverCallback)( - SubGhzReceiver* decoder, - SubGhzProtocolDecoderBase* decoder_base, - void* context); - -/** - * Allocate and init SubGhzReceiver. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzReceiver* pointer to a SubGhzReceiver instance - */ -SubGhzReceiver* subghz_receiver_alloc_init(SubGhzEnvironment* environment); - -/** - * Free SubGhzReceiver. - * @param instance Pointer to a SubGhzReceiver instance - */ -void subghz_receiver_free(SubGhzReceiver* instance); - -/** - * Parse a raw sequence of levels and durations received from the air. - * @param instance Pointer to a SubGhzReceiver instance - * @param level Signal level true-high false-low - * @param duration Duration of this level in, us - */ -void subghz_receiver_decode(SubGhzReceiver* instance, bool level, uint32_t duration); - -/** - * Reset decoder SubGhzReceiver. - * @param instance Pointer to a SubGhzReceiver instance - */ -void subghz_receiver_reset(SubGhzReceiver* instance); - -/** - * Set a callback upon completion of successful decoding of one of the protocols. - * @param instance Pointer to a SubGhzReceiver instance - * @param callback Callback, SubGhzReceiverCallback - * @param context Context - */ -void subghz_receiver_set_rx_callback( - SubGhzReceiver* instance, - SubGhzReceiverCallback callback, - void* context); - -/** - * Set the filter of receivers that will work at the moment. - * @param instance Pointer to a SubGhzReceiver instance - * @param filter Filter, SubGhzProtocolFlag - */ -void subghz_receiver_set_filter(SubGhzReceiver* instance, SubGhzProtocolFlag filter); - -/** - * Search for a cattery by his name. - * @param instance Pointer to a SubGhzReceiver instance - * @param decoder_name Receiver name - * @return SubGhzProtocolDecoderBase* pointer to a SubGhzProtocolDecoderBase instance - */ -SubGhzProtocolDecoderBase* - subghz_receiver_search_decoder_base_by_name(SubGhzReceiver* instance, const char* decoder_name); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/registry.c b/applications/main/subghz/registry.c deleted file mode 100644 index d0c22ea8c..000000000 --- a/applications/main/subghz/registry.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "registry.h" - -const SubGhzProtocol* subghz_protocol_registry_get_by_name( - const SubGhzProtocolRegistry* protocol_registry, - const char* name) { - furi_assert(protocol_registry); - - for(size_t i = 0; i < subghz_protocol_registry_count(protocol_registry); i++) { - if(strcmp(name, protocol_registry->items[i]->name) == 0) { - return protocol_registry->items[i]; - } - } - return NULL; -} - -const SubGhzProtocol* subghz_protocol_registry_get_by_index( - const SubGhzProtocolRegistry* protocol_registry, - size_t index) { - furi_assert(protocol_registry); - if(index < subghz_protocol_registry_count(protocol_registry)) { - return protocol_registry->items[index]; - } else { - return NULL; - } -} - -size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry) { - furi_assert(protocol_registry); - return protocol_registry->size; -} diff --git a/applications/main/subghz/registry.h b/applications/main/subghz/registry.h deleted file mode 100644 index 91027807e..000000000 --- a/applications/main/subghz/registry.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "types.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzEnvironment SubGhzEnvironment; - -typedef struct SubGhzProtocolRegistry SubGhzProtocolRegistry; - -struct SubGhzProtocolRegistry { - const SubGhzProtocol** items; - const size_t size; -}; - -/** - * Registration by name SubGhzProtocol. - * @param protocol_registry SubGhzProtocolRegistry - * @param name Protocol name - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_name( - const SubGhzProtocolRegistry* protocol_registry, - const char* name); - -/** - * Registration protocol by index in array SubGhzProtocol. - * @param protocol_registry SubGhzProtocolRegistry - * @param index Protocol by index in array - * @return SubGhzProtocol* pointer to a SubGhzProtocol instance - */ -const SubGhzProtocol* subghz_protocol_registry_get_by_index( - const SubGhzProtocolRegistry* protocol_registry, - size_t index); - -/** - * Getting the number of registered protocols. - * @param protocol_registry SubGhzProtocolRegistry - * @return Number of protocols - */ -size_t subghz_protocol_registry_count(const SubGhzProtocolRegistry* protocol_registry); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/subghz_file_encoder_worker.c b/applications/main/subghz/subghz_file_encoder_worker.c deleted file mode 100644 index a8c6519ef..000000000 --- a/applications/main/subghz/subghz_file_encoder_worker.c +++ /dev/null @@ -1,244 +0,0 @@ -#include "subghz_file_encoder_worker.h" - -#include -#include -#include - -#define TAG "SubGhzFileEncoderWorker" - -#define SUBGHZ_FILE_ENCODER_LOAD 512 - -struct SubGhzFileEncoderWorker { - FuriThread* thread; - FuriStreamBuffer* stream; - - Storage* storage; - FlipperFormat* flipper_format; - - volatile bool worker_running; - volatile bool worker_stoping; - bool level; - bool is_storage_slow; - FuriString* str_data; - FuriString* file_path; - - SubGhzFileEncoderWorkerCallbackEnd callback_end; - void* context_end; -}; - -void subghz_file_encoder_worker_callback_end( - SubGhzFileEncoderWorker* instance, - SubGhzFileEncoderWorkerCallbackEnd callback_end, - void* context_end) { - furi_assert(instance); - furi_assert(callback_end); - instance->callback_end = callback_end; - instance->context_end = context_end; -} - -void subghz_file_encoder_worker_add_level_duration( - SubGhzFileEncoderWorker* instance, - int32_t duration) { - bool res = true; - if(duration < 0 && !instance->level) { - res = false; - } else if(duration > 0 && instance->level) { - res = false; - } - - if(res) { - instance->level = !instance->level; - furi_stream_buffer_send(instance->stream, &duration, sizeof(int32_t), 100); - } else { - FURI_LOG_E(TAG, "Invalid level in the stream"); - } -} - -bool subghz_file_encoder_worker_data_parse(SubGhzFileEncoderWorker* instance, const char* strStart) { - char* str1; - bool res = false; - // Line sample: "RAW_Data: -1, 2, -2..." - - // Look for a key in the line - str1 = strstr(strStart, "RAW_Data: "); - - if(str1 != NULL) { - // Skip key - str1 = strchr(str1, ' '); - - // Check that there is still an element in the line - while(strchr(str1, ' ') != NULL) { - str1 = strchr(str1, ' '); - - // Skip space - str1 += 1; - subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); - } - res = true; - } - return res; -} - -void subghz_file_encoder_worker_get_text_progress( - SubGhzFileEncoderWorker* instance, - FuriString* output) { - UNUSED(output); - Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); - size_t total_size = stream_size(stream); - size_t current_offset = stream_tell(stream); - size_t buffer_avail = furi_stream_buffer_bytes_available(instance->stream); - - furi_string_printf(output, "%03u%%", 100 * (current_offset - buffer_avail) / total_size); -} - -LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) { - furi_assert(context); - SubGhzFileEncoderWorker* instance = context; - int32_t duration; - int ret = furi_stream_buffer_receive(instance->stream, &duration, sizeof(int32_t), 0); - if(ret == sizeof(int32_t)) { - LevelDuration level_duration = {.level = LEVEL_DURATION_RESET}; - if(duration < 0) { - level_duration = level_duration_make(false, -duration); - } else if(duration > 0) { - level_duration = level_duration_make(true, duration); - } else if(duration == 0) { //-V547 - level_duration = level_duration_reset(); - FURI_LOG_I(TAG, "Stop transmission"); - instance->worker_stoping = true; - } - return level_duration; - } else { - instance->is_storage_slow = true; - return level_duration_wait(); - } -} - -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t subghz_file_encoder_worker_thread(void* context) { - SubGhzFileEncoderWorker* instance = context; - FURI_LOG_I(TAG, "Worker start"); - bool res = false; - instance->is_storage_slow = false; - Stream* stream = flipper_format_get_raw_stream(instance->flipper_format); - do { - if(!flipper_format_file_open_existing( - instance->flipper_format, furi_string_get_cstr(instance->file_path))) { - FURI_LOG_E( - TAG, - "Unable to open file for read: %s", - furi_string_get_cstr(instance->file_path)); - break; - } - if(!flipper_format_read_string(instance->flipper_format, "Protocol", instance->str_data)) { - FURI_LOG_E(TAG, "Missing Protocol"); - break; - } - - //skip the end of the previous line "\n" - stream_seek(stream, 1, StreamOffsetFromCurrent); - res = true; - instance->worker_stoping = false; - FURI_LOG_I(TAG, "Start transmission"); - } while(0); - - while(res && instance->worker_running) { - size_t stream_free_byte = furi_stream_buffer_spaces_available(instance->stream); - if((stream_free_byte / sizeof(int32_t)) >= SUBGHZ_FILE_ENCODER_LOAD) { - if(stream_read_line(stream, instance->str_data)) { - furi_string_trim(instance->str_data); - if(!subghz_file_encoder_worker_data_parse( - instance, furi_string_get_cstr(instance->str_data))) { - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - break; - } - } else { - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); - break; - } - } else { - furi_delay_ms(1); - } - } - //waiting for the end of the transfer - if(instance->is_storage_slow) { - FURI_LOG_E(TAG, "Storage is slow"); - } - FURI_LOG_I(TAG, "End read file"); - while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) { - furi_delay_ms(5); - } - FURI_LOG_I(TAG, "End transmission"); - while(instance->worker_running) { - if(instance->worker_stoping) { - if(instance->callback_end) instance->callback_end(instance->context_end); - } - furi_delay_ms(50); - } - flipper_format_file_close(instance->flipper_format); - - FURI_LOG_I(TAG, "Worker stop"); - return 0; -} - -SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc() { - SubGhzFileEncoderWorker* instance = malloc(sizeof(SubGhzFileEncoderWorker)); - - instance->thread = - furi_thread_alloc_ex("SubGhzFEWorker", 2048, subghz_file_encoder_worker_thread, instance); - instance->stream = furi_stream_buffer_alloc(sizeof(int32_t) * 2048, sizeof(int32_t)); - - instance->storage = furi_record_open(RECORD_STORAGE); - instance->flipper_format = flipper_format_file_alloc(instance->storage); - - instance->str_data = furi_string_alloc(); - instance->file_path = furi_string_alloc(); - instance->level = false; - instance->worker_stoping = true; - - return instance; -} - -void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance) { - furi_assert(instance); - - furi_stream_buffer_free(instance->stream); - furi_thread_free(instance->thread); - - furi_string_free(instance->str_data); - furi_string_free(instance->file_path); - - flipper_format_free(instance->flipper_format); - furi_record_close(RECORD_STORAGE); - - free(instance); -} - -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path) { - furi_assert(instance); - furi_assert(!instance->worker_running); - - furi_stream_buffer_reset(instance->stream); - furi_string_set(instance->file_path, file_path); - instance->worker_running = true; - furi_thread_start(instance->thread); - - return true; -} - -void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - furi_thread_join(instance->thread); -} - -bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance) { - furi_assert(instance); - return instance->worker_running; -} diff --git a/applications/main/subghz/subghz_file_encoder_worker.h b/applications/main/subghz/subghz_file_encoder_worker.h deleted file mode 100644 index 19a46f1e6..000000000 --- a/applications/main/subghz/subghz_file_encoder_worker.h +++ /dev/null @@ -1,65 +0,0 @@ -#pragma once - -#include - -typedef void (*SubGhzFileEncoderWorkerCallbackEnd)(void* context); - -typedef struct SubGhzFileEncoderWorker SubGhzFileEncoderWorker; - -/** - * End callback SubGhzWorker. - * @param instance SubGhzFileEncoderWorker instance - * @param callback SubGhzFileEncoderWorkerCallbackEnd callback - */ -void subghz_file_encoder_worker_callback_end( - SubGhzFileEncoderWorker* instance, - SubGhzFileEncoderWorkerCallbackEnd callback_end, - void* context_end); - -/** - * Allocate SubGhzFileEncoderWorker. - * @return SubGhzFileEncoderWorker* pointer to a SubGhzFileEncoderWorker instance - */ -SubGhzFileEncoderWorker* subghz_file_encoder_worker_alloc(); - -/** - * Free SubGhzFileEncoderWorker. - * @param instance Pointer to a SubGhzFileEncoderWorker instance - */ -void subghz_file_encoder_worker_free(SubGhzFileEncoderWorker* instance); - -/** - * Get a description of the progress. - * @param instance Pointer to a SubGhzFileEncoderWorker instance - * @param output - */ -void subghz_file_encoder_worker_get_text_progress( - SubGhzFileEncoderWorker* instance, - FuriString* output); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzFileEncoderWorker instance - * @return LevelDuration - */ -LevelDuration subghz_file_encoder_worker_get_level_duration(void* context); - -/** - * Start SubGhzFileEncoderWorker. - * @param instance Pointer to a SubGhzFileEncoderWorker instance - * @return bool - true if ok - */ -bool subghz_file_encoder_worker_start(SubGhzFileEncoderWorker* instance, const char* file_path); - -/** - * Stop SubGhzFileEncoderWorker - * @param instance Pointer to a SubGhzFileEncoderWorker instance - */ -void subghz_file_encoder_worker_stop(SubGhzFileEncoderWorker* instance); - -/** - * Check if worker is running - * @param instance Pointer to a SubGhzFileEncoderWorker instance - * @return bool - true if running - */ -bool subghz_file_encoder_worker_is_running(SubGhzFileEncoderWorker* instance); diff --git a/applications/main/subghz/subghz_keystore.c b/applications/main/subghz/subghz_keystore.c deleted file mode 100644 index e0b1cf6ca..000000000 --- a/applications/main/subghz/subghz_keystore.c +++ /dev/null @@ -1,613 +0,0 @@ -#include "subghz_keystore.h" - -#include -#include - -#include -#include -#include -#include -#include - -#define TAG "SubGhzKeystore" - -#define FILE_BUFFER_SIZE 64 - -#define SUBGHZ_KEYSTORE_FILE_TYPE "Flipper SubGhz Keystore File" -#define SUBGHZ_KEYSTORE_FILE_RAW_TYPE "Flipper SubGhz Keystore RAW File" -#define SUBGHZ_KEYSTORE_FILE_VERSION 0 - -#define SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT 1 -#define SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE 512 -#define SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE (SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE * 2) - -typedef enum { - SubGhzKeystoreEncryptionNone, - SubGhzKeystoreEncryptionAES256, -} SubGhzKeystoreEncryption; - -struct SubGhzKeystore { - SubGhzKeyArray_t data; -}; - -SubGhzKeystore* subghz_keystore_alloc() { - SubGhzKeystore* instance = malloc(sizeof(SubGhzKeystore)); - - SubGhzKeyArray_init(instance->data); - - return instance; -} - -void subghz_keystore_free(SubGhzKeystore* instance) { - furi_assert(instance); - - for - M_EACH(manufacture_code, instance->data, SubGhzKeyArray_t) { - furi_string_free(manufacture_code->name); - manufacture_code->key = 0; - } - SubGhzKeyArray_clear(instance->data); - - free(instance); -} - -static void subghz_keystore_add_key( - SubGhzKeystore* instance, - const char* name, - uint64_t key, - uint16_t type) { - SubGhzKey* manufacture_code = SubGhzKeyArray_push_raw(instance->data); - manufacture_code->name = furi_string_alloc_set(name); - manufacture_code->key = key; - manufacture_code->type = type; -} - -static bool subghz_keystore_process_line(SubGhzKeystore* instance, char* line) { - uint64_t key = 0; - uint16_t type = 0; - char skey[17] = {0}; - char name[65] = {0}; - int ret = sscanf(line, "%16s:%hu:%64s", skey, &type, name); - key = strtoull(skey, NULL, 16); - if(ret == 3) { - subghz_keystore_add_key(instance, name, key, type); - return true; - } else { - FURI_LOG_E(TAG, "Failed to load line: %s\r\n", line); - return false; - } -} - -static void subghz_keystore_mess_with_iv(uint8_t* iv) { - // Alignment check for `ldrd` instruction - furi_assert(((uint32_t)iv) % 4 == 0); - // Please do not share decrypted manufacture keys - // Sharing them will bring some discomfort to legal owners - // And potential legal action against you - // While you reading this code think about your own personal responsibility - asm volatile("nani%=: \n" - "ldrd r0, r2, [%0, #0x0] \n" - "lsl r1, r0, #8 \n" - "lsl r3, r2, #8 \n" - "orr r3, r3, r0, lsr #24\n" - "uadd8 r1, r1, r0 \n" - "uadd8 r3, r3, r2 \n" - "strd r1, r3, [%0, #0x0] \n" - "ldrd r1, r3, [%0, #0x8] \n" - "lsl r0, r1, #8 \n" - "orr r0, r0, r2, lsr #24\n" - "lsl r2, r3, #8 \n" - "orr r2, r2, r1, lsr #24\n" - "uadd8 r1, r1, r0 \n" - "uadd8 r3, r3, r2 \n" - "strd r1, r3, [%0, #0x8] \n" - : - : "r"(iv) - : "r0", "r1", "r2", "r3", "memory"); -} - -static bool subghz_keystore_read_file(SubGhzKeystore* instance, Stream* stream, uint8_t* iv) { - bool result = true; - uint8_t buffer[FILE_BUFFER_SIZE]; - - char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - size_t encrypted_line_cursor = 0; - - do { - 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; - } - } - - 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, "Invalid encrypted data: %s", encrypted_line); - } - } 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; - } - } - } - } while(ret > 0 && result); - - if(iv) furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); - } while(false); - - free(encrypted_line); - free(decrypted_line); - - return result; -} - -bool subghz_keystore_load(SubGhzKeystore* instance, const char* file_name) { - furi_assert(instance); - bool result = false; - uint8_t iv[16]; - uint32_t version; - uint32_t encryption; - - FuriString* filetype; - filetype = furi_string_alloc(); - - FURI_LOG_I(TAG, "Loading keystore %s", file_name); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - do { - if(!flipper_format_file_open_existing(flipper_format, file_name)) { - FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); - break; - } - if(!flipper_format_read_header(flipper_format, filetype, &version)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { - FURI_LOG_E(TAG, "Missing encryption type"); - break; - } - - if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_TYPE) != 0 || - version != SUBGHZ_KEYSTORE_FILE_VERSION) { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - Stream* stream = flipper_format_get_raw_stream(flipper_format); - if(encryption == SubGhzKeystoreEncryptionNone) { - result = subghz_keystore_read_file(instance, stream, NULL); - } else if(encryption == SubGhzKeystoreEncryptionAES256) { - if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { - FURI_LOG_E(TAG, "Missing IV"); - break; - } - subghz_keystore_mess_with_iv(iv); - result = subghz_keystore_read_file(instance, stream, iv); - } else { - FURI_LOG_E(TAG, "Unknown encryption"); - break; - } - } while(0); - flipper_format_free(flipper_format); - - furi_record_close(RECORD_STORAGE); - - furi_string_free(filetype); - - return result; -} - -bool subghz_keystore_save(SubGhzKeystore* instance, const char* file_name, uint8_t* iv) { - furi_assert(instance); - bool result = false; - - Storage* storage = furi_record_open(RECORD_STORAGE); - char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - do { - if(!flipper_format_file_open_always(flipper_format, file_name)) { - FURI_LOG_E(TAG, "Unable to open file for write: %s", file_name); - break; - } - if(!flipper_format_write_header_cstr( - flipper_format, SUBGHZ_KEYSTORE_FILE_TYPE, SUBGHZ_KEYSTORE_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - uint32_t encryption = SubGhzKeystoreEncryptionAES256; - if(!flipper_format_write_uint32(flipper_format, "Encryption", &encryption, 1)) { - FURI_LOG_E(TAG, "Unable to add Encryption"); - break; - } - if(!flipper_format_write_hex(flipper_format, "IV", iv, 16)) { - FURI_LOG_E(TAG, "Unable to add IV"); - break; - } - - subghz_keystore_mess_with_iv(iv); - - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { - FURI_LOG_E(TAG, "Unable to load encryption key"); - break; - } - - Stream* stream = flipper_format_get_raw_stream(flipper_format); - size_t encrypted_line_count = 0; - for - M_EACH(key, instance->data, SubGhzKeyArray_t) { - // Wipe buffer before packing - memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - // Form unecreypted line - int len = snprintf( - decrypted_line, - SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE, - "%08lX%08lX:%hu:%s", - (uint32_t)(key->key >> 32), - (uint32_t)key->key, - key->type, - furi_string_get_cstr(key->name)); - // Verify length and align - furi_assert(len > 0); - if(len % 16 != 0) { - len += (16 - len % 16); - } - furi_assert(len % 16 == 0); - furi_assert(len <= SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - // Form encrypted line - if(!furi_hal_crypto_encrypt( - (uint8_t*)decrypted_line, (uint8_t*)encrypted_line, len)) { - FURI_LOG_E(TAG, "Encryption failed"); - break; - } - // HEX Encode encrypted line - const char xx[] = "0123456789ABCDEF"; - for(int i = 0; i < len; i++) { - size_t cursor = len - i - 1; - size_t hex_cursor = len * 2 - i * 2 - 1; - encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; - encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; - } - stream_write_cstring(stream, encrypted_line); - stream_write_char(stream, '\n'); - encrypted_line_count++; - } - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); - size_t total_keys = SubGhzKeyArray_size(instance->data); - result = encrypted_line_count == total_keys; - if(result) { - FURI_LOG_I(TAG, "Success. Encrypted: %zu of %zu", encrypted_line_count, total_keys); - } else { - FURI_LOG_E(TAG, "Failure. Encrypted: %zu of %zu", encrypted_line_count, total_keys); - } - } while(0); - flipper_format_free(flipper_format); - - free(encrypted_line); - free(decrypted_line); - furi_record_close(RECORD_STORAGE); - - return result; -} - -SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance) { - furi_assert(instance); - return &instance->data; -} - -bool subghz_keystore_raw_encrypted_save( - const char* input_file_name, - const char* output_file_name, - uint8_t* iv) { - bool encrypted = false; - uint32_t version; - uint32_t encryption; - FuriString* filetype; - filetype = furi_string_alloc(); - - Storage* storage = furi_record_open(RECORD_STORAGE); - - char* encrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - - FlipperFormat* input_flipper_format = flipper_format_file_alloc(storage); - do { - if(!flipper_format_file_open_existing(input_flipper_format, input_file_name)) { - FURI_LOG_E(TAG, "Unable to open file for read: %s", input_file_name); - break; - } - if(!flipper_format_read_header(input_flipper_format, filetype, &version)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - if(!flipper_format_read_uint32( - input_flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { - FURI_LOG_E(TAG, "Missing encryption type"); - break; - } - - if(strcmp(furi_string_get_cstr(filetype), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || - version != SUBGHZ_KEYSTORE_FILE_VERSION) { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - if(encryption != SubGhzKeystoreEncryptionNone) { - FURI_LOG_E(TAG, "Already encryption"); - break; - } - Stream* input_stream = flipper_format_get_raw_stream(input_flipper_format); - - FlipperFormat* output_flipper_format = flipper_format_file_alloc(storage); - - if(!flipper_format_file_open_always(output_flipper_format, output_file_name)) { - FURI_LOG_E(TAG, "Unable to open file for write: %s", output_file_name); - break; - } - if(!flipper_format_write_header_cstr( - output_flipper_format, - furi_string_get_cstr(filetype), - SUBGHZ_KEYSTORE_FILE_VERSION)) { - FURI_LOG_E(TAG, "Unable to add header"); - break; - } - uint32_t encryption = SubGhzKeystoreEncryptionAES256; - if(!flipper_format_write_uint32(output_flipper_format, "Encryption", &encryption, 1)) { - FURI_LOG_E(TAG, "Unable to add Encryption"); - break; - } - if(!flipper_format_write_hex(output_flipper_format, "IV", iv, 16)) { - FURI_LOG_E(TAG, "Unable to add IV"); - break; - } - - if(!flipper_format_write_string_cstr(output_flipper_format, "Encrypt_data", "RAW")) { - FURI_LOG_E(TAG, "Unable to add Encrypt_data"); - break; - } - - subghz_keystore_mess_with_iv(iv); - - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { - FURI_LOG_E(TAG, "Unable to load encryption key"); - break; - } - - Stream* output_stream = flipper_format_get_raw_stream(output_flipper_format); - uint8_t buffer[FILE_BUFFER_SIZE]; - bool result = true; - - size_t ret = 0; - furi_assert(FILE_BUFFER_SIZE % 16 == 0); - - //skip the end of the previous line "\n" - stream_read(input_stream, buffer, 1); - - do { - memset(buffer, 0, FILE_BUFFER_SIZE); - ret = stream_read(input_stream, buffer, FILE_BUFFER_SIZE); - if(ret == 0) { - break; - } - - for(uint16_t i = 0; i < FILE_BUFFER_SIZE - 1; i += 2) { - uint8_t hi_nibble = 0; - uint8_t lo_nibble = 0; - hex_char_to_hex_nibble(buffer[i], &hi_nibble); - hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); - buffer[i / 2] = (hi_nibble << 4) | lo_nibble; - } - - memset(encrypted_line, 0, SUBGHZ_KEYSTORE_FILE_ENCRYPTED_LINE_SIZE); - // Form encrypted line - if(!furi_hal_crypto_encrypt( - (uint8_t*)buffer, (uint8_t*)encrypted_line, FILE_BUFFER_SIZE / 2)) { - FURI_LOG_E(TAG, "Encryption failed"); - result = false; - break; - } - - // HEX Encode encrypted line - const char xx[] = "0123456789ABCDEF"; - for(size_t i = 0; i < FILE_BUFFER_SIZE / 2; i++) { - size_t cursor = FILE_BUFFER_SIZE / 2 - i - 1; - size_t hex_cursor = FILE_BUFFER_SIZE - i * 2 - 1; - encrypted_line[hex_cursor] = xx[encrypted_line[cursor] & 0xF]; - encrypted_line[hex_cursor - 1] = xx[(encrypted_line[cursor] >> 4) & 0xF]; - } - stream_write_cstring(output_stream, encrypted_line); - - } while(true); - - flipper_format_free(output_flipper_format); - - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); - - if(!result) break; - - encrypted = true; - } while(0); - - flipper_format_free(input_flipper_format); - - free(encrypted_line); - - furi_record_close(RECORD_STORAGE); - - return encrypted; -} - -bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len) { - bool result = false; - uint8_t iv[16]; - uint32_t version; - uint32_t encryption; - - FuriString* str_temp; - str_temp = furi_string_alloc(); - - Storage* storage = furi_record_open(RECORD_STORAGE); - char* decrypted_line = malloc(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - - FlipperFormat* flipper_format = flipper_format_file_alloc(storage); - do { - if(!flipper_format_file_open_existing(flipper_format, file_name)) { - FURI_LOG_E(TAG, "Unable to open file for read: %s", file_name); - break; - } - if(!flipper_format_read_header(flipper_format, str_temp, &version)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - if(!flipper_format_read_uint32(flipper_format, "Encryption", (uint32_t*)&encryption, 1)) { - FURI_LOG_E(TAG, "Missing encryption type"); - break; - } - - if(strcmp(furi_string_get_cstr(str_temp), SUBGHZ_KEYSTORE_FILE_RAW_TYPE) != 0 || - version != SUBGHZ_KEYSTORE_FILE_VERSION) { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - Stream* stream = flipper_format_get_raw_stream(flipper_format); - if(encryption != SubGhzKeystoreEncryptionAES256) { - FURI_LOG_E(TAG, "Unknown encryption"); - break; - } - - if(offset < 16) { - if(!flipper_format_read_hex(flipper_format, "IV", iv, 16)) { - FURI_LOG_E(TAG, "Missing IV"); - break; - } - subghz_keystore_mess_with_iv(iv); - } - - if(!flipper_format_read_string(flipper_format, "Encrypt_data", str_temp)) { - FURI_LOG_E(TAG, "Missing Encrypt_data"); - break; - } - - size_t bufer_size; - if(len <= (16 - offset % 16)) { - bufer_size = 32; - } else { - bufer_size = (((len) / 16) + 2) * 32; - } - furi_assert(SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE >= bufer_size / 2); - - uint8_t buffer[bufer_size]; - size_t ret = 0; - bool decrypted = true; - //skip the end of the previous line "\n" - stream_read(stream, buffer, 1); - - size_t size = stream_size(stream); - size -= stream_tell(stream); - if(size < (offset * 2 + len * 2)) { - FURI_LOG_E(TAG, "Seek position exceeds file size"); - break; - } - - if(offset >= 16) { - stream_seek(stream, ((offset / 16) - 1) * 32, StreamOffsetFromCurrent); - ret = stream_read(stream, buffer, 32); - furi_assert(ret == 32); - for(uint16_t i = 0; i < ret - 1; i += 2) { - uint8_t hi_nibble = 0; - uint8_t lo_nibble = 0; - hex_char_to_hex_nibble(buffer[i], &hi_nibble); - hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); - iv[i / 2] = (hi_nibble << 4) | lo_nibble; - } - } - - if(!furi_hal_crypto_store_load_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT, iv)) { - FURI_LOG_E(TAG, "Unable to load encryption key"); - break; - } - - do { - memset(buffer, 0, bufer_size); - ret = stream_read(stream, buffer, bufer_size); - furi_assert(ret == bufer_size); - for(uint16_t i = 0; i < ret - 1; i += 2) { - uint8_t hi_nibble = 0; - uint8_t lo_nibble = 0; - hex_char_to_hex_nibble(buffer[i], &hi_nibble); - hex_char_to_hex_nibble(buffer[i + 1], &lo_nibble); - buffer[i / 2] = (hi_nibble << 4) | lo_nibble; - } - - memset(decrypted_line, 0, SUBGHZ_KEYSTORE_FILE_DECRYPTED_LINE_SIZE); - - if(!furi_hal_crypto_decrypt( - (uint8_t*)buffer, (uint8_t*)decrypted_line, bufer_size / 2)) { - decrypted = false; - FURI_LOG_E(TAG, "Decryption failed"); - break; - } - memcpy(data, (uint8_t*)decrypted_line + (offset - (offset / 16) * 16), len); - - } while(0); - furi_hal_crypto_store_unload_key(SUBGHZ_KEYSTORE_FILE_ENCRYPTION_KEY_SLOT); - if(decrypted) result = true; - } while(0); - flipper_format_free(flipper_format); - - furi_record_close(RECORD_STORAGE); - - free(decrypted_line); - - furi_string_free(str_temp); - - return result; -} diff --git a/applications/main/subghz/subghz_keystore.h b/applications/main/subghz/subghz_keystore.h deleted file mode 100644 index 06ae8adae..000000000 --- a/applications/main/subghz/subghz_keystore.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - FuriString* name; - uint64_t key; - uint16_t type; -} SubGhzKey; - -ARRAY_DEF(SubGhzKeyArray, SubGhzKey, M_POD_OPLIST) - -#define M_OPL_SubGhzKeyArray_t() ARRAY_OPLIST(SubGhzKeyArray, M_POD_OPLIST) - -typedef struct SubGhzKeystore SubGhzKeystore; - -/** - * Allocate SubGhzKeystore. - * @return SubGhzKeystore* pointer to a SubGhzKeystore instance - */ -SubGhzKeystore* subghz_keystore_alloc(); - -/** - * Free SubGhzKeystore. - * @param instance Pointer to a SubGhzKeystore instance - */ -void subghz_keystore_free(SubGhzKeystore* instance); - -/** - * Loading manufacture key from file - * @param instance Pointer to a SubGhzKeystore instance - * @param filename Full path to the file - */ -bool subghz_keystore_load(SubGhzKeystore* instance, const char* filename); - -/** - * Save manufacture key to file - * @param instance Pointer to a SubGhzKeystore instance - * @param filename Full path to the file - * @return true On success - */ -bool subghz_keystore_save(SubGhzKeystore* instance, const char* filename, uint8_t* iv); - -/** - * Get array of keys and names manufacture - * @param instance Pointer to a SubGhzKeystore instance - * @return SubGhzKeyArray_t* - */ -SubGhzKeyArray_t* subghz_keystore_get_data(SubGhzKeystore* instance); - -/** - * Save RAW encrypted to file - * @param input_file_name Full path to the input file - * @param output_file_name Full path to the output file - * @param iv IV, 16 bytes in hex - */ -bool subghz_keystore_raw_encrypted_save( - const char* input_file_name, - const char* output_file_name, - uint8_t* iv); - -/** - * Get decrypt RAW data to file - * @param file_name Full path to the input file - * @param offset Offset from the start of the RAW data - * @param data Returned array - * @param len Required data length - * @return true On success - */ -bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* data, size_t len); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/subghz_setting.c b/applications/main/subghz/subghz_setting.c deleted file mode 100644 index 35ba54a8a..000000000 --- a/applications/main/subghz/subghz_setting.c +++ /dev/null @@ -1,480 +0,0 @@ -#include "subghz_setting.h" -#include "types.h" -//#include "subghz_i.h" - -#include -#include -#include - -#define TAG "SubGhzSetting" - -#define SUBGHZ_SETTING_FILE_TYPE "Flipper SubGhz Setting File" -#define SUBGHZ_SETTING_FILE_VERSION 1 - -#define FREQUENCY_FLAG_DEFAULT (1 << 31) -#define FREQUENCY_MASK (0xFFFFFFFF ^ FREQUENCY_FLAG_DEFAULT) - -/* Default */ -static const uint32_t subghz_frequency_list[] = { - /* 300 - 348 */ - 300000000, - 302757000, - 303875000, - 304250000, - 307000000, - 307500000, - 307800000, - 309000000, - 310000000, - 312000000, - 312100000, - 312200000, - 313000000, - 313850000, - 314000000, - 314350000, - 314980000, - 315000000, - 318000000, - 330000000, - 345000000, - 348000000, - 350000000, - - /* 387 - 464 */ - 387000000, - 390000000, - 418000000, - 433075000, /* LPD433 first */ - 433220000, - 433420000, - 433657070, - 433889000, - 433920000 | FREQUENCY_FLAG_DEFAULT, /* LPD433 mid */ - 434075000, - 434176948, - 434190000, - 434390000, - 434420000, - 434620000, - 434775000, /* LPD433 last channels */ - 438900000, - 440175000, - 464000000, - - /* 779 - 928 */ - 779000000, - 868350000, - 868400000, - 868800000, - 868950000, - 906400000, - 915000000, - 925000000, - 928000000, - 0, -}; - -static const uint32_t subghz_hopper_frequency_list[] = { - 315000000, - 330000000, - 390000000, - 433420000, - 433920000, - 868350000, - 0, -}; - -typedef struct { - FuriString* custom_preset_name; - uint8_t* custom_preset_data; - size_t custom_preset_data_size; -} SubGhzSettingCustomPresetItem; - -ARRAY_DEF(SubGhzSettingCustomPresetItemArray, SubGhzSettingCustomPresetItem, M_POD_OPLIST) - -#define M_OPL_SubGhzSettingCustomPresetItemArray_t() \ - ARRAY_OPLIST(SubGhzSettingCustomPresetItemArray, M_POD_OPLIST) - -LIST_DEF(FrequencyList, uint32_t) - -#define M_OPL_FrequencyList_t() LIST_OPLIST(FrequencyList) - -typedef struct { - SubGhzSettingCustomPresetItemArray_t data; -} SubGhzSettingCustomPresetStruct; - -struct SubGhzSetting { - FrequencyList_t frequencies; - FrequencyList_t hopper_frequencies; - SubGhzSettingCustomPresetStruct* preset; -}; - -SubGhzSetting* subghz_setting_alloc(void) { - SubGhzSetting* instance = malloc(sizeof(SubGhzSetting)); - FrequencyList_init(instance->frequencies); - FrequencyList_init(instance->hopper_frequencies); - instance->preset = malloc(sizeof(SubGhzSettingCustomPresetStruct)); - SubGhzSettingCustomPresetItemArray_init(instance->preset->data); - return instance; -} - -static void subghz_setting_preset_reset(SubGhzSetting* instance) { - for - M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - furi_string_free(item->custom_preset_name); - free(item->custom_preset_data); - } - SubGhzSettingCustomPresetItemArray_reset(instance->preset->data); -} - -void subghz_setting_free(SubGhzSetting* instance) { - furi_assert(instance); - FrequencyList_clear(instance->frequencies); - FrequencyList_clear(instance->hopper_frequencies); - for - M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - furi_string_free(item->custom_preset_name); - free(item->custom_preset_data); - } - SubGhzSettingCustomPresetItemArray_clear(instance->preset->data); - free(instance->preset); - free(instance); -} - -static void subghz_setting_load_default_preset( - SubGhzSetting* instance, - const char* preset_name, - const uint8_t* preset_data, - const uint8_t preset_pa_table[8]) { - furi_assert(instance); - furi_assert(preset_data); - uint32_t preset_data_count = 0; - SubGhzSettingCustomPresetItem* item = - SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); - - item->custom_preset_name = furi_string_alloc(); - furi_string_set(item->custom_preset_name, preset_name); - - while(preset_data[preset_data_count]) { - preset_data_count += 2; - } - preset_data_count += 2; - item->custom_preset_data_size = sizeof(uint8_t) * preset_data_count + sizeof(uint8_t) * 8; - item->custom_preset_data = malloc(item->custom_preset_data_size); - //load preset register - memcpy(&item->custom_preset_data[0], &preset_data[0], preset_data_count); - //load pa table - memcpy(&item->custom_preset_data[preset_data_count], &preset_pa_table[0], 8); -} - -static void subghz_setting_load_default_region( - SubGhzSetting* instance, - const uint32_t frequencies[], - const uint32_t hopper_frequencies[]) { - furi_assert(instance); - - FrequencyList_reset(instance->frequencies); - FrequencyList_reset(instance->hopper_frequencies); - subghz_setting_preset_reset(instance); - - while(*frequencies) { - FrequencyList_push_back(instance->frequencies, *frequencies); - frequencies++; - } - - while(*hopper_frequencies) { - FrequencyList_push_back(instance->hopper_frequencies, *hopper_frequencies); - hopper_frequencies++; - } - - subghz_setting_load_default_preset( - instance, - "AM270", - (uint8_t*)furi_hal_subghz_preset_ook_270khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); - subghz_setting_load_default_preset( - instance, - "AM650", - (uint8_t*)furi_hal_subghz_preset_ook_650khz_async_regs, - furi_hal_subghz_preset_ook_async_patable); - subghz_setting_load_default_preset( - instance, - "FM238", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev2_38khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); - subghz_setting_load_default_preset( - instance, - "FM476", - (uint8_t*)furi_hal_subghz_preset_2fsk_dev47_6khz_async_regs, - furi_hal_subghz_preset_2fsk_async_patable); -} - -// Region check removed -void subghz_setting_load_default(SubGhzSetting* instance) { - subghz_setting_load_default_region( - instance, subghz_frequency_list, subghz_hopper_frequency_list); -} - -void subghz_setting_load(SubGhzSetting* instance, const char* file_path) { - furi_assert(instance); - - Storage* storage = furi_record_open(RECORD_STORAGE); - FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - - FuriString* temp_str; - temp_str = furi_string_alloc(); - uint32_t temp_data32; - bool temp_bool; - - subghz_setting_load_default(instance); - - if(file_path) { - do { - if(!flipper_format_file_open_existing(fff_data_file, file_path)) { - FURI_LOG_I(TAG, "File is not used %s", file_path); - break; - } - - if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) { - FURI_LOG_E(TAG, "Missing or incorrect header"); - break; - } - - if((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_SETTING_FILE_TYPE)) && - temp_data32 == SUBGHZ_SETTING_FILE_VERSION) { - } else { - FURI_LOG_E(TAG, "Type or version mismatch"); - break; - } - - // Standard frequencies (optional) - temp_bool = true; - flipper_format_read_bool(fff_data_file, "Add_standard_frequencies", &temp_bool, 1); - if(!temp_bool) { - FURI_LOG_I(TAG, "Removing standard frequencies"); - FrequencyList_reset(instance->frequencies); - FrequencyList_reset(instance->hopper_frequencies); - } else { - FURI_LOG_I(TAG, "Keeping standard frequencies"); - } - - // Load frequencies - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - while(flipper_format_read_uint32( - fff_data_file, "Frequency", (uint32_t*)&temp_data32, 1)) { - if(furi_hal_subghz_is_frequency_valid(temp_data32)) { - FURI_LOG_I(TAG, "Frequency loaded %lu", temp_data32); - FrequencyList_push_back(instance->frequencies, temp_data32); - } else { - FURI_LOG_E(TAG, "Frequency not supported %lu", temp_data32); - } - } - - // Load hopper frequencies - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - while(flipper_format_read_uint32( - fff_data_file, "Hopper_frequency", (uint32_t*)&temp_data32, 1)) { - if(furi_hal_subghz_is_frequency_valid(temp_data32)) { - FURI_LOG_I(TAG, "Hopper frequency loaded %lu", temp_data32); - FrequencyList_push_back(instance->hopper_frequencies, temp_data32); - } else { - FURI_LOG_E(TAG, "Hopper frequency not supported %lu", temp_data32); - } - } - - // Default frequency (optional) - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - if(flipper_format_read_uint32(fff_data_file, "Default_frequency", &temp_data32, 1)) { - subghz_setting_set_default_frequency(instance, temp_data32); - } - - // custom preset (optional) - if(!flipper_format_rewind(fff_data_file)) { - FURI_LOG_E(TAG, "Rewind error"); - break; - } - while(flipper_format_read_string(fff_data_file, "Custom_preset_name", temp_str)) { - FURI_LOG_I(TAG, "Custom preset loaded %s", furi_string_get_cstr(temp_str)); - subghz_setting_load_custom_preset( - instance, furi_string_get_cstr(temp_str), fff_data_file); - } - - } while(false); - } - - furi_string_free(temp_str); - flipper_format_free(fff_data_file); - furi_record_close(RECORD_STORAGE); - - if(!FrequencyList_size(instance->frequencies) || - !FrequencyList_size(instance->hopper_frequencies)) { - FURI_LOG_E(TAG, "Error loading user settings, loading default settings"); - subghz_setting_load_default(instance); - } -} - -void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup) { - for - M_EACH(frequency, instance->frequencies, FrequencyList_t) { - *frequency &= FREQUENCY_MASK; - if(*frequency == frequency_to_setup) { - *frequency |= FREQUENCY_FLAG_DEFAULT; - } - } -} - -size_t subghz_setting_get_frequency_count(SubGhzSetting* instance) { - furi_assert(instance); - return FrequencyList_size(instance->frequencies); -} - -size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance) { - furi_assert(instance); - return FrequencyList_size(instance->hopper_frequencies); -} - -size_t subghz_setting_get_preset_count(SubGhzSetting* instance) { - furi_assert(instance); - return SubGhzSettingCustomPresetItemArray_size(instance->preset->data); -} - -const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx) { - furi_assert(instance); - if(idx >= SubGhzSettingCustomPresetItemArray_size(instance->preset->data)) { - idx = 0; - } - SubGhzSettingCustomPresetItem* item = - SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); - return furi_string_get_cstr(item->custom_preset_name); -} - -int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name) { - furi_assert(instance); - size_t idx = 0; - for - M_EACH(item, instance->preset->data, SubGhzSettingCustomPresetItemArray_t) { - if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { - return idx; - } - idx++; - } - furi_crash("SubGhz: No name preset."); - return -1; -} - -bool subghz_setting_load_custom_preset( - SubGhzSetting* instance, - const char* preset_name, - FlipperFormat* fff_data_file) { - furi_assert(instance); - furi_assert(preset_name); - uint32_t temp_data32; - SubGhzSettingCustomPresetItem* item = - SubGhzSettingCustomPresetItemArray_push_raw(instance->preset->data); - item->custom_preset_name = furi_string_alloc(); - furi_string_set(item->custom_preset_name, preset_name); - do { - if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32)) - break; - if(!temp_data32 || (temp_data32 % 2)) { - FURI_LOG_E(TAG, "Integrity error Custom_preset_data"); - break; - } - item->custom_preset_data_size = sizeof(uint8_t) * temp_data32; - item->custom_preset_data = malloc(item->custom_preset_data_size); - if(!flipper_format_read_hex( - fff_data_file, - "Custom_preset_data", - item->custom_preset_data, - item->custom_preset_data_size)) { - FURI_LOG_E(TAG, "Missing Custom_preset_data"); - break; - } - return true; - } while(true); - return false; -} - -bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name) { - furi_assert(instance); - furi_assert(preset_name); - SubGhzSettingCustomPresetItemArray_it_t it; - SubGhzSettingCustomPresetItemArray_it_last(it, instance->preset->data); - while(!SubGhzSettingCustomPresetItemArray_end_p(it)) { - SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_ref(it); - if(strcmp(furi_string_get_cstr(item->custom_preset_name), preset_name) == 0) { - furi_string_free(item->custom_preset_name); - free(item->custom_preset_data); - SubGhzSettingCustomPresetItemArray_remove(instance->preset->data, it); - return true; - } - SubGhzSettingCustomPresetItemArray_previous(it); - } - return false; -} - -uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx) { - furi_assert(instance); - SubGhzSettingCustomPresetItem* item = - SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); - return item->custom_preset_data; -} - -size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx) { - furi_assert(instance); - SubGhzSettingCustomPresetItem* item = - SubGhzSettingCustomPresetItemArray_get(instance->preset->data, idx); - return item->custom_preset_data_size; -} - -uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name) { - furi_assert(instance); - SubGhzSettingCustomPresetItem* item = SubGhzSettingCustomPresetItemArray_get( - instance->preset->data, subghz_setting_get_inx_preset_by_name(instance, preset_name)); - return item->custom_preset_data; -} - -uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx) { - furi_assert(instance); - if(idx < FrequencyList_size(instance->frequencies)) { - return (*FrequencyList_get(instance->frequencies, idx)) & FREQUENCY_MASK; - } else { - return 0; - } -} - -uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx) { - furi_assert(instance); - if(idx < FrequencyList_size(instance->frequencies)) { - return *FrequencyList_get(instance->hopper_frequencies, idx); - } else { - return 0; - } -} - -uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance) { - furi_assert(instance); - for(size_t i = 0; i < FrequencyList_size(instance->frequencies); i++) { - uint32_t frequency = *FrequencyList_get(instance->frequencies, i); - if(frequency & FREQUENCY_FLAG_DEFAULT) { - return i; - } - } - return 0; -} - -uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance) { - furi_assert(instance); - return subghz_setting_get_frequency( - instance, subghz_setting_get_frequency_default_index(instance)); -} diff --git a/applications/main/subghz/subghz_setting.h b/applications/main/subghz/subghz_setting.h deleted file mode 100644 index 3cb07ff6d..000000000 --- a/applications/main/subghz/subghz_setting.h +++ /dev/null @@ -1,58 +0,0 @@ - -#pragma once - -#include -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#define SUBGHZ_SETTING_DEFAULT_PRESET_COUNT 4 - -typedef struct SubGhzSetting SubGhzSetting; - -SubGhzSetting* subghz_setting_alloc(void); - -void subghz_setting_free(SubGhzSetting* instance); - -void subghz_setting_load(SubGhzSetting* instance, const char* file_path); - -size_t subghz_setting_get_frequency_count(SubGhzSetting* instance); - -size_t subghz_setting_get_hopper_frequency_count(SubGhzSetting* instance); - -size_t subghz_setting_get_preset_count(SubGhzSetting* instance); - -const char* subghz_setting_get_preset_name(SubGhzSetting* instance, size_t idx); - -int subghz_setting_get_inx_preset_by_name(SubGhzSetting* instance, const char* preset_name); - -uint8_t* subghz_setting_get_preset_data(SubGhzSetting* instance, size_t idx); - -size_t subghz_setting_get_preset_data_size(SubGhzSetting* instance, size_t idx); - -uint8_t* subghz_setting_get_preset_data_by_name(SubGhzSetting* instance, const char* preset_name); - -bool subghz_setting_load_custom_preset( - SubGhzSetting* instance, - const char* preset_name, - FlipperFormat* fff_data_file); - -bool subghz_setting_delete_custom_preset(SubGhzSetting* instance, const char* preset_name); - -uint32_t subghz_setting_get_frequency(SubGhzSetting* instance, size_t idx); - -uint32_t subghz_setting_get_hopper_frequency(SubGhzSetting* instance, size_t idx); - -uint32_t subghz_setting_get_frequency_default_index(SubGhzSetting* instance); - -uint32_t subghz_setting_get_default_frequency(SubGhzSetting* instance); - -void subghz_setting_set_default_frequency(SubGhzSetting* instance, uint32_t frequency_to_setup); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/subghz_tx_rx_worker.c b/applications/main/subghz/subghz_tx_rx_worker.c deleted file mode 100644 index e380fc967..000000000 --- a/applications/main/subghz/subghz_tx_rx_worker.c +++ /dev/null @@ -1,260 +0,0 @@ -#include "subghz_tx_rx_worker.h" - -#include - -#define TAG "SubGhzTxRxWorker" - -#define SUBGHZ_TXRX_WORKER_BUF_SIZE 2048 -//you can not set more than 62 because it will not fit into the FIFO CC1101 -#define SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE 60 - -#define SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF 40 - -struct SubGhzTxRxWorker { - FuriThread* thread; - FuriStreamBuffer* stream_tx; - FuriStreamBuffer* stream_rx; - - volatile bool worker_running; - volatile bool worker_stoping; - - SubGhzTxRxWorkerStatus status; - - uint32_t frequency; - - SubGhzTxRxWorkerCallbackHaveRead callback_have_read; - void* context_have_read; -}; - -bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { - furi_assert(instance); - bool ret = false; - size_t stream_tx_free_byte = furi_stream_buffer_spaces_available(instance->stream_tx); - if(size && (stream_tx_free_byte >= size)) { - if(furi_stream_buffer_send( - instance->stream_tx, data, size, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF) == - size) { - ret = true; - } - } - return ret; -} - -size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance) { - furi_assert(instance); - return furi_stream_buffer_bytes_available(instance->stream_rx); -} - -size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { - furi_assert(instance); - return furi_stream_buffer_receive(instance->stream_rx, data, size, 0); -} - -void subghz_tx_rx_worker_set_callback_have_read( - SubGhzTxRxWorker* instance, - SubGhzTxRxWorkerCallbackHaveRead callback, - void* context) { - furi_assert(instance); - furi_assert(callback); - furi_assert(context); - instance->callback_have_read = callback; - instance->context_have_read = context; -} - -bool subghz_tx_rx_worker_rx(SubGhzTxRxWorker* instance, uint8_t* data, uint8_t* size) { - uint8_t timeout = 100; - bool ret = false; - if(instance->status != SubGhzTxRxWorkerStatusRx) { - furi_hal_subghz_rx(); - instance->status = SubGhzTxRxWorkerStatusRx; - furi_delay_tick(1); - } - //waiting for reception to complete - while(furi_hal_gpio_read(furi_hal_subghz.cc1101_g0_pin)) { - furi_delay_tick(1); - if(!--timeout) { - FURI_LOG_W(TAG, "RX cc1101_g0 timeout"); - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); - break; - } - } - - if(furi_hal_subghz_rx_pipe_not_empty()) { - FURI_LOG_I( - TAG, - "RSSI: %03.1fdbm LQI: %d", - (double)furi_hal_subghz_get_rssi(), - furi_hal_subghz_get_lqi()); - if(furi_hal_subghz_is_rx_data_crc_valid()) { - furi_hal_subghz_read_packet(data, size); - ret = true; - } - furi_hal_subghz_flush_rx(); - furi_hal_subghz_rx(); - } - return ret; -} - -void subghz_tx_rx_worker_tx(SubGhzTxRxWorker* instance, uint8_t* data, size_t size) { - uint8_t timeout = 200; - if(instance->status != SubGhzTxRxWorkerStatusIDLE) { - furi_hal_subghz_idle(); - } - furi_hal_subghz_write_packet(data, size); - furi_hal_subghz_tx(); //start send - instance->status = SubGhzTxRxWorkerStatusTx; - while(!furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be set -> sync transmitted - furi_delay_tick(1); - if(!--timeout) { - FURI_LOG_W(TAG, "TX !cc1101_g0 timeout"); - break; - } - } - while(furi_hal_gpio_read( - furi_hal_subghz.cc1101_g0_pin)) { // Wait for GDO0 to be cleared -> end of packet - furi_delay_tick(1); - if(!--timeout) { - FURI_LOG_W(TAG, "TX cc1101_g0 timeout"); - break; - } - } - furi_hal_subghz_idle(); - instance->status = SubGhzTxRxWorkerStatusIDLE; -} -/** Worker thread - * - * @param context - * @return exit code - */ -static int32_t subghz_tx_rx_worker_thread(void* context) { - SubGhzTxRxWorker* instance = context; - FURI_LOG_I(TAG, "Worker start"); - - furi_hal_subghz_reset(); - furi_hal_subghz_idle(); - furi_hal_subghz_load_preset(FuriHalSubGhzPresetGFSK9_99KbAsync); - //furi_hal_subghz_load_preset(FuriHalSubGhzPresetMSK99_97KbAsync); - furi_hal_gpio_init(furi_hal_subghz.cc1101_g0_pin, GpioModeInput, GpioPullNo, GpioSpeedLow); - - furi_hal_subghz_set_frequency_and_path(instance->frequency); - furi_hal_subghz_flush_rx(); - - uint8_t data[SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE + 1] = {0}; - size_t size_tx = 0; - uint8_t size_rx[1] = {0}; - uint8_t timeout_tx = 0; - bool callback_rx = false; - - while(instance->worker_running) { - //transmit - size_tx = furi_stream_buffer_bytes_available(instance->stream_tx); - if(size_tx > 0 && !timeout_tx) { - timeout_tx = 10; //20ms - if(size_tx > SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE) { - furi_stream_buffer_receive( - instance->stream_tx, - &data, - SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE, - SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); - subghz_tx_rx_worker_tx(instance, data, SUBGHZ_TXRX_WORKER_MAX_TXRX_SIZE); - } else { - //todo checking that he managed to write all the data to the TX buffer - furi_stream_buffer_receive( - instance->stream_tx, &data, size_tx, SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); - subghz_tx_rx_worker_tx(instance, data, size_tx); - } - } else { - //recive - if(subghz_tx_rx_worker_rx(instance, data, size_rx)) { - if(furi_stream_buffer_spaces_available(instance->stream_rx) >= size_rx[0]) { - if(instance->callback_have_read && - furi_stream_buffer_bytes_available(instance->stream_rx) == 0) { - callback_rx = true; - } - //todo checking that he managed to write all the data to the RX buffer - furi_stream_buffer_send( - instance->stream_rx, - &data, - size_rx[0], - SUBGHZ_TXRX_WORKER_TIMEOUT_READ_WRITE_BUF); - if(callback_rx) { - instance->callback_have_read(instance->context_have_read); - callback_rx = false; - } - } else { - //todo RX buffer overflow - } - } - } - - if(timeout_tx) timeout_tx--; - furi_delay_tick(1); - } - - furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate); - furi_hal_subghz_sleep(); - - FURI_LOG_I(TAG, "Worker stop"); - return 0; -} - -SubGhzTxRxWorker* subghz_tx_rx_worker_alloc() { - SubGhzTxRxWorker* instance = malloc(sizeof(SubGhzTxRxWorker)); - - instance->thread = - furi_thread_alloc_ex("SubGhzTxRxWorker", 2048, subghz_tx_rx_worker_thread, instance); - instance->stream_tx = - furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); - instance->stream_rx = - furi_stream_buffer_alloc(sizeof(uint8_t) * SUBGHZ_TXRX_WORKER_BUF_SIZE, sizeof(uint8_t)); - - instance->status = SubGhzTxRxWorkerStatusIDLE; - instance->worker_stoping = true; - - return instance; -} - -void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance) { - furi_assert(instance); - furi_assert(!instance->worker_running); - furi_stream_buffer_free(instance->stream_tx); - furi_stream_buffer_free(instance->stream_rx); - furi_thread_free(instance->thread); - - free(instance); -} - -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency) { - furi_assert(instance); - furi_assert(!instance->worker_running); - bool res = false; - furi_stream_buffer_reset(instance->stream_tx); - furi_stream_buffer_reset(instance->stream_rx); - - instance->worker_running = true; - - if(furi_hal_subghz_is_tx_allowed(frequency)) { - instance->frequency = frequency; - res = true; - } - - furi_thread_start(instance->thread); - - return res; -} - -void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance) { - furi_assert(instance); - furi_assert(instance->worker_running); - - instance->worker_running = false; - - furi_thread_join(instance->thread); -} - -bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance) { - furi_assert(instance); - return instance->worker_running; -} diff --git a/applications/main/subghz/subghz_tx_rx_worker.h b/applications/main/subghz/subghz_tx_rx_worker.h deleted file mode 100644 index ddc02e749..000000000 --- a/applications/main/subghz/subghz_tx_rx_worker.h +++ /dev/null @@ -1,89 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef void (*SubGhzTxRxWorkerCallbackHaveRead)(void* context); - -typedef struct SubGhzTxRxWorker SubGhzTxRxWorker; - -typedef enum { - SubGhzTxRxWorkerStatusIDLE, - SubGhzTxRxWorkerStatusTx, - SubGhzTxRxWorkerStatusRx, -} SubGhzTxRxWorkerStatus; - -/** - * SubGhzTxRxWorker, add data to transfer - * @param instance Pointer to a SubGhzTxRxWorker instance - * @param data *data - * @param size data size - * @return bool true if ok - */ -bool subghz_tx_rx_worker_write(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); - -/** - * SubGhzTxRxWorker, get available data - * @param instance Pointer to a SubGhzTxRxWorker instance - * @return size_t data size - */ -size_t subghz_tx_rx_worker_available(SubGhzTxRxWorker* instance); - -/** - * SubGhzTxRxWorker, read data - * @param instance Pointer to a SubGhzTxRxWorker instance - * @param data *data - * @param size max data size, which can be read - * @return size_t data size, how much is actually read - */ -size_t subghz_tx_rx_worker_read(SubGhzTxRxWorker* instance, uint8_t* data, size_t size); - -/** - * Сallback SubGhzTxRxWorker when there is data to read in an empty buffer - * @param instance Pointer to a SubGhzTxRxWorker instance - * @param callback SubGhzTxRxWorkerCallbackHaveRead callback - * @param context - */ -void subghz_tx_rx_worker_set_callback_have_read( - SubGhzTxRxWorker* instance, - SubGhzTxRxWorkerCallbackHaveRead callback, - void* context); - -/** - * Allocate SubGhzTxRxWorker - * @return SubGhzTxRxWorker* Pointer to a SubGhzTxRxWorker instance - */ -SubGhzTxRxWorker* subghz_tx_rx_worker_alloc(); - -/** - * Free SubGhzTxRxWorker - * @param instance Pointer to a SubGhzTxRxWorker instance - */ -void subghz_tx_rx_worker_free(SubGhzTxRxWorker* instance); - -/** - * Start SubGhzTxRxWorker - * @param instance Pointer to a SubGhzTxRxWorker instance - * @return bool - true if ok - */ -bool subghz_tx_rx_worker_start(SubGhzTxRxWorker* instance, uint32_t frequency); - -/** - * Stop SubGhzTxRxWorker - * @param instance Pointer to a SubGhzTxRxWorker instance - */ -void subghz_tx_rx_worker_stop(SubGhzTxRxWorker* instance); - -/** - * Check if worker is running - * @param instance Pointer to a SubGhzTxRxWorker instance - * @return bool - true if running - */ -bool subghz_tx_rx_worker_is_running(SubGhzTxRxWorker* instance); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/subghz_worker.c b/applications/main/subghz/subghz_worker.c deleted file mode 100644 index 50b5aba51..000000000 --- a/applications/main/subghz/subghz_worker.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "subghz_worker.h" - -#include - -#define TAG "SubGhzWorker" - -struct SubGhzWorker { - FuriThread* thread; - FuriStreamBuffer* stream; - - volatile bool running; - volatile bool overrun; - - LevelDuration filter_level_duration; - uint16_t filter_duration; - - SubGhzWorkerOverrunCallback overrun_callback; - SubGhzWorkerPairCallback pair_callback; - void* context; -}; - -/** Rx callback timer - * - * @param level received signal level - * @param duration received signal duration - * @param context - */ -void subghz_worker_rx_callback(bool level, uint32_t duration, void* context) { - SubGhzWorker* instance = context; - - LevelDuration level_duration = level_duration_make(level, duration); - if(instance->overrun) { - instance->overrun = false; - level_duration = level_duration_reset(); - } - size_t ret = - furi_stream_buffer_send(instance->stream, &level_duration, sizeof(LevelDuration), 0); - if(sizeof(LevelDuration) != ret) instance->overrun = true; -} - -/** Worker callback thread - * - * @param context - * @return exit code - */ -static int32_t subghz_worker_thread_callback(void* context) { - SubGhzWorker* instance = context; - - LevelDuration level_duration; - while(instance->running) { - int ret = furi_stream_buffer_receive( - instance->stream, &level_duration, sizeof(LevelDuration), 10); - if(ret == sizeof(LevelDuration)) { - if(level_duration_is_reset(level_duration)) { - FURI_LOG_E(TAG, "Overrun buffer"); - if(instance->overrun_callback) instance->overrun_callback(instance->context); - } else { - bool level = level_duration_get_level(level_duration); - uint32_t duration = level_duration_get_duration(level_duration); - - if((duration < instance->filter_duration) || - (instance->filter_level_duration.level == level)) { - instance->filter_level_duration.duration += duration; - - } else if(instance->filter_level_duration.level != level) { - if(instance->pair_callback) - instance->pair_callback( - instance->context, - instance->filter_level_duration.level, - instance->filter_level_duration.duration); - - instance->filter_level_duration.duration = duration; - instance->filter_level_duration.level = level; - } - } - } - } - - return 0; -} - -SubGhzWorker* subghz_worker_alloc() { - SubGhzWorker* instance = malloc(sizeof(SubGhzWorker)); - - instance->thread = - furi_thread_alloc_ex("SubGhzWorker", 2048, subghz_worker_thread_callback, instance); - - instance->stream = - furi_stream_buffer_alloc(sizeof(LevelDuration) * 4096, sizeof(LevelDuration)); - - //setting default filter in us - instance->filter_duration = 30; - - return instance; -} - -void subghz_worker_free(SubGhzWorker* instance) { - furi_assert(instance); - - furi_stream_buffer_free(instance->stream); - furi_thread_free(instance->thread); - - free(instance); -} - -void subghz_worker_set_overrun_callback( - SubGhzWorker* instance, - SubGhzWorkerOverrunCallback callback) { - furi_assert(instance); - instance->overrun_callback = callback; -} - -void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback) { - furi_assert(instance); - instance->pair_callback = callback; -} - -void subghz_worker_set_context(SubGhzWorker* instance, void* context) { - furi_assert(instance); - instance->context = context; -} - -void subghz_worker_start(SubGhzWorker* instance) { - furi_assert(instance); - furi_assert(!instance->running); - - instance->running = true; - - furi_thread_start(instance->thread); -} - -void subghz_worker_stop(SubGhzWorker* instance) { - furi_assert(instance); - furi_assert(instance->running); - - instance->running = false; - - furi_thread_join(instance->thread); -} - -bool subghz_worker_is_running(SubGhzWorker* instance) { - furi_assert(instance); - return instance->running; -} - -void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout) { - furi_assert(instance); - instance->filter_duration = timeout; -} \ No newline at end of file diff --git a/applications/main/subghz/subghz_worker.h b/applications/main/subghz/subghz_worker.h deleted file mode 100644 index 657278f50..000000000 --- a/applications/main/subghz/subghz_worker.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzWorker SubGhzWorker; - -typedef void (*SubGhzWorkerOverrunCallback)(void* context); - -typedef void (*SubGhzWorkerPairCallback)(void* context, bool level, uint32_t duration); - -void subghz_worker_rx_callback(bool level, uint32_t duration, void* context); - -/** - * Allocate SubGhzWorker. - * @return SubGhzWorker* Pointer to a SubGhzWorker instance - */ -SubGhzWorker* subghz_worker_alloc(); - -/** - * Free SubGhzWorker. - * @param instance Pointer to a SubGhzWorker instance - */ -void subghz_worker_free(SubGhzWorker* instance); - -/** - * Overrun callback SubGhzWorker. - * @param instance Pointer to a SubGhzWorker instance - * @param callback SubGhzWorkerOverrunCallback callback - */ -void subghz_worker_set_overrun_callback( - SubGhzWorker* instance, - SubGhzWorkerOverrunCallback callback); - -/** - * Pair callback SubGhzWorker. - * @param instance Pointer to a SubGhzWorker instance - * @param callback SubGhzWorkerOverrunCallback callback - */ -void subghz_worker_set_pair_callback(SubGhzWorker* instance, SubGhzWorkerPairCallback callback); - -/** - * Context callback SubGhzWorker. - * @param instance Pointer to a SubGhzWorker instance - * @param context - */ -void subghz_worker_set_context(SubGhzWorker* instance, void* context); - -/** - * Start SubGhzWorker. - * @param instance Pointer to a SubGhzWorker instance - */ -void subghz_worker_start(SubGhzWorker* instance); - -/** Stop SubGhzWorker - * @param instance Pointer to a SubGhzWorker instance - */ -void subghz_worker_stop(SubGhzWorker* instance); - -/** - * Check if worker is running. - * @param instance Pointer to a SubGhzWorker instance - * @return bool - true if running - */ -bool subghz_worker_is_running(SubGhzWorker* instance); - -/** - * Short duration filter setting. - * glues short durations into 1. The default setting is 30 us, if set to 0 the filter will be disabled - * @param instance Pointer to a SubGhzWorker instance - * @param timeout time in us - */ -void subghz_worker_set_filter(SubGhzWorker* instance, uint16_t timeout); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/transmitter.c b/applications/main/subghz/transmitter.c deleted file mode 100644 index 8507ee054..000000000 --- a/applications/main/subghz/transmitter.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "transmitter.h" - -#include "protocols/base.h" -#include "registry.h" -#include "protocols/protocol_items.h" - -struct SubGhzTransmitter { - const SubGhzProtocol* protocol; - SubGhzProtocolEncoderBase* protocol_instance; -}; - -SubGhzTransmitter* - subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { - SubGhzTransmitter* instance = NULL; - const SubGhzProtocolRegistry* protocol_registry_items = - subghz_environment_get_protocol_registry(environment); - - const SubGhzProtocol* protocol = - subghz_protocol_registry_get_by_name(protocol_registry_items, protocol_name); - - if(protocol && protocol->encoder && protocol->encoder->alloc) { - instance = malloc(sizeof(SubGhzTransmitter)); - instance->protocol = protocol; - instance->protocol_instance = instance->protocol->encoder->alloc(environment); - } - return instance; -} - -void subghz_transmitter_free(SubGhzTransmitter* instance) { - furi_assert(instance); - instance->protocol->encoder->free(instance->protocol_instance); - free(instance); -} - -SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) { - furi_assert(instance); - return instance->protocol_instance; -} - -bool subghz_transmitter_stop(SubGhzTransmitter* instance) { - furi_assert(instance); - bool ret = false; - if(instance->protocol && instance->protocol->encoder && instance->protocol->encoder->stop) { - instance->protocol->encoder->stop(instance->protocol_instance); - ret = true; - } - return ret; -} - -bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format) { - furi_assert(instance); - bool ret = false; - if(instance->protocol && instance->protocol->encoder && - instance->protocol->encoder->deserialize) { - ret = - instance->protocol->encoder->deserialize(instance->protocol_instance, flipper_format); - } - return ret; -} - -LevelDuration subghz_transmitter_yield(void* context) { - SubGhzTransmitter* instance = context; - return instance->protocol->encoder->yield(instance->protocol_instance); -} diff --git a/applications/main/subghz/transmitter.h b/applications/main/subghz/transmitter.h deleted file mode 100644 index cce98a463..000000000 --- a/applications/main/subghz/transmitter.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include "types.h" -#include "environment.h" -#include "protocols/base.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct SubGhzTransmitter SubGhzTransmitter; - -/** - * Allocate and init SubGhzTransmitter. - * @param environment Pointer to a SubGhzEnvironment instance - * @return SubGhzTransmitter* pointer to a SubGhzTransmitter instance - */ -SubGhzTransmitter* - subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name); - -/** - * Free SubGhzTransmitter. - * @param instance Pointer to a SubGhzTransmitter instance - */ -void subghz_transmitter_free(SubGhzTransmitter* instance); - -/** Get protocol instance. - * @param instance Pointer to a SubGhzTransmitter instance - */ -SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance); - -/** - * Forced transmission stop. - * @param instance Pointer to a SubGhzTransmitter instance - */ -bool subghz_transmitter_stop(SubGhzTransmitter* instance); - -/** - * Deserialize and generating an upload to send. - * @param instance Pointer to a SubGhzTransmitter instance - * @param flipper_format Pointer to a FlipperFormat instance - * @return true On success - */ -bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* flipper_format); - -/** - * Getting the level and duration of the upload to be loaded into DMA. - * @param context Pointer to a SubGhzTransmitter instance - * @return LevelDuration - */ -LevelDuration subghz_transmitter_yield(void* context); - -#ifdef __cplusplus -} -#endif diff --git a/applications/main/subghz/types.h b/applications/main/subghz/types.h deleted file mode 100644 index 9d121dc3c..000000000 --- a/applications/main/subghz/types.h +++ /dev/null @@ -1,104 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -#include "environment.h" -#include -#include - -#define SUBGHZ_APP_FOLDER ANY_PATH("subghz") -#define SUBGHZ_RAW_FOLDER EXT_PATH("subghz") -#define SUBGHZ_APP_EXTENSION ".sub" - -#define SUBGHZ_KEY_FILE_VERSION 1 -#define SUBGHZ_KEY_FILE_TYPE "Flipper SubGhz Key File" - -#define SUBGHZ_RAW_FILE_VERSION 1 -#define SUBGHZ_RAW_FILE_TYPE "Flipper SubGhz RAW File" - -// Radio Preset -typedef struct { - FuriString* name; - uint32_t frequency; - uint8_t* data; - size_t data_size; -} SubGhzRadioPreset; - -// Allocator and Deallocator -typedef void* (*SubGhzAlloc)(SubGhzEnvironment* environment); -typedef void (*SubGhzFree)(void* context); - -// Serialize and Deserialize -typedef bool ( - *SubGhzSerialize)(void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset); -typedef bool (*SubGhzDeserialize)(void* context, FlipperFormat* flipper_format); - -// Decoder specific -typedef void (*SubGhzDecoderFeed)(void* decoder, bool level, uint32_t duration); -typedef void (*SubGhzDecoderReset)(void* decoder); -typedef uint8_t (*SubGhzGetHashData)(void* decoder); -typedef void (*SubGhzGetString)(void* decoder, FuriString* output); - -// Encoder specific -typedef void (*SubGhzEncoderStop)(void* encoder); -typedef LevelDuration (*SubGhzEncoderYield)(void* context); - -typedef struct { - SubGhzAlloc alloc; - SubGhzFree free; - - SubGhzDecoderFeed feed; - SubGhzDecoderReset reset; - - SubGhzGetHashData get_hash_data; - SubGhzGetString get_string; - SubGhzSerialize serialize; - SubGhzDeserialize deserialize; -} SubGhzProtocolDecoder; - -typedef struct { - SubGhzAlloc alloc; - SubGhzFree free; - - SubGhzDeserialize deserialize; - SubGhzEncoderStop stop; - SubGhzEncoderYield yield; -} SubGhzProtocolEncoder; - -typedef enum { - SubGhzProtocolTypeUnknown = 0, - SubGhzProtocolTypeStatic, - SubGhzProtocolTypeDynamic, - SubGhzProtocolTypeRAW, - SubGhzProtocolWeatherStation, - SubGhzProtocolCustom, - SubGhzProtocolTypeBinRAW, -} SubGhzProtocolType; - -typedef enum { - SubGhzProtocolFlag_RAW = (1 << 0), - SubGhzProtocolFlag_Decodable = (1 << 1), - SubGhzProtocolFlag_315 = (1 << 2), - SubGhzProtocolFlag_433 = (1 << 3), - SubGhzProtocolFlag_868 = (1 << 4), - SubGhzProtocolFlag_AM = (1 << 5), - SubGhzProtocolFlag_FM = (1 << 6), - SubGhzProtocolFlag_Save = (1 << 7), - SubGhzProtocolFlag_Load = (1 << 8), - SubGhzProtocolFlag_Send = (1 << 9), - SubGhzProtocolFlag_BinRAW = (1 << 10), -} SubGhzProtocolFlag; - -typedef struct { - const char* name; - SubGhzProtocolType type; - SubGhzProtocolFlag flag; - - const SubGhzProtocolEncoder* encoder; - const SubGhzProtocolDecoder* decoder; -} SubGhzProtocol; diff --git a/lib/subghz/subghz_keystore.h b/lib/subghz/subghz_keystore.h index 06ae8adae..43f8356d0 100644 --- a/lib/subghz/subghz_keystore.h +++ b/lib/subghz/subghz_keystore.h @@ -77,4 +77,4 @@ bool subghz_keystore_raw_get_data(const char* file_name, size_t offset, uint8_t* #ifdef __cplusplus } -#endif +#endif \ No newline at end of file From 0127b87b0da7cd068baaa453c53b7a352abfdf26 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 00:43:03 +0000 Subject: [PATCH 165/231] Keep SFW and NSFW asset packs first --- .../scenes/xtreme_settings_scene_start.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index ba5e65ca0..4e8c596cd 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -7,7 +7,7 @@ static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text( - item, index == 0 ? "OFF" : *asset_packs_get(app->asset_packs, index - 1)); + item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1)); strlcpy( XTREME_SETTINGS()->asset_pack, index == 0 ? "" : *asset_packs_get(app->asset_packs, index - 1), @@ -144,10 +144,12 @@ void xtreme_settings_scene_start_on_enter(void* context) { if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); - uint idx; - for(idx = 0; idx < asset_packs_size(app->asset_packs); idx++) { - if(strcasecmp(copy, *asset_packs_get(app->asset_packs, idx)) < 0) { - break; + uint idx = 0; + if(strcmp(copy, "NSFW") != 0) { + for(; idx < asset_packs_size(app->asset_packs); idx++) { + if(strcasecmp(copy, *asset_packs_get(app->asset_packs, idx)) < 0) { + break; + } } } asset_packs_push_at(app->asset_packs, idx, copy); @@ -171,7 +173,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { app); variable_item_set_current_value_index(item, current_pack); variable_item_set_current_value_text( - item, current_pack == 0 ? "OFF" : *asset_packs_get(app->asset_packs, current_pack - 1)); + item, current_pack == 0 ? "SFW" : *asset_packs_get(app->asset_packs, current_pack - 1)); item = variable_item_list_add( var_item_list, From 2db0cae428c3e2220f933e012e05a5aa3cc2fe60 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sat, 11 Feb 2023 04:35:13 +0100 Subject: [PATCH 166/231] clara wants her nightstand --- applications/services/desktop/scenes/desktop_scene_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/desktop/scenes/desktop_scene_main.c b/applications/services/desktop/scenes/desktop_scene_main.c index 343ecac18..d7fc63578 100644 --- a/applications/services/desktop/scenes/desktop_scene_main.c +++ b/applications/services/desktop/scenes/desktop_scene_main.c @@ -207,7 +207,7 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { break; } case DesktopMainEventOpenClock: { - desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Tools/Clock.fap")); + desktop_scene_main_open_app_or_profile(desktop, EXT_PATH("/apps/Misc/Nightstand.fap")); break; } case DesktopLockedEventUpdate: From ff90b9db8d01aa8e734ced40ee33332903cc49e2 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sat, 11 Feb 2023 04:41:31 +0100 Subject: [PATCH 167/231] Naming fucking bs, i swear --- applications/plugins/nightstand/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/plugins/nightstand/application.fam b/applications/plugins/nightstand/application.fam index cea02bd00..0b8cbf83a 100644 --- a/applications/plugins/nightstand/application.fam +++ b/applications/plugins/nightstand/application.fam @@ -1,5 +1,5 @@ App( - appid="NightstandClock", + appid="Nightstand", name="Nightstand Clock", apptype=FlipperAppType.EXTERNAL, entry_point="clock_app", From a90c6f44d7a903dcfda47c4f67ebdd04a54940f7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 04:25:57 +0000 Subject: [PATCH 168/231] Expose xtreme_settings api import for other apps --- applications/settings/application.fam | 1 - applications/settings/xtreme_settings/application.fam | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/applications/settings/application.fam b/applications/settings/application.fam index 0c4a98835..cc4b9703d 100644 --- a/applications/settings/application.fam +++ b/applications/settings/application.fam @@ -5,7 +5,6 @@ App( provides=[ "passport", "system_settings", - "xtreme_settings", "about", ], ) diff --git a/applications/settings/xtreme_settings/application.fam b/applications/settings/xtreme_settings/application.fam index 54dd95af3..79c0f62b4 100644 --- a/applications/settings/xtreme_settings/application.fam +++ b/applications/settings/xtreme_settings/application.fam @@ -8,4 +8,7 @@ App( "gui", ], order=90, + provides=[ + "xtreme_settings", + ], ) From 48cd89b9044d898e91c98593946abf9d0cf701bb Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 04:26:44 +0000 Subject: [PATCH 169/231] Setting name capitalization fix --- .../xtreme_settings/scenes/xtreme_settings_scene_start.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 4e8c596cd..7aec1269e 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -215,7 +215,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Sort dirs before", + "Sort Dirs Before", 2, xtreme_settings_scene_start_sort_folders_before_changed, app); From ae9e15604e3bb194825b11a873a9909edaee3823 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 04:30:26 +0000 Subject: [PATCH 170/231] Bring back status bar as xfw setting --- applications/services/gui/application.fam | 1 + applications/services/gui/gui.c | 154 +++++++++++++----- .../scenes/xtreme_settings_scene_start.c | 13 ++ .../xtreme_settings/xtreme_settings.h | 1 + 4 files changed, 126 insertions(+), 43 deletions(-) diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 193b9dfd3..43e93559e 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -7,6 +7,7 @@ App( requires=[ "input", "notification", + "xtreme_settings", ], stack_size=2 * 1024, order=70, diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 9495128f2..2d6098851 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,3 +1,4 @@ +#include "../../settings/xtreme_settings/xtreme_settings.h" #include "gui_i.h" #include @@ -54,17 +55,23 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_frame_set( gui->canvas, GUI_STATUS_BAR_X, GUI_STATUS_BAR_Y, GUI_DISPLAY_WIDTH, GUI_STATUS_BAR_HEIGHT); + XtremeSettings* xtreme_settings = XTREME_SETTINGS(); + /* for support black theme - paint white area and * draw icon with transparent white color */ - canvas_set_color(gui->canvas, ColorWhite); - // canvas_draw_box(gui->canvas, 1, 1, 9, 7); - // canvas_draw_box(gui->canvas, 7, 3, 58, 6); - // canvas_draw_box(gui->canvas, 61, 1, 32, 7); - // canvas_draw_box(gui->canvas, 89, 3, 38, 6); - canvas_set_color(gui->canvas, ColorBlack); - canvas_set_bitmap_mode(gui->canvas, 1); - // canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); + if(xtreme_settings->status_bar) { + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box(gui->canvas, 1, 1, 9, 7); + canvas_draw_box(gui->canvas, 7, 3, 58, 6); + canvas_draw_box(gui->canvas, 61, 1, 32, 7); + canvas_draw_box(gui->canvas, 89, 3, 38, 6); + canvas_set_color(gui->canvas, ColorBlack); + canvas_set_bitmap_mode(gui->canvas, 1); + canvas_draw_icon(gui->canvas, 0, 0, &I_Background_128x11); + } else { + canvas_set_color(gui->canvas, ColorBlack); + } canvas_set_bitmap_mode(gui->canvas, 0); // Right side @@ -85,13 +92,16 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { GUI_STATUS_BAR_Y + 1, width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - // canvas_set_color(gui->canvas, ColorWhite); - // canvas_draw_box( - // gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + // Hide battery background + if(xtreme_settings->status_bar) { + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); + } canvas_set_color(gui->canvas, ColorBlack); // ViewPort draw canvas_frame_set( - gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + gui->canvas, x - xtreme_settings->status_bar, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); view_port_draw(view_port, gui->canvas); } ViewPortArray_next(it); @@ -100,41 +110,99 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { if(right_used) { canvas_frame_set( gui->canvas, - GUI_DISPLAY_WIDTH - 3 - right_used, + GUI_DISPLAY_WIDTH - 4 - right_used, GUI_STATUS_BAR_Y, - right_used + 2, + right_used + 4, GUI_STATUS_BAR_HEIGHT); - // canvas_set_color(gui->canvas, ColorBlack); - // canvas_draw_rframe( - // gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas) - 1, 1); - // canvas_draw_line( - // gui->canvas, - // canvas_width(gui->canvas) - 1, - // 2, - // canvas_width(gui->canvas) - 1, - // canvas_height(gui->canvas) - 4); + // Disable battery border + if(xtreme_settings->status_bar) { + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } } - // Extra notification - if(need_attention) { - width = icon_get_width(&I_Hidden_window_9x8); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); - canvas_set_color(gui->canvas, ColorBlack); - // Draw Icon - canvas_frame_set( - gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); - canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8); - // Recalculate next position - left_used += (width + 2); - x += (width + 2); + // Left side + if(xtreme_settings->status_bar) { + x = 2; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]); + while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); + // Recalculate next position + left_used += (width + 2); + x += (width + 2); + } + ViewPortArray_next(it); + } + // Extra notification + if(need_attention || !need_attention) { + width = icon_get_width(&I_Hidden_window_9x8); + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_set_color(gui->canvas, ColorBlack); + // Draw Icon + canvas_frame_set( + gui->canvas, x, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + canvas_draw_icon(gui->canvas, 0, 0, &I_Hidden_window_9x8); + // Recalculate next position + left_used += (width + 2); + x += (width + 2); + } + // Draw frame around icons on the left + if(left_used) { + canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } } } diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c index 7aec1269e..40972a31a 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c @@ -78,6 +78,14 @@ static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item app->settings_changed = true; } +static void xtreme_settings_scene_start_status_bar_changed(VariableItem* item) { + XtremeSettingsApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->status_bar = value; + app->settings_changed = true; +} + static void xtreme_settings_scene_start_sort_folders_before_changed(VariableItem* item) { XtremeSettingsApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -213,6 +221,11 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, battery_style_names[value_index]); + item = variable_item_list_add( + var_item_list, "Status Bar", 2, xtreme_settings_scene_start_status_bar_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->status_bar); + variable_item_set_current_value_text(item, xtreme_settings->status_bar ? "ON" : "OFF"); + item = variable_item_list_add( var_item_list, "Sort Dirs Before", diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/settings/xtreme_settings/xtreme_settings.h index 13bb574ad..aacbdd92a 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/settings/xtreme_settings/xtreme_settings.h @@ -22,6 +22,7 @@ typedef struct { BatteryStyle battery_style; uint16_t anim_speed; bool sort_ignore_dirs; + bool status_bar; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); From 9cf68935e8cf8118becc32d55acb1481ca06b2a2 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 05:03:01 +0000 Subject: [PATCH 171/231] Remove testing code --- applications/services/gui/gui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 2d6098851..46baf453d 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -165,7 +165,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { ViewPortArray_next(it); } // Extra notification - if(need_attention || !need_attention) { + if(need_attention) { width = icon_get_width(&I_Hidden_window_9x8); // Prepare work area background canvas_frame_set( From efb11dfcbd0c27b5e76550af0529ed5ce78596cc Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 05:19:37 +0000 Subject: [PATCH 172/231] Properly integrate xtreme apis and headers --- .../main/archive/helpers/archive_files.h | 2 +- .../main/bad_usb/scenes/bad_usb_scene_error.c | 2 +- .../main/bad_usb/views/bad_usb_view.c | 2 +- .../scenes/ibutton_scene_delete_success.c | 2 +- .../main/ibutton/scenes/ibutton_scene_read.c | 2 +- .../scenes/ibutton_scene_save_success.c | 2 +- .../scenes/ibutton_scene_write_success.c | 2 +- .../scenes/infrared_scene_edit_rename_done.c | 2 +- .../scenes/infrared_scene_learn_done.c | 2 +- .../scenes/infrared_scene_learn_success.c | 2 +- .../lfrfid/scenes/lfrfid_scene_clear_t5577.c | 2 +- .../scenes/lfrfid_scene_delete_success.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_emulate.c | 2 +- .../lfrfid/scenes/lfrfid_scene_raw_read.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_rpc.c | 2 +- .../lfrfid/scenes/lfrfid_scene_save_success.c | 2 +- .../main/lfrfid/scenes/lfrfid_scene_write.c | 2 +- .../scenes/lfrfid_scene_write_success.c | 2 +- .../nfc/scenes/nfc_scene_delete_success.c | 2 +- .../main/nfc/scenes/nfc_scene_emulate_nfcv.c | 2 +- .../main/nfc/scenes/nfc_scene_emulate_uid.c | 2 +- .../nfc/scenes/nfc_scene_mf_classic_emulate.c | 2 +- .../nfc_scene_mf_classic_update_success.c | 2 +- .../nfc_scene_mf_classic_write_success.c | 2 +- .../scenes/nfc_scene_mf_ultralight_emulate.c | 2 +- .../nfc/scenes/nfc_scene_restore_original.c | 2 +- applications/main/nfc/scenes/nfc_scene_rpc.c | 2 +- .../main/nfc/scenes/nfc_scene_save_success.c | 2 +- .../scenes/subghz_scene_delete_success.c | 2 +- .../main/subghz/scenes/subghz_scene_rpc.c | 2 +- .../subghz/scenes/subghz_scene_save_success.c | 2 +- applications/main/subghz/views/receiver.c | 2 +- .../main/u2f/scenes/u2f_scene_error.c | 2 +- applications/main/u2f/views/u2f_view.c | 2 +- applications/services/bt/bt_service/bt.c | 2 +- .../desktop/animations/animation_manager.c | 2 +- .../desktop/animations/animation_storage.c | 2 +- applications/services/desktop/desktop.c | 5 -- .../desktop/scenes/desktop_scene_fault.c | 2 +- .../desktop/views/desktop_view_lock_menu.c | 2 +- applications/services/gui/application.fam | 2 +- applications/services/gui/gui.c | 2 +- .../services/gui/modules/file_browser.c | 2 +- .../services/power/power_service/power.c | 2 +- applications/services/xtreme/application.fam | 10 +++ .../xtreme/assets.c} | 4 +- .../xtreme/assets.h} | 2 +- .../services/xtreme/on_system_start.c | 7 ++ .../xtreme/settings.c} | 2 +- .../xtreme/settings.h} | 2 +- .../xtreme/settings_filename.h} | 0 applications/settings/application.fam | 1 + .../bt_settings_scene_forget_dev_success.c | 2 +- .../desktop_settings_scene_pin_disable.c | 2 +- .../settings/dolphin_passport/passport.c | 2 +- .../scenes/power_settings_scene_power_off.c | 2 +- .../scenes/storage_settings_scene_unmounted.c | 2 +- .../application.fam | 8 +- .../xtreme_app/scenes/xtreme_app_scene.c | 30 ++++++++ .../scenes/xtreme_app_scene.h} | 16 ++-- .../scenes/xtreme_app_scene_config.h | 1 + .../scenes/xtreme_app_scene_main.c} | 74 +++++++++---------- .../xtreme_app.c} | 45 ++++++----- .../xtreme_app.h} | 14 ++-- .../scenes/xtreme_settings_scene.c | 30 -------- .../scenes/xtreme_settings_scene_config.h | 1 - 66 files changed, 179 insertions(+), 169 deletions(-) create mode 100644 applications/services/xtreme/application.fam rename applications/{settings/xtreme_settings/xtreme_assets.c => services/xtreme/assets.c} (99%) rename applications/{settings/xtreme_settings/xtreme_assets.h => services/xtreme/assets.h} (97%) create mode 100644 applications/services/xtreme/on_system_start.c rename applications/{settings/xtreme_settings/xtreme_settings.c => services/xtreme/settings.c} (97%) rename applications/{settings/xtreme_settings/xtreme_settings.h => services/xtreme/settings.h} (94%) rename applications/{settings/xtreme_settings/xtreme_settings_filename.h => services/xtreme/settings_filename.h} (100%) rename applications/settings/{xtreme_settings => xtreme_app}/application.fam (54%) create mode 100644 applications/settings/xtreme_app/scenes/xtreme_app_scene.c rename applications/settings/{xtreme_settings/scenes/xtreme_settings_scene.h => xtreme_app/scenes/xtreme_app_scene.h} (63%) create mode 100644 applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h rename applications/settings/{xtreme_settings/scenes/xtreme_settings_scene_start.c => xtreme_app/scenes/xtreme_app_scene_main.c} (78%) rename applications/settings/{xtreme_settings/xtreme_settings_app.c => xtreme_app/xtreme_app.c} (69%) rename applications/settings/{xtreme_settings/xtreme_settings_app.h => xtreme_app/xtreme_app.h} (79%) delete mode 100644 applications/settings/xtreme_settings/scenes/xtreme_settings_scene.c delete mode 100644 applications/settings/xtreme_settings/scenes/xtreme_settings_scene_config.h diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index a9c33c9f6..6eb3dda50 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -6,7 +6,7 @@ #include #include #include "toolbox/path.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "xtreme/settings.h" #define FAP_MANIFEST_MAX_ICON_SIZE 32 diff --git a/applications/main/bad_usb/scenes/bad_usb_scene_error.c b/applications/main/bad_usb/scenes/bad_usb_scene_error.c index d32eee382..5cef1b7ef 100644 --- a/applications/main/bad_usb/scenes/bad_usb_scene_error.c +++ b/applications/main/bad_usb/scenes/bad_usb_scene_error.c @@ -1,5 +1,5 @@ #include "../bad_usb_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" typedef enum { BadUsbCustomEventErrorBack, diff --git a/applications/main/bad_usb/views/bad_usb_view.c b/applications/main/bad_usb/views/bad_usb_view.c index 51bed3835..88a225271 100644 --- a/applications/main/bad_usb/views/bad_usb_view.c +++ b/applications/main/bad_usb/views/bad_usb_view.c @@ -3,7 +3,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define MAX_NAME_LEN 64 diff --git a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c index 5f12d49d7..e2b7e3837 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_delete_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void ibutton_scene_delete_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_read.c b/applications/main/ibutton/scenes/ibutton_scene_read.c index 718cb8bfc..1ccd2562b 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_read.c +++ b/applications/main/ibutton/scenes/ibutton_scene_read.c @@ -1,6 +1,6 @@ #include "../ibutton_i.h" #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void ibutton_scene_read_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_save_success.c b/applications/main/ibutton/scenes/ibutton_scene_save_success.c index ee49b432c..03e88e047 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_save_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_save_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void ibutton_scene_save_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/ibutton/scenes/ibutton_scene_write_success.c b/applications/main/ibutton/scenes/ibutton_scene_write_success.c index e77f3f049..3f565e274 100644 --- a/applications/main/ibutton/scenes/ibutton_scene_write_success.c +++ b/applications/main/ibutton/scenes/ibutton_scene_write_success.c @@ -1,5 +1,5 @@ #include "../ibutton_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void ibutton_scene_write_success_popup_callback(void* context) { iButton* ibutton = context; diff --git a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c index 3d7a9d081..36224f418 100644 --- a/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c +++ b/applications/main/infrared/scenes/infrared_scene_edit_rename_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void infrared_scene_edit_rename_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_done.c b/applications/main/infrared/scenes/infrared_scene_learn_done.c index dce74db54..0d2522946 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_done.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_done.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void infrared_scene_learn_done_on_enter(void* context) { Infrared* infrared = context; diff --git a/applications/main/infrared/scenes/infrared_scene_learn_success.c b/applications/main/infrared/scenes/infrared_scene_learn_success.c index 3340a6342..bbc84ba3b 100644 --- a/applications/main/infrared/scenes/infrared_scene_learn_success.c +++ b/applications/main/infrared/scenes/infrared_scene_learn_success.c @@ -1,5 +1,5 @@ #include "../infrared_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void infrared_scene_learn_success_dialog_result_callback(DialogExResult result, void* context) { diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c index 7d9d50765..888288a31 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_clear_t5577.c @@ -1,6 +1,6 @@ #include "../lfrfid_i.h" #include "../helpers/rfid_writer.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void writer_initialize(T55xxTiming* t55xxtiming) { t55xxtiming->wait_time = 400; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c index 873382b6c..991748515 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void lfrfid_scene_delete_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c index 9132f4a91..eff92dc37 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_emulate.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void lfrfid_scene_emulate_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c index 73e1dbbb9..3213297d7 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_raw_read.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define RAW_READ_TIME 5000 diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c index c11dd1109..181346e9d 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_rpc.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void lfrfid_scene_rpc_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c index 4003ee405..738e90bfd 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_save_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void lfrfid_scene_save_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write.c b/applications/main/lfrfid/scenes/lfrfid_scene_write.c index 9cae43c96..0f74ece45 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void lfrfid_write_callback(LFRFIDWorkerWriteResult result, void* context) { LfRfid* app = context; diff --git a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c index 48d046227..5eeb88616 100644 --- a/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c +++ b/applications/main/lfrfid/scenes/lfrfid_scene_write_success.c @@ -1,5 +1,5 @@ #include "../lfrfid_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void lfrfid_scene_write_success_on_enter(void* context) { LfRfid* app = context; diff --git a/applications/main/nfc/scenes/nfc_scene_delete_success.c b/applications/main/nfc/scenes/nfc_scene_delete_success.c index a732f5384..4994dd8d4 100644 --- a/applications/main/nfc/scenes/nfc_scene_delete_success.c +++ b/applications/main/nfc/scenes/nfc_scene_delete_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_delete_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c index f423efc38..0e9f6b428 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_nfcv.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define NFC_SCENE_EMULATE_NFCV_LOG_SIZE_MAX (100) diff --git a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c index a583ac12d..7c016ceda 100644 --- a/applications/main/nfc/scenes/nfc_scene_emulate_uid.c +++ b/applications/main/nfc/scenes/nfc_scene_emulate_uid.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define NFC_SCENE_EMULATE_UID_LOG_SIZE_MAX (200) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c index 1d2e1784f..7f5cac406 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_emulate.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define NFC_MF_CLASSIC_DATA_NOT_CHANGED (0UL) #define NFC_MF_CLASSIC_DATA_CHANGED (1UL) diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c index 3c2c724bd..9544721b6 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_update_success.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_mf_classic_update_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c index f16d2f733..e82dedeaf 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_classic_write_success.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_mf_classic_write_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c index d50ad5b84..77d831cf8 100644 --- a/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c +++ b/applications/main/nfc/scenes/nfc_scene_mf_ultralight_emulate.c @@ -1,6 +1,6 @@ #include "../nfc_i.h" #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define NFC_SCENE_MF_ULTRALIGHT_EMULATE_LOG_SIZE_MAX (200) diff --git a/applications/main/nfc/scenes/nfc_scene_restore_original.c b/applications/main/nfc/scenes/nfc_scene_restore_original.c index 3794b438d..409785e26 100644 --- a/applications/main/nfc/scenes/nfc_scene_restore_original.c +++ b/applications/main/nfc/scenes/nfc_scene_restore_original.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_restore_original_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_rpc.c b/applications/main/nfc/scenes/nfc_scene_rpc.c index e6b31fffa..6adacfda5 100644 --- a/applications/main/nfc/scenes/nfc_scene_rpc.c +++ b/applications/main/nfc/scenes/nfc_scene_rpc.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_rpc_on_enter(void* context) { Nfc* nfc = context; diff --git a/applications/main/nfc/scenes/nfc_scene_save_success.c b/applications/main/nfc/scenes/nfc_scene_save_success.c index a9359c434..c5d8a6872 100644 --- a/applications/main/nfc/scenes/nfc_scene_save_success.c +++ b/applications/main/nfc/scenes/nfc_scene_save_success.c @@ -1,5 +1,5 @@ #include "../nfc_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void nfc_scene_save_success_popup_callback(void* context) { Nfc* nfc = context; diff --git a/applications/main/subghz/scenes/subghz_scene_delete_success.c b/applications/main/subghz/scenes/subghz_scene_delete_success.c index de17d7a86..8e3ca5c78 100644 --- a/applications/main/subghz/scenes/subghz_scene_delete_success.c +++ b/applications/main/subghz/scenes/subghz_scene_delete_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void subghz_scene_delete_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/scenes/subghz_scene_rpc.c b/applications/main/subghz/scenes/subghz_scene_rpc.c index 2a61d5dc5..609b4a71d 100644 --- a/applications/main/subghz/scenes/subghz_scene_rpc.c +++ b/applications/main/subghz/scenes/subghz_scene_rpc.c @@ -1,7 +1,7 @@ #include "../subghz_i.h" #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" typedef enum { SubGhzRpcStateIdle, diff --git a/applications/main/subghz/scenes/subghz_scene_save_success.c b/applications/main/subghz/scenes/subghz_scene_save_success.c index bf8ed0185..48804fe54 100644 --- a/applications/main/subghz/scenes/subghz_scene_save_success.c +++ b/applications/main/subghz/scenes/subghz_scene_save_success.c @@ -1,6 +1,6 @@ #include "../subghz_i.h" #include "../helpers/subghz_custom_event.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void subghz_scene_save_success_popup_callback(void* context) { SubGhz* subghz = context; diff --git a/applications/main/subghz/views/receiver.c b/applications/main/subghz/views/receiver.c index 74bdbf5b0..fa3569245 100644 --- a/applications/main/subghz/views/receiver.c +++ b/applications/main/subghz/views/receiver.c @@ -7,7 +7,7 @@ #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define FRAME_HEIGHT 12 #define MAX_LEN_PX 111 diff --git a/applications/main/u2f/scenes/u2f_scene_error.c b/applications/main/u2f/scenes/u2f_scene_error.c index 35a6ce1d9..d87b13063 100644 --- a/applications/main/u2f/scenes/u2f_scene_error.c +++ b/applications/main/u2f/scenes/u2f_scene_error.c @@ -1,5 +1,5 @@ #include "../u2f_app_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void u2f_scene_error_event_callback(GuiButtonType result, InputType type, void* context) { furi_assert(context); diff --git a/applications/main/u2f/views/u2f_view.c b/applications/main/u2f/views/u2f_view.c index fc1c5c4fa..7bd2cf94f 100644 --- a/applications/main/u2f/views/u2f_view.c +++ b/applications/main/u2f/views/u2f_view.c @@ -1,7 +1,7 @@ #include "u2f_view.h" #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" struct U2fView { View* view; diff --git a/applications/services/bt/bt_service/bt.c b/applications/services/bt/bt_service/bt.c index 1ca6a2775..3f1577751 100644 --- a/applications/services/bt/bt_service/bt.c +++ b/applications/services/bt/bt_service/bt.c @@ -5,7 +5,7 @@ #include #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define TAG "BtSrv" diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 0808a3618..5b548c5bc 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -13,7 +13,7 @@ #include "animation_storage.h" #include "animation_manager.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define TAG "AnimationManager" diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 4263dc0a4..4ac36762e 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -11,7 +11,7 @@ #include "animation_storage_i.h" #include #include -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define ANIMATION_META_FILE "meta.txt" #define BASE_ANIMATION_DIR EXT_PATH("dolphin") #define TAG "AnimationStorage" diff --git a/applications/services/desktop/desktop.c b/applications/services/desktop/desktop.c index b5b73668b..7840cd00a 100644 --- a/applications/services/desktop/desktop.c +++ b/applications/services/desktop/desktop.c @@ -17,8 +17,6 @@ #include "helpers/pin_lock.h" #include "helpers/slideshow_filename.h" -#include "../../settings/xtreme_settings/xtreme_assets.h" - static void desktop_auto_lock_arm(Desktop*); static void desktop_auto_lock_inhibit(Desktop*); static void desktop_start_auto_lock_timer(Desktop*); @@ -307,9 +305,6 @@ static bool desktop_check_file_flag(const char* flag_path) { int32_t desktop_srv(void* p) { UNUSED(p); - // TODO: find a (working) way to run this at startup without hooking desktop - XTREME_ASSETS_LOAD(); - if(furi_hal_rtc_get_boot_mode() != FuriHalRtcBootModeNormal) { FURI_LOG_W("Desktop", "Desktop load skipped. Device is in special startup mode."); } else { diff --git a/applications/services/desktop/scenes/desktop_scene_fault.c b/applications/services/desktop/scenes/desktop_scene_fault.c index c2149253c..b3801d78d 100644 --- a/applications/services/desktop/scenes/desktop_scene_fault.c +++ b/applications/services/desktop/scenes/desktop_scene_fault.c @@ -1,7 +1,7 @@ #include #include "../desktop_i.h" -#include "../../../settings/xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define DesktopFaultEventExit 0x00FF00FF diff --git a/applications/services/desktop/views/desktop_view_lock_menu.c b/applications/services/desktop/views/desktop_view_lock_menu.c index 594676c28..9e02f2bdf 100644 --- a/applications/services/desktop/views/desktop_view_lock_menu.c +++ b/applications/services/desktop/views/desktop_view_lock_menu.c @@ -5,7 +5,7 @@ #include "../desktop_i.h" #include "desktop_view_lock_menu.h" -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "xtreme/settings.h" #define LOCK_MENU_ITEMS_NB 5 diff --git a/applications/services/gui/application.fam b/applications/services/gui/application.fam index 43e93559e..7981a4fcb 100644 --- a/applications/services/gui/application.fam +++ b/applications/services/gui/application.fam @@ -7,7 +7,7 @@ App( requires=[ "input", "notification", - "xtreme_settings", + "xtreme", ], stack_size=2 * 1024, order=70, diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 46baf453d..2fdec197b 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -1,4 +1,4 @@ -#include "../../settings/xtreme_settings/xtreme_settings.h" +#include "xtreme/settings.h" #include "gui_i.h" #include diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index a3011a049..0a08db7c3 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -14,7 +14,7 @@ #include "m-string.h" #include "m-algo.h" #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "xtreme/settings.h" #define LIST_ITEMS 5u #define MAX_LEN_PX 110 diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index e52cb4e10..6f31f3044 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -2,7 +2,7 @@ #include #include -#include "../../../settings/xtreme_settings/xtreme_settings.h" +#include "xtreme/settings.h" #define POWER_OFF_TIMEOUT 90 diff --git a/applications/services/xtreme/application.fam b/applications/services/xtreme/application.fam new file mode 100644 index 000000000..2dfcab051 --- /dev/null +++ b/applications/services/xtreme/application.fam @@ -0,0 +1,10 @@ +App( + appid="xtreme", + apptype=FlipperAppType.STARTUP, + entry_point="xtreme_on_system_start", + requires=["storage"], + order=1000, + provides=[ + "xtreme", + ], +) diff --git a/applications/settings/xtreme_settings/xtreme_assets.c b/applications/services/xtreme/assets.c similarity index 99% rename from applications/settings/xtreme_settings/xtreme_assets.c rename to applications/services/xtreme/assets.c index 13014b8d1..4d3f3470b 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.c +++ b/applications/services/xtreme/assets.c @@ -1,5 +1,5 @@ -#include "xtreme_assets.h" -#include "assets_icons.h" +#include "assets.h" +#include #include #define ICONS_FMT PACKS_DIR "/%s/Icons/%s" diff --git a/applications/settings/xtreme_settings/xtreme_assets.h b/applications/services/xtreme/assets.h similarity index 97% rename from applications/settings/xtreme_settings/xtreme_assets.h rename to applications/services/xtreme/assets.h index 038372a43..b88a6cf1e 100644 --- a/applications/settings/xtreme_settings/xtreme_assets.h +++ b/applications/services/xtreme/assets.h @@ -1,7 +1,7 @@ #pragma once +#include "settings.h" #include -#include "xtreme_settings.h" #include #define PACKS_DIR EXT_PATH("dolphin_custom") diff --git a/applications/services/xtreme/on_system_start.c b/applications/services/xtreme/on_system_start.c new file mode 100644 index 000000000..531888f1b --- /dev/null +++ b/applications/services/xtreme/on_system_start.c @@ -0,0 +1,7 @@ +#include "settings.h" +#include "assets.h" + +void xtreme_on_system_start() { + XTREME_SETTINGS_LOAD(); + XTREME_ASSETS_LOAD(); +} diff --git a/applications/settings/xtreme_settings/xtreme_settings.c b/applications/services/xtreme/settings.c similarity index 97% rename from applications/settings/xtreme_settings/xtreme_settings.c rename to applications/services/xtreme/settings.c index 3db0a4c1c..eea68bd2b 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.c +++ b/applications/services/xtreme/settings.c @@ -1,4 +1,4 @@ -#include "xtreme_settings.h" +#include "settings.h" XtremeSettings* xtreme_settings = NULL; diff --git a/applications/settings/xtreme_settings/xtreme_settings.h b/applications/services/xtreme/settings.h similarity index 94% rename from applications/settings/xtreme_settings/xtreme_settings.h rename to applications/services/xtreme/settings.h index aacbdd92a..9fd5f5c53 100644 --- a/applications/settings/xtreme_settings/xtreme_settings.h +++ b/applications/services/xtreme/settings.h @@ -1,6 +1,6 @@ #pragma once -#include "xtreme_settings_filename.h" +#include "settings_filename.h" #include #include diff --git a/applications/settings/xtreme_settings/xtreme_settings_filename.h b/applications/services/xtreme/settings_filename.h similarity index 100% rename from applications/settings/xtreme_settings/xtreme_settings_filename.h rename to applications/services/xtreme/settings_filename.h diff --git a/applications/settings/application.fam b/applications/settings/application.fam index cc4b9703d..49695b4b3 100644 --- a/applications/settings/application.fam +++ b/applications/settings/application.fam @@ -5,6 +5,7 @@ App( provides=[ "passport", "system_settings", + "xtreme_app", "about", ], ) diff --git a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c index f797cdc0d..59071959c 100644 --- a/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c +++ b/applications/settings/bt_settings_app/scenes/bt_settings_scene_forget_dev_success.c @@ -1,6 +1,6 @@ #include "../bt_settings_app.h" #include "furi_hal_bt.h" -#include "../../xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #include void bt_settings_app_scene_forget_dev_success_popup_callback(void* context) { diff --git a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c index cdafbb828..cad5f43f4 100644 --- a/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c +++ b/applications/settings/desktop_settings/scenes/desktop_settings_scene_pin_disable.c @@ -6,7 +6,7 @@ #include "../desktop_settings_app.h" #include #include "desktop_settings_scene.h" -#include "../../xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #define SCENE_EVENT_EXIT (0U) diff --git a/applications/settings/dolphin_passport/passport.c b/applications/settings/dolphin_passport/passport.c index 450c5af23..d6474b4cf 100644 --- a/applications/settings/dolphin_passport/passport.c +++ b/applications/settings/dolphin_passport/passport.c @@ -6,7 +6,7 @@ #include #include #include "dolphin/dolphin.h" -#include "../xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" #include "math.h" typedef struct { diff --git a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c index 6fd26138b..afcc6f950 100644 --- a/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c +++ b/applications/settings/power_settings_app/scenes/power_settings_scene_power_off.c @@ -1,5 +1,5 @@ #include "../power_settings_app.h" -#include "../../xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" void power_settings_scene_power_off_dialog_callback(DialogExResult result, void* context) { furi_assert(context); diff --git a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c index 455cbaa78..13f53acd1 100644 --- a/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c +++ b/applications/settings/storage_settings/scenes/storage_settings_scene_unmounted.c @@ -1,5 +1,5 @@ #include "../storage_settings.h" -#include "../../xtreme_settings/xtreme_assets.h" +#include "xtreme/assets.h" static void storage_settings_scene_unmounted_dialog_callback(DialogExResult result, void* context) { diff --git a/applications/settings/xtreme_settings/application.fam b/applications/settings/xtreme_app/application.fam similarity index 54% rename from applications/settings/xtreme_settings/application.fam rename to applications/settings/xtreme_app/application.fam index 79c0f62b4..e1b7fc964 100644 --- a/applications/settings/xtreme_settings/application.fam +++ b/applications/settings/xtreme_app/application.fam @@ -1,14 +1,12 @@ App( - appid="xtreme_settings", + appid="xtreme_app", name="Xtreme FW", apptype=FlipperAppType.SETTINGS, - entry_point="xtreme_settings_app", + entry_point="xtreme_app", stack_size=2 * 1024, requires=[ "gui", + "xtreme", ], order=90, - provides=[ - "xtreme_settings", - ], ) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene.c new file mode 100644 index 000000000..5448bb85a --- /dev/null +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene.c @@ -0,0 +1,30 @@ +#include "xtreme_app_scene.h" + +// Generate scene on_enter handlers array +#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, +void (*const xtreme_app_on_enter_handlers[])(void*) = { +#include "xtreme_app_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 xtreme_app_on_event_handlers[])(void* context, SceneManagerEvent event) = { +#include "xtreme_app_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 xtreme_app_on_exit_handlers[])(void* context) = { +#include "xtreme_app_scene_config.h" +}; +#undef ADD_SCENE + +// Initialize scene handlers configuration structure +const SceneManagerHandlers xtreme_app_scene_handlers = { + .on_enter_handlers = xtreme_app_on_enter_handlers, + .on_event_handlers = xtreme_app_on_event_handlers, + .on_exit_handlers = xtreme_app_on_exit_handlers, + .scene_num = XtremeAppSceneNum, +}; diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h b/applications/settings/xtreme_app/scenes/xtreme_app_scene.h similarity index 63% rename from applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h rename to applications/settings/xtreme_app/scenes/xtreme_app_scene.h index 70abf4f77..d7f19abac 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.h +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene.h @@ -3,27 +3,27 @@ #include // Generate scene id and total number -#define ADD_SCENE(prefix, name, id) XtremeSettingsAppScene##id, +#define ADD_SCENE(prefix, name, id) XtremeAppScene##id, typedef enum { -#include "xtreme_settings_scene_config.h" - XtremeSettingsAppSceneNum, -} XtremeSettingsAppScene; +#include "xtreme_app_scene_config.h" + XtremeAppSceneNum, +} XtremeAppScene; #undef ADD_SCENE -extern const SceneManagerHandlers xtreme_settings_scene_handlers; +extern const SceneManagerHandlers xtreme_app_scene_handlers; // Generate scene on_enter handlers declaration #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); -#include "xtreme_settings_scene_config.h" +#include "xtreme_app_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 "xtreme_settings_scene_config.h" +#include "xtreme_app_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 "xtreme_settings_scene_config.h" +#include "xtreme_app_scene_config.h" #undef ADD_SCENE diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h b/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h new file mode 100644 index 000000000..9eed63575 --- /dev/null +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_config.h @@ -0,0 +1 @@ +ADD_SCENE(xtreme_app, main, Main) diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c similarity index 78% rename from applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c rename to applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index 40972a31a..b645c0d64 100644 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_start.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -1,10 +1,10 @@ -#include "../xtreme_settings_app.h" +#include "../xtreme_app.h" #include #include #include -static void xtreme_settings_scene_start_asset_pack_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_asset_pack_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text( item, index == 0 ? "SFW" : *asset_packs_get(app->asset_packs, index - 1)); @@ -20,8 +20,8 @@ const char* const anim_speed_names[] = {"25%", "50%", "75%", "100%", "125%", "150%", "175%", "200%", "225%", "250%", "275%", "300%"}; const int32_t anim_speed_values[COUNT_OF(anim_speed_names)] = {25, 50, 75, 0, 125, 150, 175, 200, 225, 250, 275, 300}; -static void xtreme_settings_scene_start_anim_speed_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_anim_speed_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, anim_speed_names[index]); XTREME_SETTINGS()->anim_speed = anim_speed_values[index]; @@ -44,16 +44,16 @@ const char* const cycle_anims_names[] = { "24 H"}; const int32_t cycle_anims_values[COUNT_OF(cycle_anims_names)] = {-1, 0, 30, 60, 300, 600, 900, 1800, 3600, 7200, 21600, 43200, 86400}; -static void xtreme_settings_scene_start_cycle_anims_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_cycle_anims_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, cycle_anims_names[index]); XTREME_SETTINGS()->cycle_anims = cycle_anims_values[index]; app->settings_changed = true; } -static void xtreme_settings_scene_start_unlock_anims_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_unlock_anims_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); XTREME_SETTINGS()->unlock_anims = value; @@ -70,32 +70,32 @@ const int32_t battery_style_values[COUNT_OF(battery_style_names)] = { BatteryStyleRetro3, BatteryStyleRetro5, BatteryStyleBarPercent}; -static void xtreme_settings_scene_start_battery_style_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_battery_style_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, battery_style_names[index]); XTREME_SETTINGS()->battery_style = battery_style_values[index]; app->settings_changed = true; } -static void xtreme_settings_scene_start_status_bar_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_status_bar_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); XTREME_SETTINGS()->status_bar = value; app->settings_changed = true; } -static void xtreme_settings_scene_start_sort_folders_before_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_sort_folders_before_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); XTREME_SETTINGS()->sort_ignore_dirs = !value; app->settings_changed = true; } -static void xtreme_settings_scene_start_xp_level_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_xp_level_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); app->dolphin_level = variable_item_get_current_value_index(item) + 1; char level_str[4]; snprintf(level_str, 4, "%i", app->dolphin_level); @@ -103,22 +103,22 @@ static void xtreme_settings_scene_start_xp_level_changed(VariableItem* item) { app->level_changed = true; } -static void xtreme_settings_scene_start_subghz_extend_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_subghz_extend_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); app->subghz_extend = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); app->subghz_changed = true; } -static void xtreme_settings_scene_start_subghz_bypass_changed(VariableItem* item) { - XtremeSettingsApp* app = variable_item_get_context(item); +static void xtreme_app_scene_main_subghz_bypass_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); app->subghz_bypass = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); app->subghz_changed = true; } -void xtreme_settings_scene_start_on_enter(void* context) { - XtremeSettingsApp* app = context; +void xtreme_app_scene_main_on_enter(void* context) { + XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); VariableItemList* var_item_list = app->var_item_list; VariableItem* item; @@ -177,7 +177,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "Asset Pack", asset_packs_size(app->asset_packs) + 1, - xtreme_settings_scene_start_asset_pack_changed, + xtreme_app_scene_main_asset_pack_changed, app); variable_item_set_current_value_index(item, current_pack); variable_item_set_current_value_text( @@ -187,7 +187,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "Anim Speed", COUNT_OF(anim_speed_names), - xtreme_settings_scene_start_anim_speed_changed, + xtreme_app_scene_main_anim_speed_changed, app); value_index = value_index_int32( xtreme_settings->anim_speed, anim_speed_values, COUNT_OF(anim_speed_names)); @@ -198,7 +198,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "Cycle Anims", COUNT_OF(cycle_anims_names), - xtreme_settings_scene_start_cycle_anims_changed, + xtreme_app_scene_main_cycle_anims_changed, app); value_index = value_index_int32( xtreme_settings->cycle_anims, cycle_anims_values, COUNT_OF(cycle_anims_names)); @@ -206,7 +206,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, cycle_anims_names[value_index]); item = variable_item_list_add( - var_item_list, "Unlock Anims", 2, xtreme_settings_scene_start_unlock_anims_changed, app); + var_item_list, "Unlock Anims", 2, xtreme_app_scene_main_unlock_anims_changed, app); variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); @@ -214,7 +214,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "Battery Style", COUNT_OF(battery_style_names), - xtreme_settings_scene_start_battery_style_changed, + xtreme_app_scene_main_battery_style_changed, app); value_index = value_index_int32( xtreme_settings->battery_style, battery_style_values, COUNT_OF(battery_style_names)); @@ -222,7 +222,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { variable_item_set_current_value_text(item, battery_style_names[value_index]); item = variable_item_list_add( - var_item_list, "Status Bar", 2, xtreme_settings_scene_start_status_bar_changed, app); + var_item_list, "Status Bar", 2, xtreme_app_scene_main_status_bar_changed, app); variable_item_set_current_value_index(item, xtreme_settings->status_bar); variable_item_set_current_value_text(item, xtreme_settings->status_bar ? "ON" : "OFF"); @@ -230,7 +230,7 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "Sort Dirs Before", 2, - xtreme_settings_scene_start_sort_folders_before_changed, + xtreme_app_scene_main_sort_folders_before_changed, app); variable_item_set_current_value_index(item, !xtreme_settings->sort_ignore_dirs); variable_item_set_current_value_text(item, !xtreme_settings->sort_ignore_dirs ? "ON" : "OFF"); @@ -241,18 +241,18 @@ void xtreme_settings_scene_start_on_enter(void* context) { var_item_list, "XP Level", DOLPHIN_LEVEL_COUNT + 1, - xtreme_settings_scene_start_xp_level_changed, + xtreme_app_scene_main_xp_level_changed, app); variable_item_set_current_value_index(item, app->dolphin_level - 1); variable_item_set_current_value_text(item, level_str); item = variable_item_list_add( - var_item_list, "SubGHz Extend", 2, xtreme_settings_scene_start_subghz_extend_changed, app); + var_item_list, "SubGHz Extend", 2, xtreme_app_scene_main_subghz_extend_changed, app); variable_item_set_current_value_index(item, app->subghz_extend); variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); item = variable_item_list_add( - var_item_list, "SubGHz Bypass", 2, xtreme_settings_scene_start_subghz_bypass_changed, app); + var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_main_subghz_bypass_changed, app); variable_item_set_current_value_index(item, app->subghz_bypass); variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); @@ -260,18 +260,18 @@ void xtreme_settings_scene_start_on_enter(void* context) { "%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); item = variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); - view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList); + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } -bool xtreme_settings_scene_start_on_event(void* context, SceneManagerEvent event) { +bool xtreme_app_scene_main_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); bool consumed = false; return consumed; } -void xtreme_settings_scene_start_on_exit(void* context) { - XtremeSettingsApp* app = context; +void xtreme_app_scene_main_on_exit(void* context) { + XtremeApp* app = context; asset_packs_it_t it; for(asset_packs_it(it, app->asset_packs); !asset_packs_end_p(it); asset_packs_next(it)) { free(*asset_packs_cref(it)); diff --git a/applications/settings/xtreme_settings/xtreme_settings_app.c b/applications/settings/xtreme_app/xtreme_app.c similarity index 69% rename from applications/settings/xtreme_settings/xtreme_settings_app.c rename to applications/settings/xtreme_app/xtreme_app.c index 6b0a12f1c..cdacf6cca 100644 --- a/applications/settings/xtreme_settings/xtreme_settings_app.c +++ b/applications/settings/xtreme_app/xtreme_app.c @@ -1,19 +1,19 @@ -#include "xtreme_settings_app.h" +#include "xtreme_app.h" -static bool xtreme_settings_custom_event_callback(void* context, uint32_t event) { +static bool xtreme_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); - XtremeSettingsApp* app = context; + XtremeApp* app = context; return scene_manager_handle_custom_event(app->scene_manager, event); } -void xtreme_settings_reboot(void* context) { +void xtreme_app_reboot(void* context) { UNUSED(context); power_reboot(PowerBootModeNormal); } -static bool xtreme_settings_back_event_callback(void* context) { +static bool xtreme_app_back_event_callback(void* context) { furi_assert(context); - XtremeSettingsApp* app = context; + XtremeApp* app = context; if(app->level_changed) { Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); @@ -45,11 +45,11 @@ static bool xtreme_settings_back_event_callback(void* context) { if(app->assets_changed) { popup_set_header(app->popup, "Rebooting...", 64, 26, AlignCenter, AlignCenter); popup_set_text(app->popup, "Swapping assets...", 64, 40, AlignCenter, AlignCenter); - popup_set_callback(app->popup, xtreme_settings_reboot); + popup_set_callback(app->popup, xtreme_app_reboot); popup_set_context(app->popup, app); popup_set_timeout(app->popup, 1000); popup_enable_timeout(app->popup); - view_dispatcher_switch_to_view(app->view_dispatcher, XtremeSettingsAppViewPopup); + view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewPopup); return true; } } @@ -57,20 +57,20 @@ static bool xtreme_settings_back_event_callback(void* context) { return scene_manager_handle_back_event(app->scene_manager); } -XtremeSettingsApp* xtreme_settings_app_alloc() { - XtremeSettingsApp* app = malloc(sizeof(XtremeSettingsApp)); +XtremeApp* xtreme_app_alloc() { + XtremeApp* app = malloc(sizeof(XtremeApp)); app->gui = furi_record_open(RECORD_GUI); // View Dispatcher and Scene Manager app->view_dispatcher = view_dispatcher_alloc(); - app->scene_manager = scene_manager_alloc(&xtreme_settings_scene_handlers, app); + app->scene_manager = scene_manager_alloc(&xtreme_app_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, xtreme_settings_custom_event_callback); + app->view_dispatcher, xtreme_app_custom_event_callback); view_dispatcher_set_navigation_event_callback( - app->view_dispatcher, xtreme_settings_back_event_callback); + app->view_dispatcher, xtreme_app_back_event_callback); view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); @@ -78,25 +78,23 @@ XtremeSettingsApp* xtreme_settings_app_alloc() { app->var_item_list = variable_item_list_alloc(); view_dispatcher_add_view( app->view_dispatcher, - XtremeSettingsAppViewVarItemList, + XtremeAppViewVarItemList, variable_item_list_get_view(app->var_item_list)); app->popup = popup_alloc(); view_dispatcher_add_view( - app->view_dispatcher, XtremeSettingsAppViewPopup, popup_get_view(app->popup)); + app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); - // Set first scene - scene_manager_next_scene(app->scene_manager, XtremeSettingsAppSceneStart); return app; } -void xtreme_settings_app_free(XtremeSettingsApp* app) { +void xtreme_app_free(XtremeApp* app) { furi_assert(app); // Gui modules - view_dispatcher_remove_view(app->view_dispatcher, XtremeSettingsAppViewVarItemList); + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewVarItemList); variable_item_list_free(app->var_item_list); - view_dispatcher_remove_view(app->view_dispatcher, XtremeSettingsAppViewPopup); + view_dispatcher_remove_view(app->view_dispatcher, XtremeAppViewPopup); popup_free(app->popup); // View Dispatcher and Scene Manager @@ -108,10 +106,11 @@ void xtreme_settings_app_free(XtremeSettingsApp* app) { free(app); } -extern int32_t xtreme_settings_app(void* p) { +extern int32_t xtreme_app(void* p) { UNUSED(p); - XtremeSettingsApp* app = xtreme_settings_app_alloc(); + XtremeApp* app = xtreme_app_alloc(); + scene_manager_next_scene(app->scene_manager, XtremeAppSceneMain); view_dispatcher_run(app->view_dispatcher); - xtreme_settings_app_free(app); + xtreme_app_free(app); return 0; } diff --git a/applications/settings/xtreme_settings/xtreme_settings_app.h b/applications/settings/xtreme_app/xtreme_app.h similarity index 79% rename from applications/settings/xtreme_settings/xtreme_settings_app.h rename to applications/settings/xtreme_app/xtreme_app.h index 7265da078..23adbf5ac 100644 --- a/applications/settings/xtreme_settings/xtreme_settings_app.h +++ b/applications/settings/xtreme_app/xtreme_app.h @@ -8,14 +8,14 @@ #include #include #include -#include "xtreme_settings.h" -#include "xtreme_assets.h" -#include "scenes/xtreme_settings_scene.h" +#include "scenes/xtreme_app_scene.h" #include "dolphin/helpers/dolphin_state.h" #include "dolphin/dolphin.h" #include "dolphin/dolphin_i.h" #include #include +#include "xtreme/settings.h" +#include "xtreme/assets.h" ARRAY_DEF(asset_packs, char*) @@ -33,9 +33,9 @@ typedef struct { bool subghz_changed; bool level_changed; asset_packs_t asset_packs; -} XtremeSettingsApp; +} XtremeApp; typedef enum { - XtremeSettingsAppViewVarItemList, - XtremeSettingsAppViewPopup, -} XtremeSettingsAppView; + XtremeAppViewVarItemList, + XtremeAppViewPopup, +} XtremeAppView; diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.c b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.c deleted file mode 100644 index 3d97ed979..000000000 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "xtreme_settings_scene.h" - -// Generate scene on_enter handlers array -#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, -void (*const xtreme_settings_on_enter_handlers[])(void*) = { -#include "xtreme_settings_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 xtreme_settings_on_event_handlers[])(void* context, SceneManagerEvent event) = { -#include "xtreme_settings_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 xtreme_settings_on_exit_handlers[])(void* context) = { -#include "xtreme_settings_scene_config.h" -}; -#undef ADD_SCENE - -// Initialize scene handlers configuration structure -const SceneManagerHandlers xtreme_settings_scene_handlers = { - .on_enter_handlers = xtreme_settings_on_enter_handlers, - .on_event_handlers = xtreme_settings_on_event_handlers, - .on_exit_handlers = xtreme_settings_on_exit_handlers, - .scene_num = XtremeSettingsAppSceneNum, -}; diff --git a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_config.h b/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_config.h deleted file mode 100644 index eddd4f82f..000000000 --- a/applications/settings/xtreme_settings/scenes/xtreme_settings_scene_config.h +++ /dev/null @@ -1 +0,0 @@ -ADD_SCENE(xtreme_settings, start, Start) From 786ea60af93d5124a973403edec6d02779594c9b Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 05:53:56 +0000 Subject: [PATCH 173/231] Nightstand clock tweaks --- applications/plugins/nightstand/clock_app.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/applications/plugins/nightstand/clock_app.c b/applications/plugins/nightstand/clock_app.c index d6bf63862..d7a9f7faa 100644 --- a/applications/plugins/nightstand/clock_app.c +++ b/applications/plugins/nightstand/clock_app.c @@ -270,6 +270,7 @@ int32_t clock_app(void* p) { notif = furi_record_open(RECORD_NOTIFICATION); float tmpBrightness = notif->settings.display_brightness; + brightness = tmpBrightness * 100; notification_message(notif, &sequence_display_backlight_enforce_on); notification_message(notif, &led_off); @@ -284,7 +285,7 @@ int32_t clock_app(void* p) { if(furi_mutex_acquire(plugin_state->mutex, FuriWaitForever) != FuriStatusOk) continue; // press events if(event.type == EventTypeKey) { - if(event.input.type == InputTypeLong) { + if(event.input.type == InputTypeShort) { switch(event.input.key) { case InputKeyLeft: // Reset seconds @@ -298,11 +299,6 @@ int32_t clock_app(void* p) { // Exit the plugin processing = false; break; - default: - break; - } - } else if(event.input.type == InputTypeShort) { - switch(event.input.key) { case InputKeyUp: handle_up(); break; @@ -335,4 +331,4 @@ int32_t clock_app(void* p) { notification_message(notif, &led_reset); return 0; -} \ No newline at end of file +} From a708164dac2870eb81848faa5f09f3fbf526bbba Mon Sep 17 00:00:00 2001 From: jbohack Date: Sat, 11 Feb 2023 01:02:37 -0500 Subject: [PATCH 174/231] added pager to subghz frequency analyzer in subghz settings subghz frequency changes are hardcoded in the firmware for app loading times so this is needed --- applications/main/subghz/views/subghz_frequency_analyzer.c | 4 ++-- lib/subghz/subghz_setting.c | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 9fa304e90..24f24c39d 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -25,7 +25,7 @@ static const uint32_t subghz_frequency_list[] = { 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, - 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 779000000, + 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { @@ -645,4 +645,4 @@ SubGHzFrequencyAnalyzerFeedbackLevel subghz_frequency_analyzer_feedback_level( float subghz_frequency_analyzer_get_trigger_level(SubGhzFrequencyAnalyzer* instance) { furi_assert(instance); return subghz_frequency_analyzer_worker_get_trigger_level(instance->worker); -} \ No newline at end of file +} diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 35ba54a8a..05b6a74ad 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -61,6 +61,7 @@ static const uint32_t subghz_frequency_list[] = { 438900000, 440175000, 464000000, + 467750000, /* 779 - 928 */ 779000000, From 2455cc0c7526b1a3db7f7b2aacffeba3c125dcdf Mon Sep 17 00:00:00 2001 From: jbohack Date: Sat, 11 Feb 2023 14:56:50 -0500 Subject: [PATCH 175/231] added pager modulation back modulation made by jimi --- applications/main/subghz/subghz.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/applications/main/subghz/subghz.c b/applications/main/subghz/subghz.c index 39e89e9e9..78295f08c 100644 --- a/applications/main/subghz/subghz.c +++ b/applications/main/subghz/subghz.c @@ -208,27 +208,38 @@ SubGhz* subghz_alloc(bool alloc_for_tx_only) { flipper_format_free(temp_fm_preset2); - // # HND - FM presets + // Pagers FlipperFormat* temp_fm_preset3 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset3, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 07 04 08 32 0B 06 10 64 11 93 12 0C 13 02 14 00 15 15 18 18 19 16 1B 07 1C 00 1D 91 20 FB 21 56 22 10 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset3); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset3); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"Pagers", temp_fm_preset3); flipper_format_free(temp_fm_preset3); + // # HND - FM presets FlipperFormat* temp_fm_preset4 = flipper_format_string_alloc(); flipper_format_write_string_cstr( temp_fm_preset4, (const char*)"Custom_preset_data", - (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 04 11 36 10 69 15 32 18 18 19 16 1D 91 1C 00 1B 07 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); flipper_format_rewind(temp_fm_preset4); - subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset4); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_1", temp_fm_preset4); flipper_format_free(temp_fm_preset4); + FlipperFormat* temp_fm_preset5 = flipper_format_string_alloc(); + flipper_format_write_string_cstr( + temp_fm_preset5, + (const char*)"Custom_preset_data", + (const char*)"02 0D 0B 06 08 32 07 04 14 00 13 02 12 07 11 36 10 E9 15 32 18 18 19 16 1D 92 1C 40 1B 03 20 FB 22 10 21 56 00 00 C0 00 00 00 00 00 00 00"); + flipper_format_rewind(temp_fm_preset5); + subghz_setting_load_custom_preset(subghz->setting, (const char*)"HND_2", temp_fm_preset5); + + flipper_format_free(temp_fm_preset5); + // custom presets loading - end // Load last used values for Read, Read RAW, etc. or default From becc3982688ecff6986397d2ec15527d8755ddb2 Mon Sep 17 00:00:00 2001 From: jbohack Date: Sat, 11 Feb 2023 14:59:33 -0500 Subject: [PATCH 176/231] removed pager bruteforce playlist until we can support the custom modulation for the playlist --- assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt diff --git a/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt b/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt deleted file mode 100644 index 5ea4e2df5..000000000 --- a/assets/resources/subghz/playlist/Pager_bruteforce_playlist.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Pager Bruteforce Playlist -sub: /ext/subghz/Misc/Pager_Bruteforce.sub From 9ed14fe7732d632c5d8e386c9bc91ceeed60327f Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 20:07:53 +0000 Subject: [PATCH 177/231] Remove unused imports and old code --- applications/main/archive/helpers/archive_files.h | 1 - applications/services/gui/modules/file_browser.c | 1 - applications/services/gui/modules/file_browser_worker.c | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/applications/main/archive/helpers/archive_files.h b/applications/main/archive/helpers/archive_files.h index 6eb3dda50..d84f7088a 100644 --- a/applications/main/archive/helpers/archive_files.h +++ b/applications/main/archive/helpers/archive_files.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "toolbox/path.h" #include "xtreme/settings.h" diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 0a08db7c3..175bab88d 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -11,7 +11,6 @@ #include #include #include -#include "m-string.h" #include "m-algo.h" #include #include "xtreme/settings.h" diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4453b6d9f..4b7be70a1 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -15,8 +15,6 @@ #define TAG "BrowserWorker" #define ASSETS_DIR "assets" -#define BADUSB_LAYOUTS_DIR "layouts" -#define SUBGHZ_TEMP_DIR "tmp_history" #define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX #define FILE_NAME_LEN_MAX 256 #define LONG_LOAD_THRESHOLD 100 @@ -92,9 +90,7 @@ static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, boo if(is_folder) { // Skip assets folders (if enabled) if(browser->skip_assets) { - return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)) && - ((furi_string_cmp_str(name, BADUSB_LAYOUTS_DIR) == 0) ? (false) : (true)) && - ((furi_string_cmp_str(name, SUBGHZ_TEMP_DIR) == 0) ? (false) : (true)); + return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); } else { return true; } From 999c609773b3fe638cbbd2d60f2673b46c06e048 Mon Sep 17 00:00:00 2001 From: yocvito Date: Sat, 11 Feb 2023 22:01:02 +0100 Subject: [PATCH 178/231] Handle write request on HID keyboard led state characteristic and respond to it (should fix numlock issue) --- firmware/targets/f7/ble_glue/hid_service.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index 31eeb03da..c04656688 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -61,14 +61,6 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { aci_gatt_write_permit_req_event_rp0* req = (aci_gatt_write_permit_req_event_rp0*)blecore_evt->data; - // FURI_LOG_I(TAG, "GATT write request"); - // size_t len = 2 + event_pckt->plen; - // hexdump((uint8_t*)event_pckt, len); - // FURI_LOG_D(TAG, "conn_handle = %04x", req->Connection_Handle); - // FURI_LOG_D(TAG, "attr handle = %04x", req->Attribute_Handle); - // FURI_LOG_D(TAG, "led char handle = %04x", hid_svc->led_state_char_handle); - // FURI_LOG_D(TAG, "led state = %02x", req->Data[0]); - furi_check(hid_svc->led_state_event_callback && hid_svc->led_state_ctx); // this check is likely to be incorrect, it will actually work in our case @@ -76,6 +68,12 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { // that specify attibute handle value from char handle (or the reverse) if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); + aci_gatt_write_resp(req->Connection_Handle, req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, req->Data); + aci_gatt_write_char_value(req->Connection_Handle, hid_svc->led_state_char_handle, req->Data_Length, req->Data); + ret = SVCCTL_EvtAckFlowEnable; } } } @@ -252,7 +250,7 @@ void hid_svc_start() { UUID_TYPE_16, &char_uuid, 1, - CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP, + CHAR_PROP_READ | CHAR_PROP_WRITE_WITHOUT_RESP | CHAR_PROP_WRITE, ATTR_PERMISSION_NONE, GATT_NOTIFY_ATTRIBUTE_WRITE | GATT_NOTIFY_WRITE_REQ_AND_WAIT_FOR_APPL_RESP, 10, From c9941d44a77e5cb00bbcbc40c751898a5aefda2b Mon Sep 17 00:00:00 2001 From: yocvito Date: Sat, 11 Feb 2023 22:14:11 +0100 Subject: [PATCH 179/231] removes useless commented function --- firmware/targets/f7/ble_glue/hid_service.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index c04656688..f983ec13b 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -23,27 +23,6 @@ typedef struct { static HIDSvc* hid_svc = NULL; -// #define N_BYTE_PER_LINE 16 -// static void hexdump(uint8_t* data, uint32_t len) { -// uint32_t n_line = len / N_BYTE_PER_LINE + 1; -// char line[len * 3 + n_line + 1]; -// memset(line, 0, sizeof(line)); -// uint32_t i; -// for(i = 0; i < len; i++) { -// if(i % N_BYTE_PER_LINE == 0) { -// if(i != 0) { -// FURI_LOG_D(TAG, "%s", line); -// } -// memset(line, 0, sizeof(line)); -// } -// uint32_t line_len = strlen(line); -// snprintf(line + line_len, sizeof(line) - line_len, "%02X ", data[i]); -// } -// if(strlen(line) > 0) { -// FURI_LOG_D(TAG, "%s", line); -// } -// } - static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { SVCCTL_EvtAckStatus_t ret = SVCCTL_EvtNotAck; hci_event_pckt* event_pckt = (hci_event_pckt*)(((hci_uart_pckt*)event)->data); From 409a79ddee69acdc4dca47f75df575a3f69a4133 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sat, 11 Feb 2023 23:27:21 +0100 Subject: [PATCH 180/231] Fix chat & subghz settings --- applications/main/subghz/subghz_cli.c | 8 ++- .../targets/f7/furi_hal/furi_hal_subghz.c | 64 ++++++++++++++++--- .../f7/furi_hal/furi_hal_subghz_configs.h | 10 +++ 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/applications/main/subghz/subghz_cli.c b/applications/main/subghz/subghz_cli.c index c047a32b3..be7143643 100644 --- a/applications/main/subghz/subghz_cli.c +++ b/applications/main/subghz/subghz_cli.c @@ -602,7 +602,8 @@ static void subghz_cli_command_encrypt_raw(Cli* cli, FuriString* args) { furi_string_free(source); } -static void subghz_cli_command_chat(Cli* cli, FuriString* args) { +static void subghz_cli_command_chat(Cli* cli, FuriString* args, void* context) { + UNUSED(context); uint32_t frequency = 433920000; if(furi_string_size(args)) { @@ -795,7 +796,7 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) { } if(furi_string_cmp_str(cmd, "chat") == 0) { - subghz_cli_command_chat(cli, args); + subghz_cli_command_chat(cli, args, NULL); break; } @@ -853,6 +854,9 @@ void subghz_on_system_start() { cli_add_command(cli, "subghz", CliCommandFlagDefault, subghz_cli_command, NULL); + // psst RM... i know you dont care much about errors, but if you ever see this... incompatible pointer type :3 + cli_add_command(cli, "chat", CliCommandFlagDefault, subghz_cli_command_chat, NULL); + furi_record_close(RECORD_CLI); #else UNUSED(subghz_cli_command); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c index c2c238a13..0fe5d1878 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c @@ -1,6 +1,6 @@ #include #include - +#include #include #include #include @@ -390,7 +390,8 @@ uint8_t furi_hal_subghz_get_lqi() { /* Modified by @tkerby & MX to the full YARD Stick One extended range of 281-361 MHz, 378-481 MHz, and 749-962 MHz. - These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage! + These changes are at your own risk. The PLL may not lock and FZ devs have warned of possible damage + Set flag use_ext_range_at_own_risk in extend_range.txt to use */ bool furi_hal_subghz_is_frequency_valid(uint32_t value) { @@ -420,29 +421,69 @@ uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) { bool furi_hal_subghz_is_tx_allowed(uint32_t value) { bool is_extended = false; + bool is_allowed = false; // TODO: !!! Move file check to another place Storage* storage = furi_record_open(RECORD_STORAGE); FlipperFormat* fff_data_file = flipper_format_file_alloc(storage); - if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/dangerous_settings")) { - flipper_format_read_bool( - fff_data_file, "yes_i_want_to_destroy_my_flipper", &is_extended, 1); + if(flipper_format_file_open_existing(fff_data_file, "/ext/subghz/assets/extend_range.txt")) { + flipper_format_read_bool(fff_data_file, "use_ext_range_at_own_risk", &is_extended, 1); + flipper_format_read_bool(fff_data_file, "ignore_default_tx_region", &is_allowed, 1); } flipper_format_free(fff_data_file); furi_record_close(RECORD_STORAGE); - if(!(value >= 299999755 && value <= 350000335) && + switch(furi_hal_version_get_hw_region()) { + case FuriHalVersionRegionEuRu: + //433,05..434,79; 868,15..868,55 + if(!(value >= 433050000 && value <= 434790000) && + !(value >= 868150000 && value <= 868550000)) { + } else { + is_allowed = true; + } + break; + case FuriHalVersionRegionUsCaAu: + //304,10..321,95; 433,05..434,79; 915,00..928,00 + if(!(value >= 304100000 && value <= 321950000) && + !(value >= 433050000 && value <= 434790000) && + !(value >= 915000000 && value <= 928000000)) { + } else { + if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) { + if(value <= 321950000 && + ((furi_hal_subghz.preset == FuriHalSubGhzPresetOok270Async) || + (furi_hal_subghz.preset == FuriHalSubGhzPresetOok650Async))) { + furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable_au); + } + } + is_allowed = true; + } + break; + case FuriHalVersionRegionJp: + //312,00..315,25; 920,50..923,50 + if(!(value >= 312000000 && value <= 315250000) && + !(value >= 920500000 && value <= 923500000)) { + } else { + is_allowed = true; + } + break; + + default: + is_allowed = true; + break; + } + // No flag - test original range, flag set, test extended range + if(!(value >= 299999755 && value <= 348000335) && !(value >= 386999938 && value <= 464000000) && !(value >= 778999847 && value <= 928000000) && !(is_extended)) { - FURI_LOG_I(TAG, "Frequency blocked - outside default range"); + FURI_LOG_I(TAG, "Frequency blocked - outside regional range"); return false; } else if( !(value >= 281000000 && value <= 361000000) && !(value >= 378000000 && value <= 481000000) && !(value >= 749000000 && value <= 962000000) && is_extended) { - FURI_LOG_I(TAG, "Frequency blocked - outside dangerous range"); + FURI_LOG_I(TAG, "Frequency blocked - outside extended range"); return false; } @@ -450,8 +491,11 @@ bool furi_hal_subghz_is_tx_allowed(uint32_t value) { } uint32_t furi_hal_subghz_set_frequency(uint32_t value) { - furi_hal_subghz.regulation = SubGhzRegulationTxRx; - + if(furi_hal_region_is_frequency_allowed(value)) { + furi_hal_subghz.regulation = SubGhzRegulationTxRx; + } else { + furi_hal_subghz.regulation = SubGhzRegulationTxRx; + } furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle); uint32_t real_frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, value); cc1101_calibrate(furi_hal_subghz.spi_bus_handle); diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h index b2b5760fd..5ea17b6dd 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz_configs.h @@ -273,6 +273,16 @@ static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = { 0x00, 0x00}; +static const uint8_t furi_hal_subghz_preset_ook_async_patable_au[8] = { + 0x00, + 0x37, // 12dBm 0xC0, 10dBm 0xC5, 7dBm 0xCD, 5dBm 0x86, 0dBm 0x50, -6dBm 0x37, -10dBm 0x26, -15dBm 0x1D, -20dBm 0x17, -30dBm 0x03 + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00}; + static const uint8_t furi_hal_subghz_preset_2fsk_async_patable[8] = { 0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12 0x00, From f385a9b5314fb0099265d23fcebf7087d6d13b9e Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sat, 11 Feb 2023 23:31:55 +0100 Subject: [PATCH 181/231] wording --- applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index b645c0d64..8263d387c 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -228,7 +228,7 @@ void xtreme_app_scene_main_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Sort Dirs Before", + "Force Dirs first", 2, xtreme_app_scene_main_sort_folders_before_changed, app); From 28db43faba610efe84e54dbaeecf1ac927fa7493 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sat, 11 Feb 2023 23:34:53 +0100 Subject: [PATCH 182/231] fix dithering on u2f / usb --- .../custom/NSFW/Icons/U2F/Auth_62x31.png | Bin 1864 -> 1702 bytes .../NSFW/Icons/U2F/Connect_me_62x31.png | Bin 1895 -> 1690 bytes .../custom/NSFW/Icons/U2F/Connected_62x31.png | Bin 1874 -> 1701 bytes .../custom/NSFW/Icons/U2F/Error_62x31.png | Bin 1863 -> 1763 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Auth_62x31.png index dd220bb65104666cb19752a96fe716cc44dffb68..255e391a47de398bedd198c7bbca1fa42f3cc832 100644 GIT binary patch delta 1664 zcmV-`27md;4yFx|Ie%D5L_t(|+P#=tNL5=DhId=dGB25Fq2?`7^PZOIG0Vg|6o|Ko zHU??>pea%aULr;5(Lf<6Uwp{7e9|72UL?*T!j}|L!B8u2TPv@rX`N38t`_Ps5A5Y+ zv-ezc%{A9Q#{9=W#@zbC!omv|F5J9%)55~S%gf8r(UHrOCx1`sbh<-ZP2HEK-)!o; zd|O*vo12@{F*7sE&CUI#anrwl|Ng{@6FogWKYsl9^y$;LZ{LQ8hrfLJB50JxRCWL7 zW@8S01^wdUqL{S4zTV&8uhY2cCnqQS`uYY32f0{XUB!TznHe!l4cgMuQh?IOwYIjl z_V)Jl*(>!-R(}vnMn*<7hYr@IrKOdX6*U1A4k(@A;9!W9l9ECvCMM?M#fv2+B>@2e zm;`!dCW}b_=dk-N|Wzt}th4Xo&rpo0~g)_*bpO%)EnwnZ&U5$}K{jXoYio%eXwS~?rVKCbb%fX5l^YZeVo12}T zodJPW5CBhO3CWAgr%#_k^vcT0=g*(#IA@DY(cZhBSAqy2?+_faJbAMBqOcB)HzCSheXmCZ zflj1S^dS))9Ub-c^`LKWZtd)1p)t;lH6n2Hpfd@*mQHn4pGaN2r1hzIycvvBN5+Qa0}A zHEnHeN+Q$N>%`yp@84ksoPpMqQ1}>oq~PWo-2V#-(B%|z5H$=~F)Bh)uiah)LnH` z6X;;&XL@=X9}}kNBjlzoP)PFf&2r$hb?3!P=Xj2!XFf!3N|}C8=>5`G*Tm{I5`a}|B#u`>v?~V{Q759r+m2F z?sXh2lmI;-5JwJgK%qlY#G@os_z%S{Fb(X)qYn1(uMH^90ZdVqw>rGiP)M*!~41;s?*ecO*jq0000< KMNUMnLSTZet28zM delta 1828 zcmV+<2iy3j4ag3VIe+>|L_t(|+P#=*OjK7C$A^(!zzvtkB8U|P5m2#EHX$rxV~wFM zq1uvwzz0xLYiR(98VG5>V5^Zt+YdIuCT*l?BmH3OhtkHSYT5#w04an;WK%(0P!#BI zIQn=#1fCBihBP}g$U3sq`mU}ngTc_y(7?^$;2;Lvx^-)0 zw5dVc*w_eA`hU3A$;oNXoH_K_EA>oP5KCHGTFed|tPdVMc=YIzng9w1l+LPEs~}Qh zVj`W0h=}0e;GCQsKR-WA0=+VmMWo->)|TIwXI|(%Jw1+&j^iI_2y^Gooh3__czb($ zd3nv8In&?!u5=O-F=7kPxMHU32c#aV}J3Hg!X9NHZBT$Q{9UUEz zhe5V0Iy#yTTY;xxKgKYDX_&-DnHM@(K_Gq-tc>742A3~izHs3}HH8R?&dyH!8Xq6e z$ga-!bAR=paErFgnl+06NJ&Y-Y+GAf#Oif~>Fn9Fd6{^fm=iPDjRgx9Jbd_2Ariji z5zN`KV~59gp2`xjv9U-Yh~W~ya&>jZU}TEEd7-1h0ODo14N8FZ$&)8I7+>DIcaOfz z!z5;+`gFp=!cM!Cf7G=vr+sh6zv&?%AEF zzqhwn6o$mC#!c8Omr2m;c_j>HyJ0z4Gcq!E_obg&`Ss$(ivfXE5CBhO3CWAwva&LW zUVl(faPs8I54uw})FgUUEcV*#27S5YD53$eA&W3($U|R2Pg(v1x z7>PwXa1@5%tqQL*1TMJ~9us_w$Hc_s<$vY*`1s&*7=j3nUn?mo34XHzg5&P)t~glj z^6Apu;$!1AA@rfqhF_sIw+RUeMMXthw{8V%WMm{%Arh!1vj(#F@86%9nF+_D{#ZYL zt$gc(FTKBt{WFd|#AeM?4Pp(2{*Tv%;^2cVXf}Do-`_tjE)E;QFGeJ{zXxGp0)OQS zvlFT}#Qhb2#O3qJ7ln0byau<1>(gp4^t~Pt1UivQ(T7BwJ9nI+%f|e!QHS6;WBf|R`uquc)_%| zdZ=hT^93_fzP^ z-@w2CtbjAnni2{hV~?@m&d=u)+-fK<16@ub8ig?}?x!^jSmhi7^jHEf=u_}1q>g~nxda@7_jnn^X5%_OqimNkej+dA<3DL3Ic$l;F#S}!`uI;e+*pZ;x_nO zV-de%#ft9k?#YzSVj7yL>1b0DU$7icgC0wu5IG2vFd~x}d8d9eG)PTNJPo7z}cXlhdH`57{KDGF@rEkfpoW zI(BiZ^*Rm~N`M{^h$DwLpwJ;H;!zT+a=4au#Htjfhf%nkNBmI!^y}&AF>TV(gsB!f z-jyEjhAPd?&FBmYgTQenqSgfvmh7tDIaXj*Rf+6yo&Y*QEFJ7}b92+Z#`YhSD+?h^ S+HHjZ0000)w diff --git a/assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png b/assets/dolphin/custom/NSFW/Icons/U2F/Connect_me_62x31.png index 495e8ab55c1e2dabb547151c2319f321f4af13bb..a8cfe1efbe26f5b48a039df8c6ed3280402ddc69 100644 GIT binary patch delta 1653 zcmV-*28#LT4w?;+IDZCANklIVQ3QuDF?>`|5YiVP^i7}C2cwtpMZ}ksP(iunlyjCtYW97t z*=L`9{%h_3`meRGac*wzjT<)}KYo1X%o$HlPdht1Zf$LCjem@c{@H8lzBJ?ArhdxR z%*^cc>C<#hojR47nfb4toAHMaA1+?JI5;@Cy}dm-Ir;VLSB*xqw6r8>l*Uwb|JQC~ z4t)jv+S;0!w6n7_JUncq=Vm-RJNxnD$I;PIZnn0zFko?UQOr_z!3qNLlVD{8|1s#}<8$fKCAB9Ai7#Ki;Me&0ct$cZGD=EH zxJ6qmEq^TufTvHNV)nUn=MXC$`Jk5FL&Ma{%8HMPeZ-uY!EV^w+yD6SL-7H=2$SpHq5!^+~W#+N%{rmTLLHAU8d<_E`_!9WY ze@F*nf*O(?p$aRZBGaXMQ76;`bE+}%x3RGSE8q;Yri8-B z*dqluS8)F?C_tA}h#`&|V3l(aC4V7_P>v3SFC%0q4-XG)lVjA;@h%-!qxkt-_-srM z|03GL4~00O>C+)tiAz*Mwh0Nx@ubp&gI_Vzqo!lbg=TXu&{uS2~+eD za#I&5Bx!GN2Np#^Lqh|nSoO=lRE=8^L|d-jZk-`CeSK0dCs5NUsCXz1$It5jXAW(cl_goMZ;2*;C^WCw^{g#${x z|KPy`Ds`feBWY!2Wqo~pZ*Q+mxOeX!nn-XL6ck_s9Ya8u0u`!&m@9}yC8 zsxXH}qRr0EmV||!h!PQu{SZvYW6?>?DroX>m`Ex>=x3@RW7Oul6 z5`q-y~Nj{IzcS#a&d7nB=r9P(clEqit3A_00000NkvXXu0mjfbuk+p delta 1860 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 U&pJX2P5=M^07*qoM6N<$f_%VAIRF3v delta 1827 zcmV+;2i*AM4aW|UIe+;{L_t(|+P#=djdb8Du#+%hpdv3v1@Q$%iGGJm zo*p@z)(#x*v4{J)pZj_4|8?E}>wjJMQ8F?zHg4RQn3!l|V}IlB?(XE|#O=3;WaG-*0Pc z+tJanbm`KF`bcMIXFoqbf|6Sa0IpcVh&bN5&|$5}fxSm_3G72mMqayh>+;&>cX!v zF)@tn?SFbZTltV%v}NABc?3XGQW9p{+1VjhQxT@~=g(&`@j5XlX0RKJ7cYMN__0bP ze90r2vwQb$*Kgd^PeesUA%!4@OT4vw`Em?Krs!K2IvNZh7Q<~&0<6!TJ;TBH^1*`# z^kp60Xuf=*t&IV zK|ujV3iW$?dqrVL%ocmp9D41z!jdqU?S|!GO-oDL*Oz)`&6mrTEdvBrK>$3BB_uCy z%YVwsAbMV2-s#h)-|0!((irD)e${(J2MY@ek(BK0Y>b3mxF9$<*aFaGcys905t(BQ zN_;8N$$B_=%^6=FtY5z#4j>fw@84%3Vt&+q_pn|5!i5VF`^?Nt=*I@8 z^zIu85}l@oupIpu-2Xxs0fD111aF=9IDbdrk~`rs!N+)HWMocGj+d7gE{7qApy`tF z@$vARFCaKBE-s_GqV~dK45)u-?8Wt7?Rfq&?$&7)_ zLx&Efr>DcQajUCb+q};`zli!Rnmxqk2?~g*@3pu^$Bh&RA8bLh$s=pmu8oe4#(##* zzlJ7uyai!k0_6&`V;iN zl$Vzm7Z*crnE{L7ZbrZ!c zbHldg?JpGH_4V~?zvYc<+DCki;%5?hVh+86w(yTa9MJUX5Ud0oDj@|K5{~0ZrP{Bs zOVVkLZ=p#1>ovXHQqqKnhfl7ym>XBX%D>yUZ{uUa6n%u;)CDR@&IO$(04NGh+L!8I z{Zj^|#HYER^B`e)SNPBm{D0b5#Cv;t_w@8k1hhHaVnpp+RzT^5OoxOJNtuR_eE;08VL2zH;S?kB<*k7hls^zbP9wY>-0`jwdV04iLL= zT|M}Tc{|&8QK=J!97&HHIZ|r(=`X=Q%Y-*C#n;r-5Zng_vnXBYOn(J*DNvybh`EAT zRFY~0l8m7T$nx{^sT8WKtM~8UPqF1+?zj275E2>ksz&G8fUmUTXf5axi(rM^Q~?|~ zsK!Vxa>{(AGF;N6`T~hIH8oWd7Iq>^L@@S4Fl!%+#yOh`n<2V@zziIZL?ShHb#>~G z6Vg_F`%>t|u-4XA`hPGAsgVMmyrxaJQvqE(3Ut^)Rsto6aUuLc(Wzi})$c?o$GoQ3 z>*W+Dr$O}>vMGg*gM|{H2L$5C;SDHsNQ!urgsL8{rJb-TLFr)>F6R+1%8z~qgJDXc z<6Y^oHdJYAYeQ#H7zBL}Z8a1ked$VI0?r6)UC^`afjU_Y&OT RXkY*U002ovPDHLkV1nRlfHMF9 From 0e50bf97075d7627438e763bd3e8cb2fc2699a77 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 22:58:35 +0000 Subject: [PATCH 183/231] Sort order wording (again) --- applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index 8263d387c..4b7d14ff2 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -228,7 +228,7 @@ void xtreme_app_scene_main_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Force Dirs first", + "Sort Dirs First", 2, xtreme_app_scene_main_sort_folders_before_changed, app); From 796930b12cc26d64b6df10e5d5b79101416763ce Mon Sep 17 00:00:00 2001 From: yocvito Date: Sun, 12 Feb 2023 00:28:03 +0100 Subject: [PATCH 184/231] remove useless func --- applications/main/bad_kb/bad_kb_script.c | 27 ++----------------- firmware/targets/f7/furi_hal/furi_hal_bt.c | 11 ++------ .../targets/f7/furi_hal/furi_hal_bt_hid.c | 8 ------ .../furi_hal_include/furi_hal_bt_hid.h | 12 ++------- 4 files changed, 6 insertions(+), 52 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index 9cf989eb8..98177209e 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -200,23 +200,7 @@ static inline void update_bt_timeout(Bt* bt) { LevelRssiRange r = bt_remote_rssi_range(bt); if(r < LevelRssiNum) { bt_timeout = bt_hid_delays[r]; - } -} - -/** - * @brief Wait until there are enough free slots in the keyboard buffer - * - * @param n_free_chars Number of free slots to wait for (and consider the buffer not full) -*/ -static void bt_hid_hold_while_keyboard_buffer_full(uint8_t n_free_chars, int32_t timeout) { - uint32_t start = furi_get_tick(); - uint32_t timeout_ms = timeout <= -1 ? 0 : timeout; - while(furi_hal_bt_hid_kb_free_slots(n_free_chars) == false) { - furi_delay_ms(100); - - if(timeout != -1 && (furi_get_tick() - start) > timeout_ms) { - break; - } + FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } } @@ -244,7 +228,6 @@ static bool ducky_is_line_end(const char chr) { static void ducky_numlock_on(BadKbScript* bad_kb) { if(bad_kb->bt) { if((furi_hal_bt_hid_get_led_state() & HID_KB_LED_NUM) == 0) { // FIXME - bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(HID_KEYBOARD_LOCK_NUM_LOCK); furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(HID_KEYBOARD_LOCK_NUM_LOCK); @@ -261,9 +244,7 @@ static bool ducky_numpad_press(BadKbScript* bad_kb, const char num) { if((num < '0') || (num > '9')) return false; uint16_t key = numpad_keys[num - '0']; - FURI_LOG_I(WORKER_TAG, "Pressing %c\r\n", num); if(bad_kb->bt) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(key); @@ -282,7 +263,6 @@ static bool ducky_altchar(BadKbScript* bad_kb, const char* charcode) { FURI_LOG_I(WORKER_TAG, "char %s", charcode); if(bad_kb->bt) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT); } else { furi_hal_hid_kb_press(KEY_MOD_LEFT_ALT); @@ -328,7 +308,6 @@ static bool ducky_string(BadKbScript* bad_kb, const char* param) { uint16_t keycode = BADKB_ASCII_TO_KEY(bad_kb, param[i]); if(keycode != HID_KEYBOARD_NONE) { if(bad_kb->bt) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(keycode); furi_delay_ms(bt_timeout); furi_hal_bt_hid_kb_release(keycode); @@ -441,7 +420,6 @@ static int32_t line_tmp = &line_tmp[ducky_get_command_len(line_tmp) + 1]; uint16_t key = ducky_get_keycode(bad_kb, line_tmp, true); if(bad_kb->bt) { - bt_hid_hold_while_keyboard_buffer_full(1, -1); furi_hal_bt_hid_kb_press(KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); furi_hal_bt_hid_kb_press(key); furi_delay_ms(bt_timeout); @@ -669,7 +647,7 @@ void bad_kb_bt_init(Bt* bt) { void bad_kb_bt_deinit(Bt* bt) { // release all keys - bt_hid_hold_while_keyboard_buffer_full(6, 3000); + // bt_hid_hold_while_keyboard_buffer_full(6, 3000); // stop ble bt_disconnect(bt); @@ -908,7 +886,6 @@ static int32_t bad_kb_worker(void* context) { } if(bad_kb->bt) { update_bt_timeout(bad_kb->bt); - FURI_LOG_D(WORKER_TAG, "BLE Key timeout : %u", bt_timeout); } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index e01669eb8..f8b7077f1 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -200,13 +200,6 @@ bool furi_hal_bt_start_app(FuriHalBtProfile profile, GapEventCallback event_cb, break; } GapConfig* config = &profile_config[profile].config; - if(strlen(&(profile_config[profile].config.adv_name[1])) == 0) { - // Set advertise name - strlcpy( - profile_config[profile].config.adv_name, - furi_hal_version_get_ble_local_device_name_ptr(), - FURI_HAL_VERSION_DEVICE_NAME_LENGTH); - } // Configure GAP if(profile == FuriHalBtProfileSerial) { // Set mac address @@ -429,8 +422,8 @@ float furi_hal_bt_get_rssi() { return val; } -/** fill the RSSI of the remote host of the bt connection and returns the time since - * the beginning of the connection +/** fill the RSSI of the remote host of the bt connection and returns the last + * time the RSSI was updated * */ uint32_t furi_hal_bt_get_conn_rssi(uint8_t* rssi) { diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 7623cf334..6921fbbc5 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -259,14 +259,6 @@ bool furi_hal_bt_hid_kb_press(uint16_t button) { ReportNumberKeyboard, (uint8_t*)kb_report, sizeof(FuriHalBtHidKbReport)); } -bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots) { - furi_assert(kb_report); - for(uint8_t i = 0; n_empty_slots > 0 && i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { - if(kb_report->key[i] == 0) n_empty_slots--; - } - return (n_empty_slots == 0); -} - bool furi_hal_bt_hid_kb_release(uint16_t button) { furi_assert(kb_report); for(uint8_t i = 0; i < FURI_HAL_BT_HID_KB_MAX_KEYS; i++) { diff --git a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h index b787c7c3e..56a8b4e48 100644 --- a/firmware/targets/furi_hal_include/furi_hal_bt_hid.h +++ b/firmware/targets/furi_hal_include/furi_hal_bt_hid.h @@ -86,18 +86,10 @@ bool furi_hal_bt_hid_consumer_key_release(uint16_t button); */ bool furi_hal_bt_hid_consumer_key_release_all(); -/** - * @brief Check if keyboard buffer has free slots - * - * @param n_emptry_slots number of empty slots in buffer to consider buffer is not full - * - * @return true if there is enough free slots in buffer -*/ -bool furi_hal_bt_hid_kb_free_slots(uint8_t n_empty_slots); - /** Retrieves LED state from remote BT HID host * - * (look at HID usage page to know what each bit of the returned byte means) + * @return (look at HID usage page to know what each bit of the returned byte means) + * NB: RFU bit has been shifted out in the returned octet so USB defines should work */ uint8_t furi_hal_bt_hid_get_led_state(void); From 7d2fdc5c6d20701a88dfd09112cd0c416a144be3 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sat, 11 Feb 2023 23:44:19 +0000 Subject: [PATCH 185/231] Hide files in browser while still loading --- applications/services/gui/modules/file_browser.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 175bab88d..ac494c7c8 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -518,19 +518,25 @@ static void browser_draw_list(Canvas* canvas, FileBrowserModel* model) { for(uint32_t i = 0; i < MIN(model->item_cnt, LIST_ITEMS); i++) { int32_t idx = CLAMP((uint32_t)(i + model->list_offset), model->item_cnt, 0u); - BrowserItemType item_type = BrowserItemTypeLoading; + BrowserItemType item_type; uint8_t* custom_icon_data = NULL; if(browser_is_item_in_array(model, idx)) { BrowserItem_t* item = items_array_get( model->items, CLAMP(idx - model->array_offset, (int32_t)(array_size - 1), 0)); item_type = item->type; - furi_string_set(filename, item->display_name); - if(item_type == BrowserItemTypeFile) { - custom_icon_data = item->custom_icon_data; + if(model->list_loading && item_type != BrowserItemTypeBack) { + furi_string_set(filename, "---"); + item_type = BrowserItemTypeLoading; + } else { + furi_string_set(filename, item->display_name); + if(item_type == BrowserItemTypeFile) { + custom_icon_data = item->custom_icon_data; + } } } else { furi_string_set(filename, "---"); + item_type = BrowserItemTypeLoading; } if(item_type == BrowserItemTypeBack) { From 9132fa2961bceac02a0e5d77e05e6d485279c830 Mon Sep 17 00:00:00 2001 From: yocvito Date: Sun, 12 Feb 2023 01:06:41 +0100 Subject: [PATCH 186/231] fix adv name length bug & finalize merge --- applications/services/gui/modules/file_browser_worker.c | 4 ---- firmware/targets/f7/api_symbols.csv | 7 +++---- firmware/targets/f7/furi_hal/furi_hal_bt.c | 1 + firmware/targets/furi_hal_include/furi_hal_version.h | 2 +- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index b29205116..216450295 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -92,13 +92,9 @@ static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, boo if(is_folder) { // Skip assets folders (if enabled) if(browser->skip_assets) { -<<<<<<< HEAD return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)) && ((furi_string_cmp_str(name, BADKB_LAYOUTS_DIR) == 0) ? (false) : (true)) && ((furi_string_cmp_str(name, SUBGHZ_TEMP_DIR) == 0) ? (false) : (true)); -======= - return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); ->>>>>>> upstream/dev } else { return true; } diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index d114736bc..a7f0f4790 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,15.1,, +Version,v,17.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, @@ -1028,7 +1028,6 @@ Function,+,furi_hal_bt_hid_consumer_key_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_consumer_key_release_all,_Bool, Function,+,furi_hal_bt_hid_get_led_state,uint8_t, -Function,+,furi_hal_bt_hid_kb_free_slots,_Bool,uint8_t Function,+,furi_hal_bt_hid_kb_press,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release,_Bool,uint16_t Function,+,furi_hal_bt_hid_kb_release_all,_Bool, @@ -1056,7 +1055,7 @@ Function,+,furi_hal_bt_serial_start,void, Function,+,furi_hal_bt_serial_stop,void, Function,+,furi_hal_bt_serial_tx,_Bool,"uint8_t*, uint16_t" Function,+,furi_hal_bt_set_key_storage_change_callback,void,"BleGlueKeyStorageChangedCallback, void*" -Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) - 1]" +Function,+,furi_hal_bt_set_profile_adv_name,void,"FuriHalBtProfile, const char[( 1 + ( 8 + 1 ) ) + 9 - 1]" Function,+,furi_hal_bt_set_profile_mac_addr,void,"FuriHalBtProfile, const uint8_t[( 6 )]" Function,+,furi_hal_bt_set_profile_pairing_method,void,"FuriHalBtProfile, GapPairing" Function,+,furi_hal_bt_start_advertising,void, @@ -3060,12 +3059,12 @@ Function,-,subghz_protocol_decoder_star_line_free,void,void* Function,-,subghz_protocol_decoder_star_line_get_hash_data,uint8_t,void* Function,-,subghz_protocol_decoder_star_line_get_string,void,"void*, FuriString*" Function,-,subghz_protocol_decoder_star_line_reset,void,void* +Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_encoder_alutech_at_4n_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_alutech_at_4n_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_alutech_at_4n_free,void,void* Function,-,subghz_protocol_encoder_alutech_at_4n_stop,void,void* Function,-,subghz_protocol_encoder_alutech_at_4n_yield,LevelDuration,void* -Function,-,subghz_protocol_decoder_star_line_serialize,_Bool,"void*, FlipperFormat*, SubGhzRadioPreset*" Function,-,subghz_protocol_encoder_ansonic_alloc,void*,SubGhzEnvironment* Function,-,subghz_protocol_encoder_ansonic_deserialize,_Bool,"void*, FlipperFormat*" Function,-,subghz_protocol_encoder_ansonic_free,void,void* diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt.c b/firmware/targets/f7/furi_hal/furi_hal_bt.c index f8b7077f1..79fbc693f 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt.c @@ -468,6 +468,7 @@ void furi_hal_bt_set_profile_adv_name( furi_assert(profile < FuriHalBtProfileNumber); furi_assert(name); + profile_config[profile].config.adv_name[0] = 0x09; memcpy( &(profile_config[profile].config.adv_name[1]), name, diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index a9865f500..63cb948db 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -17,7 +17,7 @@ extern "C" { #define FURI_HAL_VERSION_NAME_LENGTH 8 #define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) /** BLE symbol + name */ -#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) +#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) + 9 // for bad kb custom name /** OTP Versions enum */ typedef enum { From 631625d6c226ae1d3a8037d2012bac03f39dd184 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 00:21:56 +0000 Subject: [PATCH 187/231] Fix bad merge --- applications/services/gui/modules/file_browser_worker.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 216450295..4b7be70a1 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -15,8 +15,6 @@ #define TAG "BrowserWorker" #define ASSETS_DIR "assets" -#define BADKB_LAYOUTS_DIR "layouts" -#define SUBGHZ_TEMP_DIR "tmp_history" #define BROWSER_ROOT STORAGE_ANY_PATH_PREFIX #define FILE_NAME_LEN_MAX 256 #define LONG_LOAD_THRESHOLD 100 @@ -92,9 +90,7 @@ static bool browser_filter_by_name(BrowserWorker* browser, FuriString* name, boo if(is_folder) { // Skip assets folders (if enabled) if(browser->skip_assets) { - return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)) && - ((furi_string_cmp_str(name, BADKB_LAYOUTS_DIR) == 0) ? (false) : (true)) && - ((furi_string_cmp_str(name, SUBGHZ_TEMP_DIR) == 0) ? (false) : (true)); + return ((furi_string_cmp_str(name, ASSETS_DIR) == 0) ? (false) : (true)); } else { return true; } From 8dc77852f564cec65596d2f6e5c04787a70f689f Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 01:04:40 +0000 Subject: [PATCH 188/231] Update dithered compiled assets --- .../NSFW/Icons/U2F/Auth_62x31.bmx | Bin 257 -> 187 bytes .../NSFW/Icons/U2F/Connect_me_62x31.bmx | Bin 257 -> 188 bytes .../NSFW/Icons/U2F/Connected_62x31.bmx | Bin 257 -> 210 bytes .../NSFW/Icons/U2F/Error_62x31.bmx | Bin 257 -> 191 bytes 4 files changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Auth_62x31.bmx index 4a569c1eb27507a1301fa34d2ec5d96d62fe43be..a13ee8f9dbc1d18d7ed8bef91bf4dddd1206ac1b 100644 GIT binary patch literal 187 zcmV;s07U;j0000V000010IvZ3|NrywIsZWH1`s>_!0SKy-@)tV1Frvu2T$$)hdl?U z_k(K-uGaPkJ?;7s4@kfM9zRRp`Uf%toqtyXiUCH!0L{iAQvf;b;{j9fKxgro1V9dc zuy}#UE7kr!sHU}TX2mKSn#K7YW pf#2eP=nx!WkS_FJ`2z=^{x7_MhtfI^F?jFe;B!-yZrm3j0D!PLR=EHG literal 257 zcmcCxU|^63Vus)U|J!>`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tYz`Jw>Mf_uuaow2G_xpSDq; zYo|#3>Y%M&TBU-sCA_pIg(RtNzcnQ&bm13Kd)b9kJiK12zMi3FI8`f)^MACLpQdN3 z=)KufA{GS|X?~AhIooSW3e$h-uWP0ReU-W&JV|S%%1@Q@+Zy4Mync!Pn|*w;)=D4c zdgFhqTqaEuyBGh*d!?2x_y6g;CVMUVBl<7?uj(X`k4pPP|E*rK%8T>={%uROCQi`; E05YU{Gynhq diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connect_me_62x31.bmx index 582247d43e0c1cfbed6e9d8061ec26b7ad417339..0f5f60859803a15df56c3bced313dc2d83b69467 100644 GIT binary patch literal 188 zcmV;t07L&i0000V000010I&f4|NrywIsZWH1`s>_!0SKy-@)tV1C{U&JN_6PsDkmo z@aLd$wgboe!L@_TE<0P;eD}BLKs>Ne2*3UwKOi{X|Ij&*9P9eH6i^O%xWo!z2R*!C zs$g-S#$XWuIr_ok2QTp)LE;%HL<7AafIPdZVRB=09;1^ literal 257 zcmV+c0sj6z0000V00000`~UwxNs=T9{r~^Jk|aqI|Ns9#RaI3L|Nr-YBuSDa|3CGA zNs=T9|NrKHRaJcz|G&$Bk|b3r{eRPaB}r}t|NW(FNR?D7|G(LLtEB!2y?!8TBq@^> zm;X<{N>ZjGd;TjjQKhOBU#n8JN{Y%Qn;1$el2nokx7w6aRgu;xKOLczNUDk@>zFDr zl@k97|658w@~{~PtJl!ST;-&2w*DXEkcciSjeQYtwq z{+q{>ic(bte>487R7FxM-e3Gnk*Jg;|ChRxDiyT~{$KqiQleB8zg7OLL{*t0|G&1S HDoL^wQy6*i diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Connected_62x31.bmx index fae05bb57f5b883c6fd532a150899d556ce750cb..e4c689eb69f931275747eb2909bf99765ace1138 100644 GIT binary patch literal 210 zcmV;@04@JM0000V000010LB3Q|NrywIsZWH1`s>_!0SKy-@)tV1Frvu2T$$)hdl?U z_k(K-uGaPkJ?;7s4;}ynLs1}aj)v&Q9wE7;}9u;9AIz_ykJ;T z5A*_m8Gu9pgU0_8|3HM}KpTu-_*@?W=zsBj;jnB4A^%4~`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCB}#ki6tY!bdWlT`@4w$GXd+kjKW(GH zrK?5aR|jqN(oz+iE#aj#sl;1#`>iQKp$q?U*~>1R;<0L)$m91?1y!xebKX{VX$}fjR%WrFhPjdRI@^AL>$y%W& zx$2Got#T;|61*4x$9v_Y{aXL0@0#pY8p-l6{;%q!9YLD=L;tN-37x|AfB&|nt0q}# F0ssSEetQ4_ diff --git a/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx b/assets/resources/dolphin_custom/NSFW/Icons/U2F/Error_62x31.bmx index c91714b3f1114d842e45cb614c456d3b40b164c5..b6bc02baa4fff8f092d2e042090a3d2563f746ca 100644 GIT binary patch literal 191 zcmV;w06_mf0000V000010J8x7|NrywIsZWH1`s>_!0SKy-@)tV1Frvu2T$$)hdl?U z_k(K-uGaPi7#w4JeuM+c4;+jC;p6g$L$8169LNqa$UT2o0*V30aRbf9AX5N1{2)7c z#1aGt9|Qt_8Gr@I9zM9l0wf`QsMmj|Ns6;DxO0Bf&9?WP|^SYzt^h(CH~ugtp~~f|NpEW zEWZDAJxJZ(`pdPdAo;&bvpuG2iT>YzvwHQCV9C983fU@}>T=Wn`|tM(TCZ3APupl> zSB!Z4>Y%NwLUw7-mhjS=wCD@#_FGeeR7JmP*~>1SvSP(2(bqG)LRD3FD*un3G9|<- zj{n|lrO3`H>-4@yubmaNQe5`Gw2+pT>uRa{!ArwDwNy3BZ*xRXTH+z}Px|`gRY0xv zoBwHf3VCwfi~r+2X~`sw|I>E`FV$Kq^e_IOs;1`@mHkuyczXqD0^PYUbmf#FP5?Ek BcEJDu From d7e454a627696e98b9a88246366a331c3b10df8f Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 01:19:33 +0000 Subject: [PATCH 189/231] Fix api version tag --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index a7f0f4790..55773064f 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,v,17.0,, +Version,+,17.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, From 897ea3f90187b07f7493551339c54301a3715453 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 02:14:06 +0000 Subject: [PATCH 190/231] Remember bad kb connection mode --- applications/main/bad_kb/bad_kb_app.c | 2 ++ applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c | 3 +++ applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c | 3 +++ applications/services/xtreme/settings.h | 1 + 4 files changed, 9 insertions(+) diff --git a/applications/main/bad_kb/bad_kb_app.c b/applications/main/bad_kb/bad_kb_app.c index 0a47b7492..dfce5acbf 100644 --- a/applications/main/bad_kb/bad_kb_app.c +++ b/applications/main/bad_kb/bad_kb_app.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -97,6 +98,7 @@ BadKbApp* bad_kb_app_alloc(char* arg) { Bt* bt = furi_record_open(RECORD_BT); app->bt = bt; + app->is_bt = XTREME_SETTINGS()->bad_bt; const char* adv_name = bt_get_profile_adv_name(bt); memcpy(app->name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); memcpy(app->bt_old_config.name, adv_name, BAD_KB_ADV_NAME_MAX_LEN); diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c index 394783828..4412f0796 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_bt.c @@ -1,6 +1,7 @@ #include "../bad_kb_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" +#include enum VarItemListIndex { VarItemListIndexConnection, @@ -12,6 +13,8 @@ enum VarItemListIndex { void bad_kb_scene_config_bt_connection_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->is_bt = variable_item_get_current_value_index(item); + XTREME_SETTINGS()->bad_bt = bad_kb->is_bt; + XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); } diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c index fc265b64e..232ef8796 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_usb.c @@ -1,6 +1,7 @@ #include "../bad_kb_app_i.h" #include "furi_hal_power.h" #include "furi_hal_usb.h" +#include enum VarItemListIndex { VarItemListIndexConnection, @@ -10,6 +11,8 @@ enum VarItemListIndex { void bad_kb_scene_config_usb_connection_callback(VariableItem* item) { BadKbApp* bad_kb = variable_item_get_context(item); bad_kb->is_bt = variable_item_get_current_value_index(item); + XTREME_SETTINGS()->bad_bt = bad_kb->is_bt; + XTREME_SETTINGS_SAVE(); variable_item_set_current_value_text(item, bad_kb->is_bt ? "BT" : "USB"); view_dispatcher_send_custom_event(bad_kb->view_dispatcher, VarItemListIndexConnection); } diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index 9fd5f5c53..7f93e6071 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -23,6 +23,7 @@ typedef struct { uint16_t anim_speed; bool sort_ignore_dirs; bool status_bar; + bool bad_bt; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); From 25142698a66cfe7aa66d72c2f57d9b3d7834ff4a Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 04:20:07 +0000 Subject: [PATCH 191/231] Mark debug app as debug not external --- applications/debug/example_custom_font/application.fam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/debug/example_custom_font/application.fam b/applications/debug/example_custom_font/application.fam index 02285b8a0..06c0a7f61 100644 --- a/applications/debug/example_custom_font/application.fam +++ b/applications/debug/example_custom_font/application.fam @@ -1,7 +1,7 @@ App( appid="example_custom_font", name="Example: custom font", - apptype=FlipperAppType.EXTERNAL, + apptype=FlipperAppType.DEBUG, entry_point="example_custom_font_main", requires=["gui"], stack_size=1 * 1024, From e805b9b151101cece847107ec29fe7dc6f82bbf7 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sun, 12 Feb 2023 08:04:33 +0100 Subject: [PATCH 192/231] add basepath to bad-kb layouts selector --- applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c index 006ad31bd..3842c59fa 100644 --- a/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c +++ b/applications/main/bad_kb/scenes/bad_kb_scene_config_layout.c @@ -3,6 +3,8 @@ #include "furi_hal_usb.h" #include +#define KEYBOARD_FOLDER "/ext/badkb/layouts" + static bool bad_kb_layout_select(BadKbApp* bad_kb) { furi_assert(bad_kb); @@ -17,6 +19,7 @@ static bool bad_kb_layout_select(BadKbApp* bad_kb) { DialogsFileBrowserOptions browser_options; dialog_file_browser_set_basic_options( &browser_options, BAD_KB_APP_LAYOUT_EXTENSION, &I_keyboard_10px); + browser_options.base_path = KEYBOARD_FOLDER; // Input events and views are managed by file_browser bool res = dialog_file_browser_show( From 54019b46ca003130b42c68cddbf309c5cb8235db Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 07:39:15 +0000 Subject: [PATCH 193/231] Fix file browser clownage comment --- applications/services/gui/modules/file_browser_worker.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 4b7be70a1..e9877f277 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -226,13 +226,15 @@ static bool // break; // } - // FLIPPER DEVS MOMENT - // this used to load the file list in chunks, and then sort it... + // ROGUE MASTER MOMENT + // this used to load the file list in chunks, which makes sense + // but then RM made it sort the files, still in chunks... // so while scrolling, it loads more files and sorts them... // chances are, the new files are higher in the sorted list... // so the files keep shifting around while scrolling... - // now this does something intelligent and loads all in one go. + // now this does something intelligent: loads and sorts all in one go. // might take a few milliseconds longer, but atleast it works :kekw: + // and yes skotopes, most definitely a "limitation of fatfs driver" :roflmao: UNUSED(offset); UNUSED(count); if(browser->list_load_cb) { From 9b16c2178bfdef62c4b7edff014fd0db6463a6fe Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 11:15:11 +0000 Subject: [PATCH 194/231] Fix file browser back history --- .../main/archive/helpers/archive_browser.c | 3 ++- .../services/gui/modules/file_browser.c | 27 ++++++++++++++----- .../gui/modules/file_browser_worker.c | 4 +-- .../gui/modules/file_browser_worker.h | 1 + 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 9cb66a5e5..fd3d5957e 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -57,8 +57,9 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) { } static void - archive_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) { + archive_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last) { furi_assert(context); + UNUSED(idx); ArchiveBrowserView* browser = (ArchiveBrowserView*)context; if(!is_last) { diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index ac494c7c8..2abf26142 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -35,6 +35,7 @@ typedef enum { } BrowserItemType; typedef struct { + uint32_t unsorted_idx; FuriString* path; BrowserItemType type; uint8_t* custom_icon_data; @@ -42,6 +43,7 @@ typedef struct { } BrowserItem_t; static void BrowserItem_t_init(BrowserItem_t* obj) { + obj->unsorted_idx = 0; obj->type = BrowserItemTypeLoading; obj->path = furi_string_alloc(); obj->display_name = furi_string_alloc(); @@ -49,6 +51,7 @@ static void BrowserItem_t_init(BrowserItem_t* obj) { } static void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->unsorted_idx = src->unsorted_idx; obj->type = src->type; obj->path = furi_string_alloc_set(src->path); obj->display_name = furi_string_alloc_set(src->display_name); @@ -61,6 +64,7 @@ static void BrowserItem_t_init_set(BrowserItem_t* obj, const BrowserItem_t* src) } static void BrowserItem_t_set(BrowserItem_t* obj, const BrowserItem_t* src) { + obj->unsorted_idx = src->unsorted_idx; obj->type = src->type; furi_string_set(obj->path, src->path); furi_string_set(obj->display_name, src->display_name); @@ -161,7 +165,7 @@ static void browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); static void browser_list_load_cb(void* context, uint32_t list_load_offset); static void - browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last); + browser_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last); static void browser_long_load_cb(void* context); static void file_browser_scroll_timer_callback(void* context) { @@ -416,12 +420,13 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) { } static void - browser_list_item_cb(void* context, FuriString* item_path, bool is_folder, bool is_last) { + browser_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last) { furi_assert(context); FileBrowser* browser = (FileBrowser*)context; BrowserItem_t item; item.custom_icon_data = NULL; + item.unsorted_idx = idx; if(!is_last) { item.path = furi_string_alloc_set(item_path); @@ -468,10 +473,23 @@ static void browser->view, FileBrowserModel * model, { + FuriString* selected = NULL; + if(model->item_idx > 0) { + selected = furi_string_alloc_set(items_array_get(model->items, model->item_idx)->path); + } items_array_sort(model->items); + if(selected) { + for(uint32_t i = 0; i < model->item_cnt; i++) { + if(!furi_string_cmp(items_array_get(model->items, i)->path, selected)) { + model->item_idx = i; + break; + } + } + } model->list_loading = false; }, true); + browser_update_offset(browser); } } @@ -670,10 +688,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { if(browser_is_item_in_array(model, model->item_idx)) { selected_item = items_array_get(model->items, model->item_idx - model->array_offset); - select_index = model->item_idx; - if((!model->is_root) && (select_index > 0)) { - select_index -= 1; - } + select_index = selected_item->unsorted_idx; } }, false); diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index e9877f277..28a968428 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -247,13 +247,13 @@ static bool furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); if(browser->list_item_cb) { browser->list_item_cb( - browser->cb_ctx, name_str, (file_info.flags & FSF_DIRECTORY), false); + browser->cb_ctx, name_str, items_cnt, (file_info.flags & FSF_DIRECTORY), false); } items_cnt++; } } if(browser->list_item_cb) { - browser->list_item_cb(browser->cb_ctx, NULL, false, true); + browser->list_item_cb(browser->cb_ctx, NULL, 0, false, true); } ret = true; } while(0); diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 3b4be6aa7..19a9337ff 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -17,6 +17,7 @@ typedef void (*BrowserWorkerListLoadCallback)(void* context, uint32_t list_load_ typedef void (*BrowserWorkerListItemCallback)( void* context, FuriString* item_path, + uint32_t idx, bool is_folder, bool is_last); typedef void (*BrowserWorkerLongLoadCallback)(void* context); From 205da2920e910393e4005e02b40909235cc17a17 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 11:16:12 +0000 Subject: [PATCH 195/231] File browser load speed improvements (skip redraw) --- applications/main/archive/views/archive_browser_view.c | 2 +- applications/services/gui/modules/file_browser.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index 9351f687b..c7d0e14f2 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -482,7 +482,7 @@ static bool archive_view_input(InputEvent* event, void* context) { model->scroll_counter = 0; } }, - true); + false); archive_update_offset(browser); } diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 2abf26142..7eae1dfaf 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -354,7 +354,7 @@ static void browser_update_offset(FileBrowser* browser) { CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0); } }, - false); + true); } static void @@ -386,7 +386,7 @@ static void model->list_loading = true; model->folder_loading = false; }, - true); + false); browser_update_offset(browser); file_browser_worker_load(browser->worker, load_offset, ITEM_LIST_LEN_MAX); @@ -462,7 +462,7 @@ static void items_array_push_back(model->items, item); // TODO: calculate if element is visible }, - true); + false); furi_string_free(item.display_name); furi_string_free(item.path); if(item.custom_icon_data) { @@ -488,7 +488,7 @@ static void } model->list_loading = false; }, - true); + false); browser_update_offset(browser); } } @@ -673,7 +673,7 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { model->scroll_counter = 0; } }, - true); + false); browser_update_offset(browser); consumed = true; } From f15e3680bd6bf1a0ce107f4997e1ca587eef1278 Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 12 Feb 2023 16:48:41 +0100 Subject: [PATCH 196/231] godspeed unauthorized --- .../resources/subghz/Jamming/Jam_315.00.sub | 216 ------------------ .../resources/subghz/Jamming/Jam_433.42.sub | 15 -- .../resources/subghz/Jamming/Jam_433.92.sub | 16 -- .../resources/subghz/Jamming/Jam_438.90.sub | 16 -- .../resources/subghz/Jamming/Jam_868.35.sub | 16 -- 5 files changed, 279 deletions(-) delete mode 100644 assets/resources/subghz/Jamming/Jam_315.00.sub delete mode 100644 assets/resources/subghz/Jamming/Jam_433.42.sub delete mode 100644 assets/resources/subghz/Jamming/Jam_433.92.sub delete mode 100644 assets/resources/subghz/Jamming/Jam_438.90.sub delete mode 100644 assets/resources/subghz/Jamming/Jam_868.35.sub diff --git a/assets/resources/subghz/Jamming/Jam_315.00.sub b/assets/resources/subghz/Jamming/Jam_315.00.sub deleted file mode 100644 index c74aa39ce..000000000 --- a/assets/resources/subghz/Jamming/Jam_315.00.sub +++ /dev/null @@ -1,216 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 315000000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: 124 -73 55 -122 115 -171 97 -54 108 -66 73 -86 63 -58 65 -76 79 -80 59 -331 75 -83 110 -66 71 -62 115 -88 89 -78 303 -205 146 -52 79 -123 310 -107 55 -127 200 -88 130 -183 79 -54 118 -98 75 -445 75 -157 114 -188 121 -205 61 -124 288 -206 97 -154 73 -269 53 -60 192 -78 65 -131 111 -119 180 -134 236 -190 129 -191 67 -164 75 -88 207 -60 164 -87 91 -115 243 -97 326 -80 79 -66 79 -194 61 -135 147 -108 289 -62 67 -134 181 -248 173 -123 67 -101 290 -144 151 -123 85 -184 430 -106 109 -130 186 -99 154 -72 61 -81 73 -69 194 -52 180 -69 334 -74 51 -62 129 -54 51 -264 77 -186 59 -629 114 -414 181 -100 160 -52 72 -94 81 -70 151 -113 342 -92 69 -343 95 -78 87 -94 195 -52 105 -64 129 -193 53 -56 104 -168 175 -52 169 -82 63 -58 67 -254 87 -70 137 -106 200 -90 163 -108 125 -118 53 -120 79 -97 53 -137 85 -119 109 -88 137 -54 87 -72 75 -60 209 -54 51 -178 319 -52 129 -128 130 -177 78 -68 94 -68 53 -90 65 -95 103 -54 498 -85 84 -88 170 -70 155 -135 88 -72 244 -92 115 -141 89 -131 59 -104 53 -79 149 -58 83 -66 117 -54 51 -175 340 -84 61 -223 61 -80 69 -70 208 -64 115 -54 53 -132 130 -183 77 -204 69 -62 77 -62 79 -52 65 -176 256 -240 256 -164 85 -119 125 -237 89 -72 135 -286 103 -95 99 -60 114 -122 87 -56 215 -74 303 -56 57 -217 59 -206 214 -199 75 -60 71 -64 75 -78 89 -82 346 -257 65 -78 53 -173 63 -108 175 -52 136 -241 91 -56 75 -112 264 -64 93 -64 91 -62 51 -104 119 -68 85 -137 222 -52 162 -84 84 -284 93 -247 214 -369 71 -228 226 -108 69 -86 212 -54 213 -93 198 -84 127 -194 59 -90 77 -72 67 -118 278 -60 162 -170 160 -100 107 -204 53 -78 152 -100 219 -166 138 -150 81 -74 113 -78 59 -58 266 -66 71 -154 142 -58 53 -76 71 -169 174 -72 51 -219 63 -152 57 -125 307 -94 85 -122 136 -62 65 -132 111 -338 163 -145 156 -83 127 -162 142 -253 67 -110 65 -94 160 -248 101 -148 71 -400 120 -149 109 -109 297 -317 153 -52 157 -65 97 -239 236 -52 143 -60 59 -98 97 -72 53 -284 77 -191 113 -96 232 -257 92 -78 75 -78 119 -93 107 -126 84 -58 299 -156 179 -144 180 -60 83 -299 231 -157 191 -74 85 -74 102 -111 177 -58 194 -52 180 -60 131 -58 59 -93 427 -66 -RAW_Data: 135 -60 59 -227 95 -70 87 -74 212 -60 125 -88 176 -60 107 -54 155 -85 109 -78 103 -136 65 -119 87 -150 61 -62 83 -130 51 -98 144 -230 81 -456 69 -110 233 -144 83 -116 61 -54 79 -123 51 -70 138 -54 155 -104 305 -76 107 -66 212 -103 187 -253 307 -88 67 -174 162 -98 180 -140 170 -82 69 -167 118 -60 75 -52 51 -80 117 -173 95 -289 131 -126 141 -94 183 -76 202 -171 89 -80 111 -214 177 -94 126 -70 69 -194 103 -96 154 -106 67 -158 127 -60 157 -68 130 -58 77 -102 220 -64 591 -273 222 -60 172 -123 101 -58 257 -108 78 -166 71 -62 204 -52 107 -195 57 -52 79 -72 87 -129 147 -82 90 -70 75 -70 254 -108 171 -197 141 -115 77 -308 142 -84 71 -56 228 -98 95 -64 51 -108 154 -286 89 -137 117 -116 244 -62 265 -119 71 -52 170 -121 63 -385 299 -164 140 -86 79 -190 150 -194 75 -236 177 -106 133 -99 167 -79 55 -298 154 -62 63 -62 121 -96 115 -66 77 -117 73 -197 82 -171 73 -173 53 -111 69 -60 107 -64 427 -84 83 -202 71 -88 123 -152 69 -77 123 -74 81 -113 135 -62 82 -82 257 -170 97 -107 61 -88 96 -174 51 -52 262 -52 61 -60 153 -120 148 -203 243 -90 96 -58 360 -92 167 -99 53 -106 178 -84 75 -116 157 -145 51 -249 71 -54 238 -131 142 -160 112 -88 59 -52 51 -85 69 -98 53 -128 75 -52 112 -224 149 -84 98 -78 93 -132 136 -104 139 -96 139 -110 57 -54 155 -88 67 -102 225 -79 87 -193 246 -84 67 -93 150 -78 422 -224 55 -140 53 -91 61 -149 234 -66 75 -143 186 -139 247 -174 96 -62 226 -52 127 -145 67 -62 130 -267 121 -121 67 -182 97 -152 228 -52 482 -116 89 -131 53 -78 209 -54 149 -54 285 -133 144 -78 370 -88 59 -100 111 -120 53 -76 81 -86 166 -84 115 -81 77 -92 111 -52 71 -107 239 -188 113 -157 59 -115 51 -135 51 -80 51 -162 223 -68 69 -132 212 -149 241 -70 59 -84 199 -74 120 -62 250 -74 97 -78 210 -118 416 -195 77 -126 129 -68 150 -194 77 -112 81 -177 75 -212 131 -375 71 -102 137 -126 57 -66 89 -84 53 -907 363 -560 97 -492 65 -1152 67 -630 131 -402 197 -730 165 -300 99 -934 131 -1092 97 -164 230 -264 231 -462 163 -130 131 -228 99 -130 99 -464 199 -166 365 -298 199 -300 65 -264 197 -134 65 -332 99 -100 97 -666 65 -132 97 -234 163 -166 231 -332 65 -464 165 -266 97 -200 65 -960 -RAW_Data: 331 -164 67 -498 65 -666 97 -500 131 -298 399 -166 199 -332 97 -100 65 -232 99 -132 435 -168 693 -100 131 -232 829 -798 199 -68 99 -66 727 -68 131 -66 623 -98 263 -66 763 -66 65 -132 131 -332 631 -66 99 -164 65 -860 263 -100 267 -528 99 -532 133 -132 467 -134 167 -466 67 -98 499 -168 131 -66 99 -730 65 -132 65 -166 67 -100 893 -166 165 -66 463 -398 65 -464 99 -266 231 -396 67 -794 99 -730 131 -268 63 -132 99 -330 99 -728 197 -396 163 -800 97 -164 65 -1284 427 -430 65 -862 231 -298 97 -696 197 -166 99 -298 263 -1090 129 -494 299 -66 65 -200 65 -266 131 -66 2905 -100 597 -462 131 -428 265 -958 163 -528 65 -1098 99 -398 165 -232 133 -462 133 -428 331 -232 99 -134 65 -462 65 -98 195 -98 229 -856 163 -822 97 -100 65 -130 495 -232 65 -658 461 -394 99 -132 99 -200 131 -334 165 -166 265 -298 99 -100 297 -600 133 -66 97 -662 2157 -230 65 -662 97 -494 131 -164 229 -494 99 -166 97 -134 131 -464 199 -100 131 -830 133 -266 295 -432 131 -166 167 -166 99 -1092 199 -496 133 -198 99 -134 99 -830 131 -200 131 -232 63 -396 97 -268 233 -198 329 -332 65 -132 393 -100 163 -954 131 -660 99 -296 65 -266 163 -398 131 -364 2005 -134 729 -268 65 -100 65 -1788 99 -198 333 -202 1387 -66 555 -332 97 -66 97 -396 67 -266 65 -464 231 -696 65 -2040 131 -132 129 -196 97 -164 65 -196 195 -560 99 -330 163 -686 131 -162 99 -164 297 -100 131 -298 165 -466 131 -364 65 -532 99 -66 99 -1658 167 -432 163 -556 263 -296 65 -296 97 -132 63 -132 97 -164 65 -628 163 -98 97 -230 129 -66 1651 -792 99 -400 65 -562 65 -66 65 -98 97 -200 99 -764 99 -496 99 -100 197 -1026 165 -1392 327 -166 131 -202 165 -134 297 -696 263 -196 131 -462 165 -66 165 -166 363 -166 65 -296 65 -164 65 -920 131 -460 129 -166 97 -264 231 -396 265 -1096 65 -200 99 -132 133 -1262 131 -166 129 -100 131 -856 65 -760 97 -198 129 -560 329 -200 67 -366 65 -598 65 -330 65 -890 327 -166 97 -594 65 -1024 131 -298 97 -398 97 -166 65 -398 133 -632 165 -400 165 -1194 297 -266 233 -564 131 -100 131 -698 265 -134 99 -1028 131 -134 131 -300 263 -562 131 -66 99 -1196 1427 -166 1823 -100 99 -68 97 -198 331 -762 267 -132 99 -198 65 -100 231 -298 199 -132 131 -134 99 -994 97 -166 299 -66 99 -264 97 -564 229 -332 331 -198 99 -168 99 -232 97 -166 -RAW_Data: 65 -460 97 -558 393 -166 97 -162 197 -164 99 -164 131 -366 65 -66 165 -2538 97 -100 131 -298 331 -166 131 -134 65 -198 1319 -98 823 -230 259 -264 99 -790 525 -822 131 -690 65 -364 297 -66 99 -68 495 -66 165 -298 99 -530 665 -132 363 -98 1467 -598 131 -132 331 -132 199 -564 65 -100 65 -66 329 -362 163 -464 359 -264 97 -558 131 -66 65 -100 197 -162 65 -896 65 -830 99 -264 65 -800 131 -996 65 -532 65 -164 65 -266 65 -166 199 -694 99 -592 65 -1384 99 -66 163 -134 131 -200 2123 -332 131 -430 297 -198 65 -168 131 -468 99 -564 197 -366 65 -696 99 -166 99 -164 131 -564 263 -730 167 -828 97 -98 97 -464 65 -1150 97 -790 165 -398 65 -130 65 -66 131 -430 231 -234 99 -464 165 -132 133 -434 131 -430 131 -330 99 -364 99 -66 861 -100 1117 -132 1413 -426 293 -132 263 -596 99 -200 131 -334 199 -64 99 -132 99 -1226 297 -760 65 -198 65 -630 263 -534 663 -1490 99 -296 295 -164 97 -758 163 -396 363 -66 131 -1656 199 -1422 163 -1094 131 -626 163 -130 195 -132 129 -460 165 -166 131 -198 131 -362 163 -264 131 -330 229 -230 161 -428 197 -426 65 -264 65 -658 131 -164 97 -100 197 -66 97 -364 199 -532 2023 -166 931 -694 131 -498 65 -598 133 -264 233 -1620 231 -1892 165 -498 131 -824 265 -954 65 -558 231 -262 99 -426 65 -562 63 -298 263 -428 97 -660 99 -664 131 -202 131 -166 231 -1028 725 -134 1259 -298 397 -132 197 -134 99 -362 99 -528 197 -132 333 -66 633 -694 65 -396 133 -198 133 -730 131 -400 197 -234 199 -528 163 -1644 65 -854 199 -1162 97 -2618 165 -266 131 -166 65 -166 165 -498 99 -100 165 -100 99 -1092 99 -1426 99 -794 163 -68 199 -596 131 -730 233 -232 229 -264 67 -394 65 -658 429 -728 133 -394 97 -200 197 -166 1143 -100 129 -328 695 -134 365 -100 165 -100 197 -200 99 -268 333 -762 131 -1084 99 -264 131 -1218 97 -362 295 -230 97 -164 65 -132 131 -362 131 -166 131 -664 67 -954 67 -334 165 -1028 99 -762 131 -1810 97 -1382 65 -392 65 -230 99 -164 167 -266 229 -558 131 -166 67 -432 65 -230 65 -462 99 -232 165 -234 65 -268 65 -100 97 -164 133 -662 233 -430 227 -262 65 -132 99 -198 65 -200 263 -630 101 -332 65 -1060 731 -98 99 -1592 231 -1060 299 -962 861 -68 263 -166 65 -200 99 -66 763 -100 133 -692 131 -234 263 -296 97 -796 65 -166 131 -464 65 -262 63 -232 129 -232 97 -130 129 -720 65 -198 65 -164 -RAW_Data: 461 -1056 165 -266 99 -232 199 -498 131 -132 131 -234 395 -596 231 -166 99 -860 97 -590 3359 -264 163 -66 129 -692 129 -392 65 -266 97 -832 99 -398 65 -332 265 -166 297 -232 235 -498 99 -530 99 -562 361 -830 65 -98 99 -198 133 -332 97 -232 131 -498 263 -1230 165 -200 163 -334 131 -100 99 -362 65 -198 97 -532 133 -198 65 -996 65 -98 133 -762 65 -100 1325 -166 597 -198 65 -600 131 -232 361 -134 229 -566 429 -1162 131 -230 97 -132 263 -98 131 -462 129 -98 99 -328 99 -98 265 -66 131 -134 131 -564 99 -266 99 -298 163 -732 165 -1428 131 -660 131 -232 201 -168 97 -264 2369 -100 165 -166 199 -264 295 -464 295 -762 231 -462 229 -828 165 -1224 197 -364 99 -98 67 -66 97 -198 65 -398 67 -168 167 -302 131 -498 65 -334 67 -198 97 -132 65 -296 65 -100 97 -230 197 -360 99 -198 99 -200 197 -134 65 -232 99 -298 99 -130 131 -232 97 -164 97 -98 131 -196 165 -164 1419 -64 1643 -134 329 -364 131 -264 361 -558 97 -366 99 -132 393 -528 167 -330 99 -464 99 -532 233 -196 133 -68 201 -66 99 -132 65 -1892 165 -166 99 -334 197 -368 65 -530 65 -100 129 -132 99 -394 197 -558 165 -66 165 -66 363 -98 131 -200 331 -332 97 -168 133 -100 199 -564 165 -736 99 -100 99 -300 231 -826 67 -100 129 -166 229 -262 197 -230 261 -164 99 -560 197 -528 65 -264 131 -462 163 -98 131 -132 99 -66 163 -298 163 -166 393 -132 163 -98 361 -330 263 -164 229 -164 197 -496 163 -100 795 -134 165 -566 99 -164 99 -98 917 -98 165 -1678 199 -854 197 -134 199 -732 1193 -66 565 -98 365 -102 171 -566 131 -98 99 -660 65 -330 99 -634 65 -400 99 -98 99 -728 65 -132 131 -592 131 -400 99 -564 131 -166 99 -366 65 -632 65 -196 65 -886 231 -264 97 -788 129 -100 129 -590 199 -298 263 -296 131 -394 259 -266 199 -432 131 -862 99 -232 265 -2114 165 -134 65 -398 199 -628 133 -268 99 -996 131 -894 263 -230 131 -300 65 -100 99 -100 197 -730 97 -726 99 -132 97 -232 65 -298 99 -1254 65 -428 261 -492 65 -164 163 -164 131 -264 129 -1094 129 -66 395 -198 195 -198 231 -398 131 -2124 65 -98 97 -132 229 -196 131 -330 97 -100 65 -328 397 -562 263 -394 129 -196 65 -330 165 -164 261 -492 133 -132 133 -1122 65 -164 165 -854 65 -196 99 -66 195 -200 131 -632 131 -100 65 -200 133 -400 359 -426 63 -824 63 -232 129 -98 195 -590 99 -1166 65 -1282 65 -626 -RAW_Data: 131 -366 131 -400 961 -166 893 -134 297 -98 133 -134 99 -800 99 -198 131 -564 165 -200 99 -698 295 -664 263 -164 99 -298 133 -100 97 -232 99 -1030 67 -132 393 -166 131 -162 131 -790 65 -528 63 -266 329 -334 361 -300 165 -232 167 -464 65 -164 99 -298 65 -200 295 -592 963 -166 199 -336 165 -200 165 -200 131 -134 133 -298 2091 -168 133 -166 133 -66 299 -264 961 -164 163 -100 65 -98 361 -164 229 -358 131 -66 361 -464 65 -100 197 -66 333 -66 99 -464 65 -266 65 -100 297 -198 229 -100 97 -398 99 -530 99 -530 67 -1522 65 -364 131 -266 101 -500 97 -200 197 -532 67 -1286 131 -1580 97 -360 165 -600 131 -68 197 -364 65 -132 99 -298 229 -132 99 -100 99 -132 1693 -132 1813 -954 195 -394 163 -328 165 -196 99 -164 65 -164 129 -592 65 -332 131 -526 131 -198 167 -202 201 -132 265 -134 165 -132 197 -100 131 -66 99 -132 65 -66 131 -564 65 -200 99 -300 163 -496 65 -662 67 -228 133 -166 99 -232 97 -164 263 -164 295 -522 65 -100 97 -100 65 -428 129 -132 195 -198 263 -360 65 -628 165 -494 65 -264 2503 -66 665 -530 133 -166 97 -630 65 -364 229 -100 65 -362 99 -196 129 -66 263 -164 131 -1288 65 -130 63 -330 99 -166 165 -466 97 -132 165 -1444 263 -134 65 -234 99 -300 65 -300 165 -232 167 -296 361 -726 97 -132 65 -230 63 -132 97 -100 2143 -890 197 -492 263 -132 131 -164 65 -428 65 -230 65 -100 129 -262 99 -64 97 -166 231 -532 131 -270 263 -98 365 -166 165 -930 299 -166 99 -298 431 -266 65 -796 267 -232 65 -200 131 -234 65 -1062 133 -1258 67 -398 65 -1096 65 -398 67 -1590 99 -198 133 -498 99 -66 97 -166 65 -400 233 -166 133 -132 131 -766 65 -334 65 -232 131 -234 131 -266 131 -200 65 -1062 331 -364 65 -798 265 -500 165 -198 131 -1260 523 -858 229 -164 263 -164 65 -194 97 -132 129 -164 131 -492 97 -394 131 -164 131 -330 65 -230 229 -198 65 -328 131 -526 97 -100 131 -164 1089 -198 561 -66 793 -334 461 -130 99 -556 261 -198 97 -462 197 -432 63 -530 131 -496 331 -400 65 -396 65 -566 167 -432 65 -896 133 -66 99 -100 65 -232 97 -98 163 -262 63 -398 197 -924 133 -498 131 -398 97 -730 99 -66 99 -264 131 -134 65 -830 99 -264 65 -730 231 -398 165 -532 199 -132 131 -596 231 -592 99 -296 97 -588 165 -690 97 -228 229 -1686 65 -166 131 -362 197 -66 65 -494 163 -230 231 -100 293 -532 99 -364 167 -1160 -RAW_Data: 65 -134 97 -234 65 -100 131 -132 99 -1028 231 -100 165 -100 265 -328 65 -328 361 -230 131 -502 133 -66 163 -166 65 -398 229 -132 99 -228 97 -66 227 -230 63 -758 97 -134 65 -464 231 -334 263 -528 65 -922 199 -428 1221 -364 99 -198 99 -100 197 -664 197 -466 97 -1096 199 -66 131 -336 65 -164 99 -332 165 -764 361 -920 131 -230 99 -954 295 -592 163 -198 197 -296 131 -98 65 -1452 65 -198 99 -166 165 -964 197 -630 2221 -298 65 -732 197 -396 201 -134 165 -100 99 -234 65 -100 165 -768 97 -826 97 -466 99 -860 67 -230 97 -100 165 -726 293 -66 163 -296 131 -334 97 -134 231 -790 97 -264 99 -626 197 -164 129 -232 97 -98 131 -430 97 -792 65 -2086 2325 -100 665 -232 97 -262 163 -396 195 -958 165 -134 231 -434 131 -232 65 -332 233 -200 99 -302 99 -230 131 -134 165 -200 431 -264 267 -132 263 -530 99 -592 99 -268 231 -100 97 -100 97 -264 199 -134 165 -402 261 -132 1333 -66 231 -134 1163 -398 99 -232 99 -134 165 -466 165 -66 131 -396 165 -298 395 -596 99 -664 131 -302 163 -432 65 -298 133 -1816 99 -1158 131 -66 2345 -132 63 -198 195 -560 197 -858 131 -860 133 -166 99 -200 231 -560 65 -66 197 -198 165 -566 65 -166 99 -298 65 -168 231 -658 99 -196 65 -496 65 -528 163 -230 97 -2384 229 -132 395 -164 65 -592 65 -232 65 -300 165 -232 435 -890 65 -98 299 -334 65 -698 265 -430 231 -626 131 -498 1061 -200 233 -132 165 -166 729 -400 65 -400 65 -930 65 -768 65 -598 131 -660 165 -332 165 -364 65 -1088 99 -198 97 -666 99 -132 65 -166 133 -168 165 -794 65 -1842 97 -232 131 -628 133 -298 131 -430 67 -132 65 -264 63 -262 65 -692 131 -98 99 -360 131 -464 65 -198 99 -1166 99 -266 99 -132 99 -66 65 -698 99 -366 65 -1656 99 -132 65 -168 267 -66 265 -564 65 -732 65 -332 167 -200 97 -98 233 -134 65 -434 197 -368 197 -100 99 -462 99 -466 65 -132 131 -632 131 -266 65 -200 65 -100 397 -1188 261 -132 229 -264 197 -492 163 -98 197 -1118 263 -1714 197 -528 295 -230 261 -758 65 -198 427 -264 65 -230 65 -66 97 -296 65 -132 197 -132 195 -492 99 -230 129 -164 165 -632 65 -998 65 -232 165 -200 133 -666 199 -100 131 -362 131 -560 133 -368 165 -498 527 -296 65 -98 65 -662 165 -532 131 -1184 99 -294 99 -66 527 -132 165 -266 165 -68 499 -860 165 -100 99 -132 65 -994 529 -498 133 -166 99 -630 133 -264 99 -266 -RAW_Data: 131 -332 165 -430 131 -1020 195 -592 97 -164 227 -460 131 -264 131 -1156 1451 -66 621 -332 133 -398 99 -66 199 -330 131 -562 233 -898 97 -662 97 -264 163 -230 463 -132 165 -166 165 -732 131 -100 165 -432 65 -762 65 -334 65 -132 165 -364 99 -862 99 -332 97 -164 229 -130 97 -264 97 -198 397 -1942 2037 -66 361 -66 1187 -230 65 -528 131 -726 131 -658 789 -132 97 -230 97 -232 97 -788 97 -98 197 -590 197 -530 65 -234 65 -530 133 -230 65 -992 65 -396 99 -232 165 -200 67 -394 229 -1230 65 -164 131 -892 229 -590 3025 -264 97 -164 163 -198 131 -362 361 -330 97 -130 65 -262 99 -66 131 -98 329 -98 197 -1284 65 -166 331 -168 199 -398 97 -164 97 -66 99 -398 363 -398 131 -300 233 -964 99 -464 65 -832 199 -396 99 -166 131 -526 65 -594 165 -590 99 -890 131 -200 65 -262 65 -296 1287 -164 589 -100 97 -660 165 -200 297 -132 67 -198 199 -1790 99 -362 99 -196 99 -362 131 -130 163 -98 65 -788 165 -264 263 -328 131 -690 65 -854 199 -200 199 -166 97 -262 131 -426 65 -360 497 -266 99 -266 199 -564 165 -266 65 -498 65 -98 131 -428 67 -464 65 -1192 199 -200 99 -132 267 -166 165 -134 231 -100 133 -98 133 -168 97 -100 99 -994 99 -336 97 -428 65 -364 129 -626 99 -1416 197 -230 131 -132 267 -532 995 -132 67 -530 463 -68 735 -270 65 -564 397 -100 167 -1028 163 -430 65 -166 99 -464 99 -2022 99 -266 65 -168 299 -464 99 -166 263 -1360 99 -564 131 -66 65 -98 99 -330 133 -1744 131 -758 65 -824 99 -990 65 -330 99 -266 99 -98 65 -796 165 -660 131 -330 295 -166 165 -300 297 -98 65 -364 129 -628 99 -434 231 -1028 133 -232 65 -498 197 -930 163 -166 99 -1264 65 -164 263 -362 629 -66 297 -100 133 -132 165 -132 165 -332 395 -134 97 -132 97 -132 263 -130 495 -198 525 -98 429 -398 429 -100 133 -266 133 -66 699 -166 65 -430 265 -132 463 -100 595 -264 131 -198 361 -230 131 -264 97 -364 63 -166 65 -360 361 -200 97 -730 99 -164 163 -198 129 -2146 65 -996 427 -1062 65 -166 97 -628 131 -494 197 -264 489 -198 63 -330 99 -460 65 -692 99 -164 199 -366 99 -1390 199 -132 163 -360 97 -264 97 -396 97 -196 65 -228 65 -294 165 -366 97 -1098 65 -664 199 -100 67 -798 97 -366 65 -166 65 -362 331 -166 65 -634 67 -300 67 -528 67 -496 165 -564 65 -598 99 -302 99 -100 101 -298 99 -400 97 -1550 131 -458 65 -1484 97 -230 -RAW_Data: 99 -656 229 -432 131 -98 131 -366 167 -958 165 -630 767 -98 65 -198 527 -132 363 -132 161 -130 131 -300 165 -134 99 -202 97 -566 97 -732 131 -198 65 -534 65 -298 165 -332 131 -662 199 -68 263 -200 465 -66 167 -134 99 -268 99 -332 167 -232 1229 -66 133 -134 963 -428 99 -68 65 -1224 165 -234 65 -132 233 -332 331 -132 131 -100 65 -1660 131 -100 397 -100 65 -198 65 -596 99 -398 99 -1126 163 -1616 65 -294 65 -330 197 -754 199 -496 133 -928 231 -728 99 -1094 3657 -132 65 -464 133 -532 131 -564 99 -466 97 -168 197 -100 133 -730 63 -300 429 -370 131 -630 267 -532 97 -266 65 -200 67 -132 99 -394 131 -328 165 -1056 165 -198 165 -230 65 -130 65 -198 97 -460 133 -198 99 -232 67 -432 231 -98 67 -758 165 -494 97 -166 3081 -198 63 -198 163 -262 131 -534 99 -498 331 -430 65 -198 99 -498 131 -696 65 -166 165 -500 65 -626 263 -298 97 -100 129 -858 99 -296 97 -560 99 -98 197 -198 163 -200 297 -332 131 -68 65 -100 165 -332 131 -134 363 -232 67 -430 165 -166 101 -364 65 -462 167 -298 97 -200 65 -332 65 -134 1189 -66 295 -100 1127 -266 133 -896 165 -262 197 -1514 197 -360 131 -66 163 -330 97 -726 63 -66 195 -690 297 -928 199 -132 165 -232 97 -132 99 -564 99 -332 165 -268 197 -100 65 -498 67 -464 165 -132 333 -364 99 -100 165 -100 99 -134 97 -632 99 -102 131 -198 97 -466 99 -500 165 -132 99 -164 195 -294 2109 -330 199 -1262 165 -1094 197 -228 263 -658 133 -922 97 -264 97 -556 165 -262 99 -98 425 -986 131 -394 65 -494 131 -98 131 -460 97 -428 295 -792 129 -66 131 -132 65 -328 133 -132 165 -66 265 -200 97 -234 331 -100 65 -100 65 -328 1637 -254 271 -258 279 -282 227 -296 263 -270 641 -314 253 -256 253 -288 247 -238 259 -256 257 -294 233 -302 265 -234 299 -266 233 -300 231 -296 229 -264 255 -290 231 -300 399 -288 243 -5348 273 -274 269 -238 299 -4900 239 -244 271 -400 311 -252 249 -280 237 -266 259 -262 655 -280 255 -254 291 -388 241 -290 255 -254 249 -278 273 -240 301 -230 261 -296 265 -266 231 -264 257 -290 265 -266 231 -264 263 -262 295 -234 299 -234 293 -264 267 -266 233 -262 261 -262 295 -236 299 -266 235 -300 231 -262 263 -262 257 -290 263 -266 231 -266 261 -264 293 -268 273 -268 233 -264 261 -262 287 -266 269 -270 271 -268 231 -262 255 -258 291 -262 269 -268 231 -264 293 -234 301 -266 233 -300 231 -264 261 -262 -RAW_Data: 263 -262 255 -292 265 -266 263 -270 273 -236 259 -260 289 -262 267 -268 265 -236 301 -230 295 -264 233 -300 231 -262 263 -262 257 -258 291 -264 267 -266 265 -234 301 -266 235 -300 229 -264 261 -264 259 -296 229 -296 229 -262 295 -236 299 -264 269 -266 231 -264 261 -264 255 -258 291 -264 267 -274 269 -266 233 -300 231 -264 259 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -266 231 -264 263 -264 293 -234 301 -266 267 -266 231 -264 261 -262 255 -292 265 -266 231 -296 263 -270 271 -268 231 -264 263 -262 261 -262 257 -290 265 -266 231 -266 295 -234 299 -266 267 -268 231 -264 261 -260 263 -262 257 -290 265 -266 265 -270 271 -236 261 -292 231 -298 231 -264 295 -270 271 -268 233 -262 261 -262 255 -258 293 -230 293 -264 269 -266 265 -270 271 -270 231 -264 261 -260 257 -290 265 -274 269 -266 235 -268 263 -262 255 -290 265 -234 259 -260 291 -262 267 -274 269 -266 269 -266 231 -262 261 -264 255 -290 265 -232 259 -294 265 -272 269 -266 267 -268 231 -262 261 -262 257 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -262 261 -264 255 -290 265 -274 269 -234 295 -262 267 -264 233 -264 257 -256 291 -264 267 -268 265 -268 273 -268 231 -264 261 -260 261 -264 295 -270 273 -268 231 -264 255 -290 265 -266 233 -264 261 -262 295 -234 301 -266 267 -266 231 -264 257 -288 265 -234 259 -258 293 -262 269 -266 265 -234 301 -266 267 -266 231 -262 263 -262 263 -262 257 -290 263 -268 231 -264 295 -270 271 -236 259 -292 263 -232 259 -294 265 -264 231 -264 295 -236 299 -264 269 -268 229 -264 261 -262 263 -262 263 -262 261 -264 295 -268 271 -270 231 -264 255 -290 265 -268 265 -270 271 -236 265 -262 255 -290 265 -268 231 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 255 -292 265 -266 233 -296 269 -272 235 -266 257 -290 263 -232 261 -292 265 -266 265 -236 299 -266 267 -234 263 -264 261 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 263 -262 261 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 257 -256 291 -264 267 -240 303 -266 269 -232 265 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 267 -268 231 -262 261 -262 261 -264 261 -264 255 -290 267 -266 265 -236 299 -232 295 -262 267 -268 231 -264 261 -262 261 -262 295 -236 299 -266 269 -234 263 -262 257 -290 265 -232 259 -292 267 -264 -RAW_Data: 233 -264 295 -268 273 -268 231 -264 261 -262 261 -264 255 -290 267 -266 231 -296 269 -240 263 -294 265 -266 231 -264 293 -234 301 -266 267 -266 231 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 261 -262 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -268 231 -264 293 -234 299 -266 269 -268 231 -264 261 -260 261 -264 255 -258 293 -262 269 -240 303 -266 267 -234 263 -262 261 -262 257 -290 265 -234 261 -258 291 -262 269 -274 275 -236 265 -262 261 -260 257 -290 265 -268 231 -264 295 -236 299 -264 269 -234 263 -262 257 -258 291 -264 267 -236 263 -264 293 -268 273 -270 231 -264 261 -260 263 -262 263 -262 261 -264 295 -268 273 -268 231 -264 261 -262 255 -258 293 -262 269 -268 231 -264 293 -234 301 -232 295 -262 267 -268 231 -264 259 -262 263 -262 257 -290 265 -274 269 -266 269 -232 265 -262 261 -262 255 -258 293 -270 271 -272 269 -234 265 -262 259 -262 263 -262 257 -290 267 -266 265 -236 301 -264 267 -234 263 -264 261 -262 257 -288 267 -266 233 -264 295 -268 273 -234 261 -290 265 -232 259 -292 265 -266 265 -236 299 -266 267 -234 263 -264 261 -260 263 -262 263 -262 263 -262 295 -236 299 -266 267 -234 263 -264 261 -262 257 -290 265 -268 231 -264 293 -236 299 -266 269 -234 263 -262 261 -294 229 -264 257 -290 265 -274 267 -268 233 -300 231 -264 259 -262 257 -292 265 -266 231 -296 263 -236 299 -266 267 -266 231 -264 261 -264 261 -262 255 -292 265 -266 233 -264 293 -234 299 -266 269 -268 231 -262 261 -294 229 -264 263 -262 257 -290 265 -272 275 -270 233 -262 261 -262 261 -294 229 -264 257 -290 233 -298 265 -236 299 -266 235 -300 231 -262 263 -262 257 -290 265 -266 231 -296 263 -268 273 -268 233 -262 257 -258 289 -264 267 -268 263 -236 301 -264 267 -266 231 -264 257 -290 231 -266 259 -294 231 -300 263 -236 301 -266 233 -300 231 -262 261 -262 257 -258 293 -264 267 -268 263 -236 299 -264 235 -300 231 -264 261 -264 255 -258 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -264 261 -294 229 -264 257 -290 265 -272 267 -268 235 -300 229 -296 229 -264 255 -290 265 -268 231 -264 257 -290 265 -266 263 -236 301 -266 233 -300 231 -264 261 -264 255 -290 265 -272 269 -266 235 -300 231 -264 261 -294 229 -262 259 -290 231 -298 231 -298 229 -264 293 -268 273 -268 233 -264 261 -262 261 -294 229 -296 229 -296 269 -272 237 -266 257 -290 -RAW_Data: 229 -300 231 -296 263 -268 273 -268 233 -264 259 -294 229 -296 231 -262 257 -290 231 -300 265 -234 301 -266 235 -300 229 -264 261 -294 229 -296 263 -236 299 -264 269 -266 231 -296 229 -262 261 -262 257 -292 231 -266 261 -292 265 -270 269 -266 235 -300 231 -264 261 -294 229 -296 229 -264 261 -264 293 -234 301 -266 233 -300 231 -264 261 -264 261 -262 261 -262 289 -300 241 -274 233 -266 261 -262 259 -262 257 -292 265 -266 263 -236 301 -266 235 -300 229 -264 261 -262 257 -258 291 -230 295 -264 233 -302 263 -234 301 -266 235 -298 231 -296 229 -262 263 -262 257 -290 265 -266 231 -296 265 -234 299 -232 293 -264 267 -234 259 -292 231 -300 263 -236 301 -232 263 -294 267 -266 233 -262 261 -262 257 -258 293 -262 269 -266 265 -234 301 -264 267 -266 231 -264 263 -262 295 -270 271 -268 231 -264 261 -294 229 -264 257 -290 265 -266 265 -236 301 -266 233 -300 231 -262 261 -262 263 -262 257 -258 291 -264 267 -274 273 -270 233 -262 257 -256 291 -262 269 -268 263 -236 301 -264 267 -266 231 -264 261 -264 261 -262 261 -262 257 -292 265 -272 269 -266 269 -266 231 -262 257 -256 291 -230 295 -264 269 -266 265 -236 299 -264 267 -268 231 -262 263 -262 257 -290 265 -266 231 -264 295 -270 271 -270 231 -262 261 -262 261 -264 257 -258 291 -262 269 -274 267 -268 267 -266 231 -262 261 -262 257 -290 265 -234 259 -292 265 -266 265 -236 299 -234 295 -262 267 -264 231 -264 257 -258 293 -228 293 -264 269 -274 275 -270 231 -264 259 -262 255 -292 265 -266 231 -264 257 -290 265 -266 267 -270 271 -270 231 -262 261 -262 255 -258 291 -230 295 -264 269 -272 275 -238 259 -258 291 -260 267 -268 265 -270 273 -268 231 -264 259 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -268 231 -262 257 -256 293 -268 271 -272 271 -236 263 -262 261 -260 263 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -268 231 -262 257 -290 265 -264 231 -266 263 -262 295 -268 273 -268 231 -264 261 -262 255 -292 265 -266 233 -262 257 -290 265 -266 265 -270 271 -236 261 -290 265 -266 231 -264 261 -264 295 -234 299 -266 267 -266 231 -264 257 -290 265 -264 233 -264 261 -264 295 -234 299 -266 267 -268 231 -262 261 -264 255 -292 263 -266 231 -266 257 -290 263 -274 269 -266 269 -234 263 -262 257 -256 293 -262 269 -268 263 -236 299 -232 293 -264 267 -266 233 -264 261 -262 255 -290 267 -266 231 -264 295 -236 299 -264 269 -266 -RAW_Data: 231 -262 263 -262 289 -264 269 -270 271 -234 265 -262 255 -258 291 -262 269 -268 265 -234 299 -266 269 -266 231 -262 261 -264 261 -262 261 -264 257 -290 265 -272 269 -266 269 -234 259 -258 289 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 257 -258 291 -264 267 -274 267 -266 269 -266 231 -262 257 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 261 -264 255 -292 265 -266 265 -270 271 -270 231 -262 261 -262 257 -256 293 -262 269 -268 265 -234 301 -264 267 -268 231 -262 263 -262 257 -290 263 -234 261 -258 291 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -264 255 -290 265 -232 261 -292 273 -276 237 -266 263 -264 259 -262 261 -264 255 -290 265 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -290 265 -274 267 -268 269 -234 263 -262 255 -290 265 -268 265 -236 299 -264 269 -234 263 -262 257 -258 291 -230 295 -264 267 -268 265 -234 299 -266 267 -234 263 -262 257 -290 267 -238 301 -268 269 -232 263 -264 261 -262 255 -292 265 -266 233 -264 261 -262 295 -236 299 -264 269 -234 263 -264 255 -290 265 -234 259 -292 233 -298 265 -270 271 -270 231 -264 261 -260 261 -264 257 -292 263 -266 231 -266 263 -262 293 -268 273 -236 261 -258 289 -262 267 -268 265 -236 301 -264 267 -268 229 -264 261 -264 257 -256 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 257 -290 265 -232 259 -294 265 -266 263 -236 301 -266 267 -234 263 -262 263 -262 257 -290 263 -268 231 -264 297 -234 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -264 255 -258 291 -230 295 -304 245 -272 235 -264 255 -290 263 -266 231 -264 297 -234 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 259 -292 265 -266 265 -270 273 -236 263 -264 255 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 229 -264 263 -262 261 -262 263 -262 263 -262 295 -234 301 -264 269 -266 231 -262 263 -262 257 -290 265 -232 259 -292 267 -272 275 -238 265 -262 261 -260 257 -290 265 -234 261 -290 265 -266 265 -270 271 -270 231 -264 261 -262 255 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -264 261 -262 255 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -264 233 -264 295 -236 299 -264 269 -234 263 -264 -RAW_Data: 261 -262 263 -262 261 -262 257 -290 265 -274 267 -268 267 -234 259 -290 265 -266 263 -236 301 -266 269 -232 265 -262 261 -262 261 -264 257 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 261 -264 261 -262 289 -264 277 -272 235 -264 261 -260 257 -290 265 -234 259 -292 265 -272 269 -234 295 -262 267 -266 231 -264 261 -264 261 -262 257 -290 265 -268 265 -268 273 -268 231 -264 261 -262 255 -292 265 -266 231 -264 257 -290 265 -266 265 -236 299 -266 269 -234 263 -262 263 -262 293 -234 301 -266 269 -234 263 -262 263 -262 257 -290 263 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 257 -290 263 -234 259 -292 267 -266 231 -262 297 -268 271 -270 231 -264 261 -260 257 -290 265 -274 275 -270 233 -262 255 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -232 261 -292 265 -272 269 -266 269 -232 263 -264 255 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -234 261 -290 265 -264 265 -270 273 -268 231 -264 261 -262 261 -262 295 -270 273 -268 231 -264 261 -260 263 -262 257 -292 265 -232 259 -292 265 -266 265 -268 273 -270 233 -262 261 -262 255 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 263 -262 257 -290 267 -272 275 -270 233 -262 261 -260 257 -290 265 -234 259 -292 265 -266 265 -270 273 -268 231 -264 257 -256 289 -262 269 -234 261 -292 265 -264 265 -236 299 -266 269 -266 231 -262 263 -262 261 -264 255 -290 265 -266 231 -264 297 -268 273 -268 231 -264 255 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -234 263 -264 261 -262 257 -290 265 -232 261 -292 265 -266 265 -234 301 -266 269 -232 263 -264 259 -262 295 -270 273 -270 231 -264 259 -262 255 -258 293 -262 269 -234 261 -290 265 -266 263 -270 273 -270 231 -262 261 -262 261 -264 257 -322 239 -278 239 -268 261 -260 255 -290 265 -266 233 -264 295 -234 299 -266 269 -234 263 -262 263 -262 255 -258 291 -230 295 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 255 -290 265 -268 231 -264 261 -262 295 -234 301 -266 269 -232 265 -262 255 -290 265 -274 269 -268 267 -234 263 -262 261 -262 257 -290 265 -234 259 -292 265 -272 269 -266 269 -234 263 -264 259 -262 263 -262 257 -258 291 -262 269 -268 231 -264 295 -268 271 -270 231 -264 261 -260 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 -RAW_Data: 261 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 257 -290 265 -266 231 -264 263 -296 269 -272 235 -268 255 -290 263 -266 231 -264 263 -262 295 -234 301 -264 269 -266 231 -262 263 -262 257 -290 265 -234 259 -292 265 -272 269 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -268 231 -264 293 -234 301 -266 267 -266 231 -264 257 -288 265 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 231 -264 295 -268 273 -268 231 -264 261 -262 255 -292 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -268 231 -264 257 -290 263 -272 269 -268 269 -234 263 -262 261 -262 261 -264 261 -296 269 -272 235 -268 255 -290 265 -232 259 -292 265 -274 267 -268 267 -234 263 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -270 271 -268 233 -262 261 -262 261 -264 295 -234 301 -266 267 -234 263 -262 261 -262 263 -262 263 -262 261 -264 295 -234 301 -266 269 -232 265 -262 261 -260 257 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 255 -290 265 -268 231 -264 289 -264 269 -268 271 -236 265 -262 255 -290 265 -232 259 -292 265 -266 265 -270 273 -270 231 -262 261 -262 255 -258 293 -262 269 -268 231 -262 295 -234 299 -266 269 -266 231 -264 261 -262 261 -264 255 -292 265 -264 265 -236 301 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 295 -234 299 -232 295 -262 269 -266 231 -264 257 -256 291 -262 269 -268 231 -262 295 -270 271 -270 231 -262 257 -290 263 -268 265 -270 273 -234 265 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -232 265 -262 261 -262 257 -258 291 -264 267 -268 273 -274 235 -266 255 -256 289 -230 295 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 263 -262 255 -290 265 -268 265 -236 299 -264 269 -266 231 -262 257 -290 265 -234 259 -292 265 -266 265 -236 301 -232 293 -264 267 -266 231 -262 257 -290 265 -266 233 -264 295 -268 273 -268 231 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -256 291 -264 267 -236 259 -292 263 -272 269 -266 269 -266 229 -264 261 -264 255 -290 267 -272 269 -266 269 -234 263 -262 261 -262 257 -290 265 -234 261 -292 265 -264 231 -264 295 -270 271 -270 231 -262 257 -256 291 -230 295 -264 269 -266 -RAW_Data: 271 -240 263 -262 291 -228 293 -264 267 -240 303 -266 269 -232 265 -262 259 -262 257 -292 265 -232 261 -292 265 -264 265 -270 273 -268 233 -262 261 -262 255 -292 265 -272 277 -270 231 -264 259 -262 261 -264 255 -292 265 -232 259 -292 267 -264 265 -270 273 -268 231 -264 261 -262 261 -262 263 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -264 261 -262 261 -264 255 -292 265 -264 265 -270 273 -270 231 -264 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 233 -262 261 -262 255 -290 265 -268 231 -266 255 -290 265 -272 269 -234 295 -228 295 -230 293 -264 269 -266 233 -262 295 -234 297 -266 269 -268 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 265 -270 271 -270 231 -262 261 -262 289 -266 267 -270 273 -234 265 -262 255 -256 291 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -290 265 -266 265 -270 273 -236 261 -290 265 -264 231 -264 295 -236 299 -232 295 -262 269 -266 231 -262 263 -262 257 -258 291 -262 269 -266 265 -236 299 -266 267 -234 263 -262 263 -262 263 -262 261 -262 261 -264 295 -236 299 -266 267 -234 263 -264 261 -262 257 -258 291 -262 269 -240 303 -266 267 -234 263 -264 261 -262 255 -290 267 -266 231 -264 295 -236 299 -266 267 -234 263 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -234 263 -264 255 -258 291 -230 295 -264 269 -234 297 -236 299 -264 267 -268 231 -262 263 -262 257 -290 265 -264 233 -264 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 263 -262 261 -264 255 -292 265 -266 267 -270 271 -236 259 -290 265 -264 233 -264 295 -268 273 -268 231 -264 261 -262 255 -258 293 -230 293 -264 269 -266 265 -268 273 -268 233 -262 261 -262 255 -290 267 -272 269 -268 269 -232 263 -264 255 -290 265 -234 259 -260 291 -262 267 -268 265 -270 271 -236 265 -262 261 -262 255 -292 265 -234 259 -292 265 -272 269 -266 267 -234 263 -264 261 -264 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 261 -264 255 -290 265 -272 269 -234 295 -262 267 -266 231 -264 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -266 231 -264 261 -262 261 -264 255 -290 265 -268 231 -264 295 -236 299 -264 267 -266 231 -264 263 -262 257 -290 265 -264 265 -236 301 -266 269 -266 231 -262 261 -262 261 -264 261 -264 255 -290 267 -266 -RAW_Data: 265 -270 273 -236 263 -264 259 -262 257 -258 291 -304 243 -276 235 -264 255 -288 265 -266 231 -264 263 -262 293 -234 301 -266 269 -234 263 -264 255 -290 265 -232 259 -294 265 -266 263 -236 301 -266 269 -232 259 -290 265 -266 231 -264 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 261 -262 263 -262 295 -236 299 -266 267 -234 263 -264 261 -264 255 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -262 263 -262 295 -236 299 -264 269 -234 263 -262 257 -290 265 -232 259 -294 265 -266 263 -236 301 -266 269 -232 265 -262 255 -258 291 -264 267 -234 261 -290 265 -266 265 -236 299 -266 267 -234 263 -264 261 -262 263 -262 263 -262 261 -262 295 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -292 265 -238 301 -266 269 -234 263 -264 261 -262 257 -256 291 -264 267 -268 233 -262 295 -234 299 -266 269 -232 259 -258 293 -262 267 -274 269 -266 267 -266 231 -262 263 -262 257 -256 293 -230 293 -264 269 -266 263 -236 301 -266 267 -234 263 -262 261 -262 295 -236 301 -266 267 -266 231 -262 257 -290 265 -232 261 -292 265 -266 265 -234 301 -266 267 -266 231 -262 263 -262 263 -262 257 -256 291 -264 269 -272 269 -266 269 -234 263 -262 261 -262 261 -264 261 -264 255 -290 267 -266 231 -262 297 -268 273 -268 233 -262 261 -262 261 -264 295 -268 273 -270 231 -264 259 -262 255 -292 265 -266 233 -264 261 -264 293 -234 301 -266 267 -234 263 -264 261 -262 261 -264 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 263 -262 261 -264 293 -234 301 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -266 231 -264 295 -270 271 -270 231 -264 261 -260 263 -262 263 -294 269 -272 237 -266 261 -262 261 -262 261 -264 261 -264 295 -234 301 -266 269 -232 263 -264 255 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 265 -262 261 -260 295 -236 301 -266 267 -266 231 -264 255 -290 265 -268 231 -264 257 -290 265 -264 265 -236 301 -266 267 -268 229 -264 255 -258 291 -230 295 -264 267 -268 263 -236 299 -266 267 -266 231 -264 261 -264 261 -262 257 -290 265 -272 269 -268 267 -234 263 -264 255 -290 265 -232 261 -292 267 -264 265 -236 299 -264 269 -266 233 -262 257 -290 263 -234 259 -292 265 -266 265 -236 299 -234 293 -262 267 -268 231 -264 261 -262 261 -262 263 -262 257 -290 265 -274 269 -266 269 -232 259 -290 265 -266 231 -264 257 -290 265 -266 263 -236 -RAW_Data: 301 -266 269 -266 231 -262 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -234 259 -290 265 -266 265 -270 273 -268 233 -262 261 -262 255 -258 293 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -262 263 -262 261 -262 257 -324 239 -276 239 -268 261 -262 255 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 267 -266 267 -234 299 -266 267 -266 231 -264 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -232 293 -264 267 -234 259 -292 265 -264 265 -270 273 -270 231 -262 261 -262 257 -290 265 -234 261 -290 265 -266 265 -236 299 -264 269 -266 231 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 301 -266 267 -266 231 -264 261 -262 263 -262 295 -234 299 -266 269 -266 231 -262 261 -262 257 -292 265 -266 231 -264 295 -234 301 -264 269 -266 231 -262 263 -262 257 -290 265 -266 231 -264 263 -296 267 -272 237 -266 257 -288 265 -264 265 -236 301 -266 269 -266 229 -264 261 -264 261 -262 263 -262 255 -292 265 -266 263 -236 301 -266 267 -268 231 -262 261 -262 295 -236 299 -266 269 -234 263 -262 261 -262 261 -264 261 -264 255 -292 265 -266 263 -236 301 -266 269 -266 231 -262 257 -256 291 -230 295 -264 267 -268 263 -270 273 -268 231 -264 261 -262 255 -292 265 -232 261 -260 289 -262 267 -274 277 -270 231 -262 257 -256 291 -264 267 -268 231 -264 293 -234 301 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -234 301 -230 295 -264 267 -266 231 -264 261 -262 257 -290 265 -232 259 -294 265 -272 269 -266 269 -232 259 -292 263 -234 259 -292 265 -264 265 -236 301 -232 295 -262 267 -268 231 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 295 -268 273 -268 231 -264 261 -262 261 -264 255 -292 265 -266 263 -236 301 -266 269 -266 231 -262 261 -264 261 -262 257 -290 265 -266 231 -298 269 -272 235 -266 263 -260 261 -262 257 -290 267 -266 265 -234 301 -234 293 -262 267 -268 231 -264 261 -260 263 -262 257 -292 265 -272 267 -268 267 -266 231 -264 261 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -268 231 -262 261 -262 261 -264 263 -262 293 -268 273 -270 233 -262 261 -262 261 -264 257 -258 291 -262 267 -274 269 -266 269 -266 231 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 299 -232 295 -262 269 -266 231 -264 255 -258 291 -230 295 -264 267 -274 273 -270 -RAW_Data: 233 -262 261 -262 255 -290 267 -266 233 -264 257 -288 265 -266 265 -236 299 -266 269 -266 231 -262 257 -258 291 -230 295 -264 267 -272 275 -238 259 -258 289 -262 267 -268 265 -270 271 -268 233 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -268 229 -264 261 -262 263 -262 295 -236 299 -266 267 -266 231 -262 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 269 -266 229 -264 261 -262 263 -262 261 -264 261 -262 295 -234 301 -266 267 -268 229 -264 261 -262 263 -262 257 -290 265 -266 265 -270 273 -268 231 -264 255 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 257 -290 265 -266 231 -264 261 -264 295 -234 299 -266 269 -266 231 -262 261 -264 255 -290 265 -266 233 -264 257 -290 265 -272 267 -268 269 -234 263 -262 255 -258 293 -262 269 -266 265 -234 301 -230 295 -264 267 -268 231 -262 261 -264 255 -292 263 -266 231 -266 295 -234 301 -264 267 -268 231 -264 261 -260 289 -264 271 -270 271 -236 263 -262 257 -256 291 -262 269 -266 267 -234 299 -266 267 -266 231 -264 263 -262 261 -262 261 -264 257 -290 265 -272 269 -266 269 -232 259 -260 289 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 257 -258 291 -262 269 -274 267 -266 269 -266 231 -262 263 -262 261 -260 263 -262 263 -264 295 -234 301 -264 269 -266 231 -264 259 -262 263 -262 257 -290 265 -266 265 -270 273 -270 231 -264 259 -262 261 -264 255 -292 265 -266 265 -236 301 -264 267 -266 231 -264 263 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -232 295 -264 267 -266 231 -264 255 -258 291 -264 269 -266 231 -296 269 -272 235 -266 263 -262 261 -260 261 -264 257 -292 263 -266 231 -264 297 -234 299 -266 269 -266 231 -262 261 -264 261 -262 257 -290 265 -272 269 -234 295 -230 293 -262 269 -268 265 -268 273 -268 231 -264 261 -260 263 -262 257 -258 291 -264 267 -268 265 -234 299 -266 267 -268 231 -262 257 -256 293 -262 269 -272 275 -270 233 -262 255 -258 289 -262 269 -234 261 -292 265 -264 265 -236 301 -266 233 -268 263 -262 261 -262 257 -290 265 -266 233 -264 295 -236 299 -264 269 -266 231 -262 257 -256 291 -264 269 -266 233 -262 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 261 -264 295 -234 299 -266 269 -266 231 -264 257 -256 291 -230 295 -264 269 -266 263 -236 299 -266 267 -268 231 -262 261 -262 261 -264 263 -262 257 -290 265 -266 265 -268 273 -270 -RAW_Data: 233 -262 261 -262 255 -292 265 -266 233 -262 295 -234 299 -268 267 -266 231 -262 261 -262 257 -292 265 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -262 257 -258 291 -230 295 -304 243 -274 235 -264 255 -290 263 -266 231 -264 295 -236 299 -264 269 -268 231 -262 261 -262 257 -290 265 -234 261 -292 263 -266 265 -268 273 -270 231 -264 255 -290 265 -266 263 -236 301 -266 269 -266 231 -262 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 229 -264 263 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -262 263 -262 257 -290 265 -264 233 -264 295 -270 271 -268 233 -262 261 -262 257 -290 265 -234 259 -292 265 -266 263 -270 273 -270 231 -264 261 -260 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 267 -268 231 -262 263 -262 257 -290 263 -268 231 -264 261 -262 295 -268 273 -236 261 -290 265 -232 259 -292 265 -266 231 -264 295 -234 301 -264 269 -266 231 -262 263 -262 257 -290 265 -232 261 -292 265 -272 267 -266 269 -234 259 -290 265 -266 265 -234 301 -266 269 -266 229 -264 261 -264 261 -262 257 -290 265 -266 263 -236 301 -266 269 -266 231 -262 257 -290 263 -266 231 -298 269 -272 235 -266 263 -260 263 -262 261 -264 255 -292 265 -266 267 -234 299 -266 267 -266 231 -264 257 -258 289 -262 269 -268 231 -264 295 -268 273 -268 231 -264 261 -262 261 -262 257 -292 265 -264 233 -264 295 -236 299 -264 269 -266 231 -262 261 -264 261 -264 261 -262 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 231 -264 297 -234 299 -266 269 -266 231 -262 261 -264 255 -292 265 -232 259 -260 291 -262 269 -266 265 -270 273 -268 231 -262 261 -262 257 -290 265 -266 265 -236 301 -266 267 -266 231 -262 263 -262 263 -262 261 -264 255 -290 265 -266 267 -236 299 -232 293 -264 267 -266 231 -264 263 -262 257 -256 293 -302 243 -274 235 -266 261 -260 257 -256 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 263 -262 261 -264 257 -288 265 -266 265 -270 273 -270 231 -262 261 -262 255 -292 265 -234 259 -260 291 -260 269 -266 267 -236 299 -264 269 -232 259 -260 291 -260 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -264 261 -262 261 -262 295 -236 301 -266 233 -300 231 -264 259 -262 257 -258 293 -262 269 -234 259 -292 263 -266 265 -270 271 -270 231 -264 261 -262 255 -290 267 -266 263 -236 301 -234 293 -262 267 -266 231 -264 263 -262 257 -290 -RAW_Data: 265 -234 259 -292 265 -272 267 -268 269 -266 231 -262 261 -262 261 -264 295 -268 273 -268 233 -262 261 -262 257 -290 265 -268 265 -236 299 -264 267 -266 231 -264 263 -262 261 -262 257 -290 265 -266 231 -266 295 -268 271 -270 231 -264 255 -290 265 -266 265 -236 299 -266 269 -266 231 -262 261 -264 261 -264 255 -290 265 -268 231 -264 295 -268 273 -268 231 -264 261 -262 255 -260 291 -262 269 -266 231 -264 295 -270 271 -268 231 -264 261 -262 261 -264 257 -290 265 -266 263 -270 273 -270 231 -264 261 -260 263 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 261 -262 261 -262 257 -258 293 -262 269 -266 263 -236 301 -266 235 -300 229 -264 259 -262 263 -262 257 -258 291 -264 267 -268 265 -270 271 -268 231 -264 261 -262 261 -262 257 -292 265 -266 263 -236 301 -232 295 -262 267 -266 231 -264 257 -258 291 -228 295 -264 267 -268 265 -268 273 -236 261 -258 291 -260 267 -274 269 -266 267 -268 229 -264 255 -258 293 -262 269 -268 231 -262 295 -234 299 -264 269 -268 231 -262 261 -262 263 -262 257 -324 239 -274 239 -266 265 -262 255 -290 265 -264 233 -264 263 -262 293 -236 299 -266 269 -266 229 -264 257 -290 265 -266 231 -264 263 -262 295 -268 273 -268 231 -264 261 -262 261 -264 263 -262 257 -290 265 -266 231 -264 295 -234 299 -266 269 -232 259 -292 265 -266 231 -264 295 -234 299 -266 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 257 -290 265 -232 259 -292 265 -266 231 -266 295 -268 271 -270 231 -264 261 -260 257 -290 267 -266 265 -236 299 -264 269 -266 231 -264 261 -264 255 -290 265 -266 231 -264 259 -290 265 -266 263 -270 273 -268 231 -264 261 -262 261 -264 261 -296 269 -272 235 -266 259 -288 265 -232 259 -292 265 -272 269 -266 269 -266 231 -262 261 -262 263 -262 257 -292 265 -264 231 -264 295 -270 271 -270 231 -264 261 -262 261 -294 263 -236 299 -266 269 -266 231 -262 263 -262 261 -262 257 -290 265 -266 233 -264 295 -268 271 -270 231 -264 261 -262 255 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 257 -290 265 -232 259 -292 265 -266 265 -268 273 -270 233 -262 261 -262 255 -258 293 -262 269 -268 231 -262 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -232 259 -292 265 -266 265 -236 301 -266 233 -300 231 -264 255 -290 265 -266 -RAW_Data: 231 -264 297 -234 299 -232 293 -264 267 -268 231 -262 257 -258 291 -262 269 -268 231 -262 295 -268 271 -270 231 -264 257 -290 263 -266 265 -270 273 -268 233 -262 261 -262 261 -296 229 -264 257 -290 265 -266 265 -236 299 -266 267 -266 231 -264 263 -262 257 -256 291 -296 239 -278 239 -268 263 -262 255 -290 263 -234 259 -292 265 -266 265 -234 301 -266 235 -300 229 -264 261 -264 255 -258 291 -264 267 -268 265 -234 301 -264 267 -266 231 -264 257 -258 291 -262 267 -236 259 -292 231 -298 265 -236 301 -266 233 -300 231 -262 257 -290 265 -272 269 -266 235 -300 231 -262 261 -262 257 -258 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 231 -264 259 -262 257 -258 291 -262 269 -274 269 -266 235 -300 231 -262 261 -262 257 -290 265 -232 261 -292 265 -266 231 -264 295 -268 271 -270 231 -264 257 -258 289 -230 293 -264 269 -266 273 -238 265 -294 231 -266 257 -292 265 -272 269 -266 235 -300 231 -262 261 -264 257 -290 265 -232 261 -292 231 -298 265 -268 273 -270 233 -262 261 -262 255 -292 265 -272 275 -270 233 -262 261 -262 261 -264 257 -290 263 -234 261 -292 231 -298 265 -270 271 -270 231 -264 261 -262 261 -264 261 -262 263 -262 295 -234 301 -266 233 -300 231 -262 263 -262 263 -262 261 -262 257 -290 267 -264 265 -270 273 -268 233 -264 259 -262 261 -264 257 -290 265 -266 265 -270 271 -270 231 -264 261 -262 261 -264 255 -290 267 -266 265 -236 299 -264 269 -266 231 -264 261 -264 261 -260 263 -262 257 -292 265 -264 265 -270 273 -268 233 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 233 -300 231 -264 261 -262 257 -290 265 -266 233 -264 261 -264 293 -268 271 -270 233 -264 261 -262 287 -266 269 -270 271 -266 231 -264 255 -258 289 -264 267 -268 231 -264 293 -236 299 -266 235 -300 231 -262 263 -262 261 -264 255 -292 265 -264 265 -270 271 -236 261 -292 229 -300 231 -264 261 -264 295 -268 271 -270 231 -264 261 -262 261 -262 257 -292 265 -264 233 -296 263 -236 299 -264 269 -266 231 -262 263 -262 263 -262 261 -262 261 -264 295 -234 299 -266 269 -268 231 -262 261 -262 255 -258 291 -264 269 -274 267 -266 235 -300 231 -262 261 -262 257 -290 265 -268 231 -264 295 -236 297 -266 269 -266 231 -262 257 -290 265 -232 259 -294 265 -264 265 -236 301 -266 235 -300 229 -264 261 -262 261 -264 257 -290 265 -266 -RAW_Data: 231 -264 295 -268 273 -268 231 -264 261 -294 229 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 263 -262 257 -290 265 -264 265 -270 273 -236 261 -292 229 -298 231 -296 263 -270 273 -268 231 -264 261 -262 255 -258 291 -230 295 -264 267 -268 265 -268 273 -268 231 -264 261 -262 257 -290 265 -272 269 -266 269 -266 231 -262 257 -290 265 -232 259 -260 293 -262 267 -266 267 -270 271 -270 231 -262 261 -262 257 -290 265 -232 261 -292 265 -272 269 -266 235 -300 229 -264 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 267 -268 231 -264 261 -262 257 -290 265 -272 269 -234 295 -262 267 -266 233 -264 255 -258 291 -228 295 -264 267 -268 265 -236 299 -264 267 -266 231 -264 263 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -266 263 -236 301 -266 235 -300 231 -262 257 -290 265 -232 259 -292 265 -266 265 -236 299 -266 235 -300 231 -262 257 -256 291 -264 269 -274 275 -270 231 -262 261 -260 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 267 -268 229 -264 261 -264 255 -290 265 -234 259 -294 265 -270 275 -236 261 -292 231 -266 259 -290 265 -274 267 -266 269 -266 231 -264 259 -262 257 -292 265 -232 259 -292 265 -266 265 -236 301 -266 233 -300 231 -262 257 -290 265 -232 259 -294 265 -266 263 -236 301 -266 233 -300 231 -264 261 -262 257 -290 265 -272 267 -268 269 -266 231 -262 257 -290 265 -232 259 -294 265 -264 265 -236 301 -266 267 -266 231 -262 257 -258 291 -262 269 -234 261 -290 265 -264 265 -236 301 -266 233 -300 231 -262 263 -262 261 -264 261 -264 261 -262 295 -268 273 -268 233 -262 261 -262 257 -290 265 -268 231 -264 261 -262 295 -234 301 -266 235 -300 231 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -236 259 -290 265 -266 229 -264 295 -270 271 -270 231 -264 261 -262 261 -264 257 -290 263 -266 265 -236 301 -266 267 -266 231 -264 261 -262 263 -262 263 -294 269 -272 235 -266 263 -262 261 -260 263 -262 259 -290 263 -266 231 -298 263 -234 301 -264 269 -266 231 -262 263 -262 261 -262 257 -290 267 -272 269 -266 269 -266 229 -264 261 -264 255 -290 265 -234 259 -292 265 -266 265 -236 299 -266 267 -234 259 -258 291 -230 293 -264 267 -274 267 -266 269 -266 231 -262 263 -262 263 -262 261 -264 261 -262 295 -234 301 -232 295 -262 267 -268 231 -264 261 -262 257 -290 265 -264 233 -264 -RAW_Data: 263 -262 295 -234 299 -266 269 -266 231 -262 261 -262 257 -290 267 -266 265 -236 301 -264 267 -266 231 -264 261 -264 259 -262 257 -292 265 -266 231 -264 295 -268 273 -270 231 -264 261 -260 263 -262 263 -294 269 -272 237 -266 261 -262 261 -262 257 -290 265 -266 265 -236 301 -264 269 -266 231 -264 257 -288 265 -266 231 -264 263 -262 295 -268 271 -270 231 -264 263 -260 295 -234 301 -266 267 -266 231 -264 263 -262 257 -256 291 -264 267 -268 231 -264 293 -268 273 -268 233 -264 261 -262 259 -264 257 -258 291 -296 241 -276 239 -268 263 -262 255 -290 263 -266 231 -266 295 -234 301 -264 269 -266 229 -264 263 -262 257 -290 263 -234 261 -292 265 -270 269 -232 295 -264 269 -266 229 -264 257 -258 291 -262 267 -268 231 -264 295 -268 273 -268 233 -262 261 -262 255 -290 267 -272 269 -266 235 -300 231 -262 261 -262 257 -292 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 261 -264 261 -260 257 -292 265 -264 233 -264 295 -236 299 -232 293 -264 267 -266 231 -264 263 -262 257 -290 263 -268 265 -270 271 -270 231 -262 261 -262 257 -292 265 -232 259 -292 233 -298 265 -236 299 -266 269 -266 229 -264 261 -264 255 -290 265 -266 233 -264 295 -268 273 -268 231 -264 255 -290 265 -266 265 -270 271 -270 231 -264 261 -262 261 -262 263 -262 257 -290 265 -268 263 -270 273 -268 233 -262 261 -262 255 -292 265 -272 269 -266 269 -266 231 -262 263 -262 257 -290 265 -264 233 -296 263 -236 299 -264 269 -266 231 -264 261 -264 261 -262 255 -260 291 -262 267 -274 269 -266 235 -300 231 -262 261 -262 261 -264 257 -258 291 -264 267 -268 231 -264 293 -268 273 -234 261 -258 291 -228 295 -264 267 -268 265 -236 299 -264 267 -268 231 -264 261 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -262 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 257 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 261 -264 255 -292 265 -232 259 -292 265 -266 265 -234 301 -266 235 -300 229 -264 261 -294 263 -268 273 -270 231 -264 261 -262 255 -290 267 -266 231 -264 295 -234 299 -266 267 -268 231 -264 255 -258 291 -262 267 -268 233 -262 295 -268 273 -268 233 -262 257 -290 265 -272 267 -268 267 -268 229 -264 261 -264 257 -288 265 -234 259 -292 265 -266 265 -234 301 -266 235 -300 231 -262 257 -256 291 -230 295 -304 243 -274 235 -264 257 -288 265 -264 231 -298 229 -264 293 -234 -RAW_Data: 301 -266 267 -266 231 -264 263 -262 261 -262 257 -290 265 -266 231 -266 295 -234 299 -266 267 -266 231 -264 263 -262 261 -264 261 -262 257 -290 265 -266 265 -234 301 -234 293 -264 267 -266 231 -264 257 -258 289 -230 293 -264 269 -266 265 -270 271 -270 231 -264 255 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -264 235 -300 231 -262 261 -262 257 -258 291 -264 269 -266 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 295 -236 299 -266 235 -300 231 -262 261 -294 229 -264 257 -290 265 -266 231 -296 263 -236 299 -266 267 -268 231 -264 255 -258 291 -268 273 -270 269 -268 231 -264 259 -296 229 -262 257 -292 265 -264 231 -298 261 -236 299 -264 269 -268 231 -264 255 -290 265 -266 231 -264 263 -262 295 -268 271 -270 231 -264 261 -262 257 -290 265 -266 233 -262 257 -292 265 -264 265 -270 273 -236 259 -258 291 -262 267 -268 265 -234 301 -266 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 255 -292 265 -234 259 -292 265 -266 231 -264 295 -268 273 -268 231 -264 261 -262 257 -290 265 -266 263 -236 301 -266 269 -266 231 -262 261 -264 261 -262 263 -262 257 -290 265 -266 265 -270 273 -268 231 -264 261 -262 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 257 -290 265 -266 265 -236 301 -266 235 -300 229 -264 261 -262 257 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -262 257 -290 267 -272 267 -268 267 -268 229 -264 261 -262 261 -264 257 -290 265 -232 259 -294 265 -272 267 -268 269 -234 263 -262 261 -262 261 -264 257 -256 293 -262 269 -274 267 -266 269 -266 231 -262 261 -264 261 -264 255 -290 265 -266 265 -270 273 -268 233 -262 255 -290 265 -232 261 -258 293 -262 267 -274 269 -266 269 -232 265 -262 255 -258 291 -262 269 -234 261 -292 263 -272 269 -266 269 -234 263 -262 255 -258 291 -230 295 -264 267 -240 303 -232 295 -262 267 -266 231 -264 263 -262 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 269 -232 259 -292 265 -266 263 -236 299 -266 269 -234 263 -262 257 -290 265 -232 259 -294 265 -272 267 -268 269 -234 263 -262 261 -262 257 -258 291 -262 269 -268 271 -240 263 -262 291 -228 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 263 -262 261 -262 261 -264 257 -290 263 -274 269 -266 269 -266 231 -262 257 -290 263 -274 269 -268 -RAW_Data: 267 -234 263 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 299 -266 269 -232 265 -262 261 -264 261 -262 261 -264 261 -264 295 -234 299 -266 269 -234 263 -262 261 -262 263 -262 257 -290 265 -268 265 -270 273 -236 263 -262 257 -288 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 261 -262 263 -262 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 233 -262 261 -262 255 -290 267 -266 233 -262 263 -262 295 -268 273 -268 233 -262 257 -290 263 -266 231 -264 297 -234 299 -232 295 -264 267 -266 231 -264 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 229 -264 261 -264 287 -266 269 -270 271 -234 263 -262 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 263 -262 261 -264 255 -290 265 -274 269 -266 269 -234 257 -258 291 -260 269 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 257 -256 293 -262 269 -238 303 -266 269 -232 265 -262 261 -262 261 -264 261 -262 263 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -264 267 -268 265 -268 273 -268 233 -262 261 -262 255 -258 293 -262 269 -268 263 -236 299 -264 269 -266 231 -264 261 -264 255 -292 263 -234 259 -260 291 -262 267 -268 265 -234 299 -266 235 -300 231 -262 257 -258 291 -264 267 -268 231 -296 269 -270 235 -300 231 -262 259 -296 229 -262 257 -290 233 -298 231 -296 263 -236 299 -264 235 -300 233 -262 261 -294 231 -262 257 -290 265 -272 267 -234 263 -262 295 -262 235 -300 265 -270 271 -268 231 -264 261 -294 229 -264 257 -258 291 -262 235 -300 263 -270 267 -266 235 -300 229 -296 229 -262 257 -292 263 -272 267 -268 235 -300 231 -294 231 -296 227 -262 257 -292 231 -298 265 -270 237 -304 231 -298 227 -262 257 -260 259 -294 233 -300 233 -296 261 -268 271 -270 231 -298 229 -296 227 -262 257 -260 259 -294 233 -300 233 -296 263 -268 231 -298 233 -300 231 -298 229 -296 229 -294 263 -268 265 -266 233 -300 231 -264 257 -260 259 -262 259 -296 235 -300 265 -270 265 -264 235 -300 231 -296 229 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -270 235 -304 231 -264 257 -290 231 -300 231 -296 263 -268 265 -264 235 -300 233 -296 229 -262 257 -292 231 -298 231 -296 229 -296 263 -268 265 -264 235 -300 231 -264 257 -258 259 -262 293 -304 243 -272 235 -266 257 -290 231 -298 231 -298 263 -268 231 -298 235 -300 231 -294 231 -262 257 -290 233 -266 259 -292 231 -300 -RAW_Data: 263 -270 237 -302 233 -264 257 -290 231 -300 263 -270 265 -266 235 -300 231 -296 229 -264 257 -258 259 -294 233 -300 265 -270 231 -298 235 -300 231 -296 229 -296 229 -296 229 -294 229 -296 263 -268 265 -264 235 -302 231 -296 229 -262 257 -290 231 -298 231 -298 263 -268 237 -302 233 -296 229 -262 257 -290 231 -266 261 -292 231 -300 263 -270 267 -264 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -268 237 -302 233 -296 231 -262 257 -290 231 -298 231 -296 263 -270 265 -264 235 -300 231 -296 229 -264 257 -290 231 -298 231 -298 263 -268 265 -264 235 -300 231 -296 231 -296 227 -294 231 -262 257 -292 231 -298 265 -268 237 -270 261 -292 231 -300 263 -270 265 -266 235 -300 231 -296 229 -296 227 -262 257 -292 231 -300 263 -270 265 -266 235 -300 231 -264 257 -292 231 -298 231 -296 267 -272 235 -300 231 -294 229 -294 229 -262 257 -292 231 -300 265 -268 273 -268 233 -262 261 -294 231 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -268 267 -266 233 -300 231 -296 229 -296 229 -262 257 -292 229 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -296 229 -296 227 -296 261 -270 265 -264 235 -302 231 -296 229 -294 229 -296 229 -264 255 -290 231 -300 263 -270 267 -266 235 -300 231 -262 257 -290 231 -298 233 -296 269 -272 235 -268 261 -262 261 -294 229 -296 263 -234 301 -266 233 -302 229 -264 257 -258 259 -294 267 -234 261 -292 231 -298 265 -268 273 -268 233 -262 261 -296 229 -296 261 -270 271 -268 231 -298 229 -262 261 -262 257 -290 233 -266 259 -292 233 -298 265 -268 273 -268 233 -296 227 -262 257 -258 259 -296 233 -300 265 -270 265 -266 235 -298 231 -296 229 -296 229 -296 229 -264 257 -290 265 -270 275 -270 233 -262 261 -262 257 -290 231 -266 261 -292 231 -298 265 -270 273 -268 233 -262 257 -258 259 -294 267 -234 261 -292 231 -298 265 -270 265 -264 235 -300 231 -296 229 -296 229 -294 231 -294 229 -296 229 -294 263 -268 271 -270 231 -264 257 -292 231 -266 259 -292 265 -272 267 -266 235 -300 231 -296 229 -264 255 -258 291 -264 233 -302 231 -264 293 -268 271 -270 231 -264 263 -292 229 -296 263 -236 299 -264 235 -300 231 -296 229 -294 229 -296 229 -264 257 -290 231 -298 -RAW_Data: 265 -270 265 -266 235 -300 231 -296 229 -262 257 -258 259 -328 239 -276 237 -302 233 -262 255 -288 231 -298 233 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -268 273 -268 233 -264 261 -262 255 -290 233 -266 259 -292 233 -298 231 -296 263 -268 267 -264 233 -300 231 -264 257 -292 265 -270 269 -266 235 -300 231 -296 229 -262 257 -292 231 -264 261 -292 265 -272 267 -266 235 -300 231 -296 229 -264 261 -262 257 -258 291 -264 267 -268 231 -262 295 -268 271 -270 233 -262 261 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 227 -296 229 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -302 229 -264 257 -292 229 -300 231 -296 229 -296 269 -272 235 -266 259 -290 231 -298 265 -268 265 -266 235 -302 231 -296 227 -296 229 -294 231 -294 229 -296 263 -268 265 -264 235 -300 233 -296 229 -262 287 -300 241 -272 235 -298 227 -262 257 -258 259 -294 233 -302 231 -298 261 -268 265 -264 235 -302 231 -262 257 -290 231 -268 259 -292 231 -300 263 -270 273 -268 233 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -262 263 -262 257 -258 291 -264 267 -266 231 -296 263 -268 273 -268 231 -264 261 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -264 235 -300 231 -296 229 -264 261 -294 263 -234 301 -264 235 -300 231 -294 231 -262 257 -290 231 -300 231 -296 263 -234 299 -266 235 -302 231 -262 257 -290 231 -266 259 -260 259 -296 267 -266 265 -236 299 -264 235 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 271 -272 237 -266 261 -262 261 -262 257 -290 231 -300 265 -270 267 -264 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -270 267 -266 233 -268 257 -292 231 -298 231 -298 263 -268 271 -268 233 -296 229 -262 257 -290 231 -266 261 -292 231 -300 231 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 233 -300 231 -296 229 -296 229 -294 229 -296 263 -268 273 -268 231 -264 261 -262 257 -292 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -294 229 -296 229 -294 229 -296 231 -262 261 -294 263 -234 299 -266 269 -268 231 -262 261 -294 229 -264 257 -258 259 -328 239 -278 237 -270 261 -262 261 -262 255 -292 231 -298 233 -296 231 -262 293 -236 299 -266 235 -300 231 -262 257 -258 291 -230 295 -264 233 -300 265 -234 301 -230 297 -228 295 -262 269 -272 269 -266 -RAW_Data: 235 -300 231 -262 257 -290 231 -266 259 -292 233 -298 231 -296 263 -270 271 -268 233 -264 261 -260 257 -258 291 -304 243 -274 235 -264 263 -260 261 -294 229 -264 257 -290 265 -266 265 -236 299 -266 235 -300 231 -262 261 -294 229 -296 231 -262 261 -294 263 -234 301 -266 235 -300 231 -262 261 -264 255 -292 265 -266 231 -264 263 -262 295 -234 299 -266 267 -268 231 -262 263 -262 261 -262 295 -234 301 -266 269 -266 231 -262 261 -264 255 -290 265 -266 231 -264 297 -234 299 -232 295 -264 267 -266 231 -264 263 -262 255 -290 265 -266 231 -264 263 -262 295 -270 271 -268 233 -262 263 -262 257 -256 291 -262 269 -268 265 -236 299 -264 267 -268 231 -262 263 -262 261 -262 257 -290 265 -268 265 -234 301 -264 269 -266 231 -264 261 -262 263 -262 287 -266 275 -274 235 -264 261 -262 255 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -260 257 -290 265 -268 231 -264 263 -262 293 -270 271 -270 231 -264 255 -290 265 -266 265 -236 299 -266 269 -266 231 -262 261 -264 261 -262 261 -264 257 -290 265 -266 265 -270 271 -270 231 -264 261 -260 257 -290 267 -266 231 -264 263 -262 261 -262 295 -234 301 -266 269 -266 229 -264 261 -264 255 -290 265 -274 267 -268 269 -232 265 -262 255 -290 265 -234 261 -290 265 -266 231 -264 295 -270 271 -268 233 -262 257 -256 291 -264 267 -236 259 -290 265 -266 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 257 -290 265 -266 233 -296 269 -272 235 -266 257 -256 291 -262 269 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -262 257 -290 265 -266 233 -264 261 -262 293 -270 273 -236 261 -258 289 -262 267 -274 269 -266 269 -232 265 -262 257 -290 263 -234 259 -292 265 -266 265 -236 299 -266 269 -234 263 -262 261 -262 257 -258 291 -296 241 -276 239 -268 263 -262 255 -256 291 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 257 -256 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 257 -290 265 -232 261 -260 289 -264 267 -268 265 -236 299 -264 267 -266 231 -264 263 -262 261 -262 295 -236 299 -266 269 -234 263 -262 261 -262 257 -258 291 -262 269 -268 265 -236 299 -264 269 -232 265 -262 261 -262 257 -290 267 -266 231 -264 257 -290 265 -266 265 -236 299 -266 267 -234 263 -264 257 -288 265 -268 265 -236 299 -232 295 -262 267 -268 231 -262 263 -262 257 -288 265 -268 231 -264 263 -260 295 -270 273 -236 265 -262 261 -262 255 -258 291 -298 -RAW_Data: 241 -278 237 -268 257 -256 289 -262 269 -268 231 -264 293 -268 273 -270 231 -264 261 -262 261 -262 257 -290 265 -268 231 -264 293 -268 273 -270 233 -262 261 -260 263 -262 295 -236 301 -266 267 -234 263 -262 261 -262 261 -264 257 -258 291 -264 267 -268 265 -270 271 -236 265 -262 261 -260 263 -262 263 -262 263 -262 295 -234 299 -232 295 -264 269 -232 265 -262 263 -262 257 -288 265 -234 293 -266 269 -270 269 -234 265 -262 255 -258 291 -230 295 -264 267 -236 297 -270 271 -234 265 -264 259 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 299 -232 295 -264 267 -234 259 -292 265 -232 259 -292 265 -264 233 -264 295 -236 299 -264 269 -232 265 -262 261 -262 263 -262 261 -264 263 -262 295 -270 271 -236 265 -264 255 -288 265 -266 267 -270 273 -236 263 -262 257 -288 265 -268 231 -264 261 -262 295 -234 301 -266 269 -234 263 -262 261 -262 257 -290 265 -268 231 -298 269 -272 235 -266 261 -262 261 -262 263 -262 261 -264 293 -234 301 -266 269 -232 265 -262 261 -262 255 -258 293 -262 269 -268 265 -236 299 -264 269 -232 265 -262 261 -264 261 -264 255 -290 267 -266 231 -262 297 -234 301 -264 269 -232 263 -264 257 -258 291 -262 269 -240 303 -266 267 -234 263 -262 261 -262 261 -264 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -232 295 -262 267 -266 231 -264 261 -262 261 -264 295 -236 299 -264 269 -234 263 -264 261 -262 255 -292 265 -266 233 -264 257 -288 265 -266 265 -270 273 -268 231 -264 261 -262 255 -290 267 -266 233 -296 269 -238 263 -294 267 -264 231 -264 293 -236 299 -266 269 -234 263 -262 263 -262 255 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -264 259 -262 295 -236 299 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -266 233 -264 295 -268 271 -270 233 -262 261 -262 261 -262 257 -258 291 -264 267 -240 303 -266 269 -232 265 -262 261 -262 255 -292 265 -266 231 -264 257 -290 265 -272 277 -270 233 -262 255 -256 291 -264 267 -268 231 -264 293 -234 301 -266 269 -234 263 -262 257 -256 291 -264 267 -268 231 -264 293 -236 299 -266 269 -234 263 -262 261 -262 263 -262 257 -258 291 -262 269 -274 269 -266 267 -234 263 -264 261 -260 257 -290 267 -266 233 -264 295 -234 299 -232 293 -264 269 -266 233 -262 261 -262 255 -258 291 -264 -RAW_Data: 267 -268 265 -236 299 -266 267 -266 231 -264 255 -290 265 -274 275 -238 263 -264 259 -262 261 -264 257 -290 265 -266 265 -236 299 -266 267 -268 231 -262 261 -264 255 -290 265 -234 261 -290 265 -266 271 -276 235 -266 257 -288 263 -234 259 -292 265 -272 269 -234 295 -262 267 -266 231 -264 261 -264 255 -290 265 -234 259 -292 265 -272 269 -266 269 -234 263 -262 257 -256 291 -264 267 -236 259 -292 265 -264 265 -236 299 -266 269 -234 263 -262 261 -262 261 -264 295 -268 273 -270 231 -264 261 -260 263 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 261 -264 255 -292 265 -232 259 -260 291 -264 267 -268 263 -236 299 -266 267 -234 263 -264 261 -264 255 -290 265 -268 265 -270 273 -268 231 -264 259 -262 257 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 265 -262 259 -262 257 -292 265 -266 231 -264 295 -270 271 -270 231 -264 255 -290 265 -266 263 -270 273 -270 233 -262 261 -260 263 -262 261 -264 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 295 -234 299 -266 269 -234 263 -264 259 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -264 255 -258 293 -262 269 -238 303 -266 269 -232 263 -264 261 -262 261 -264 257 -256 291 -264 269 -266 233 -262 295 -268 271 -238 259 -260 289 -228 295 -262 269 -268 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 263 -262 261 -262 261 -264 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 257 -290 265 -234 259 -292 265 -266 263 -270 273 -270 231 -264 261 -260 257 -258 291 -304 243 -274 235 -266 259 -262 261 -262 257 -290 265 -266 233 -264 293 -234 299 -266 269 -268 231 -264 259 -262 261 -264 257 -258 291 -262 269 -274 275 -236 265 -264 255 -256 291 -262 269 -268 263 -270 273 -236 263 -264 259 -262 257 -258 291 -264 269 -266 233 -262 261 -262 295 -268 273 -270 233 -262 255 -288 265 -234 261 -258 293 -262 269 -232 297 -236 299 -266 269 -232 265 -262 261 -264 261 -264 293 -268 273 -270 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 265 -270 273 -268 233 -262 261 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -236 299 -264 269 -268 231 -264 255 -288 267 -266 233 -264 255 -290 271 -278 237 -266 263 -262 261 -260 257 -292 265 -266 231 -264 -RAW_Data: 295 -236 299 -232 295 -262 267 -266 233 -262 257 -258 291 -230 295 -264 267 -240 301 -232 295 -230 293 -264 267 -268 265 -268 273 -236 265 -262 261 -262 255 -290 267 -232 259 -292 267 -266 263 -270 273 -236 265 -262 261 -262 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 265 -262 255 -290 265 -268 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -290 265 -268 231 -266 255 -290 265 -266 265 -270 273 -236 259 -258 291 -260 269 -266 267 -236 299 -264 269 -232 265 -262 261 -264 255 -290 265 -268 231 -264 295 -234 299 -266 269 -234 263 -264 259 -262 257 -290 265 -234 261 -292 265 -264 231 -264 295 -270 271 -270 231 -264 261 -262 255 -290 265 -268 265 -236 299 -266 267 -234 263 -264 261 -264 261 -262 261 -262 257 -290 265 -268 265 -270 273 -234 265 -264 259 -262 261 -264 261 -264 295 -268 273 -236 265 -262 261 -262 257 -290 265 -266 267 -236 299 -264 269 -232 265 -262 263 -262 257 -288 265 -234 261 -292 265 -264 265 -270 273 -236 263 -264 257 -288 265 -266 265 -236 301 -264 269 -234 263 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 261 -262 261 -264 263 -262 261 -264 295 -234 301 -264 269 -234 263 -262 263 -262 263 -262 255 -290 265 -240 303 -266 269 -232 265 -262 261 -262 261 -264 261 -264 261 -262 263 -262 295 -268 273 -238 263 -264 259 -262 263 -262 257 -258 291 -262 269 -234 299 -234 299 -266 267 -234 263 -264 263 -262 255 -290 265 -234 261 -292 263 -274 275 -236 265 -262 255 -258 291 -262 269 -268 231 -264 293 -234 301 -266 267 -234 263 -262 263 -262 257 -290 265 -266 233 -264 295 -234 299 -266 267 -234 259 -290 265 -266 265 -236 299 -266 269 -234 263 -262 257 -288 265 -234 261 -292 265 -238 301 -234 295 -262 267 -234 263 -264 257 -256 291 -230 295 -296 241 -278 241 -234 289 -254 289 -230 295 -264 267 -236 297 -236 299 -232 293 -264 267 -234 263 -264 261 -264 255 -258 291 -264 267 -236 297 -270 271 -236 265 -262 261 -262 255 -292 265 -234 259 -292 265 -232 297 -236 301 -266 269 -232 263 -264 261 -262 261 -262 263 -262 295 -270 273 -236 265 -262 261 -262 255 -258 291 -230 295 -264 267 -236 297 -236 299 -264 269 -234 263 -262 263 -262 255 -290 265 -268 231 -264 263 -262 293 -234 301 -266 269 -234 263 -262 257 -256 293 -262 269 -234 299 -234 -RAW_Data: 299 -266 267 -234 263 -264 261 -262 261 -264 257 -290 265 -234 263 -264 295 -236 299 -266 267 -234 263 -264 255 -256 293 -230 295 -296 241 -278 239 -266 257 -256 289 -264 267 -234 299 -236 299 -264 269 -232 265 -262 257 -290 265 -234 259 -292 265 -232 299 -236 299 -264 269 -234 265 -262 255 -290 265 -240 303 -266 269 -232 265 -262 261 -262 261 -262 263 -262 257 -290 265 -240 303 -266 269 -232 265 -262 261 -262 263 -262 261 -264 261 -264 261 -262 295 -234 301 -266 269 -232 265 -262 255 -290 265 -234 259 -292 265 -232 299 -236 299 -266 267 -234 265 -262 257 -256 291 -262 269 -266 233 -264 295 -236 299 -264 269 -234 263 -262 261 -262 257 -258 291 -262 269 -234 265 -264 295 -234 299 -266 269 -232 265 -262 261 -262 261 -264 261 -264 261 -264 295 -234 299 -232 295 -264 267 -234 263 -264 257 -288 265 -268 265 -236 299 -266 267 -234 265 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 269 -232 265 -262 255 -290 265 -240 303 -268 269 -232 265 -228 289 -288 265 -268 231 -264 297 -234 299 -232 295 -262 267 -234 265 -264 261 -260 257 -290 265 -268 265 -270 273 -236 259 -290 263 -266 231 -264 263 -262 295 -236 299 -266 267 -234 263 -264 261 -262 257 -256 291 -230 295 -264 269 -234 299 -268 273 -234 265 -264 259 -262 261 -264 261 -262 257 -290 265 -234 299 -236 299 -266 267 -234 263 -264 261 -264 255 -290 265 -234 299 -268 273 -236 265 -262 259 -262 257 -290 265 -234 267 -264 295 -234 299 -266 267 -234 263 -264 261 -262 257 -256 291 -264 269 -234 259 -292 265 -232 297 -236 301 -264 269 -234 263 -264 255 -290 265 -234 265 -264 293 -236 299 -264 269 -234 265 -264 259 -262 255 -292 265 -234 265 -264 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 255 -292 265 -234 299 -270 273 -234 259 -290 265 -264 233 -264 295 -234 301 -264 269 -234 263 -262 255 -258 291 -230 295 -264 269 -234 299 -234 299 -266 267 -234 263 -264 255 -290 265 -240 303 -266 269 -232 265 -262 261 -262 261 -262 263 -262 257 -290 265 -234 299 -236 299 -266 269 -232 265 -230 287 -288 267 -234 259 -292 265 -234 297 -270 273 -236 263 -262 261 -262 261 -262 257 -290 267 -234 265 -264 261 -262 293 -270 273 -238 265 -230 287 -256 291 -262 269 -236 297 -236 299 -264 269 -234 263 -262 257 -256 291 -230 295 -264 269 -234 299 -234 301 -264 267 -234 263 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -236 -RAW_Data: 299 -264 269 -266 231 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -266 267 -266 231 -264 263 -262 255 -256 293 -262 269 -268 231 -262 295 -236 299 -264 269 -234 263 -264 261 -262 295 -268 273 -270 231 -264 259 -262 261 -264 257 -258 291 -262 269 -268 265 -234 299 -266 269 -232 265 -262 255 -290 265 -234 261 -292 265 -264 273 -240 263 -294 265 -232 259 -292 265 -264 265 -236 299 -266 269 -234 263 -262 261 -262 263 -262 257 -290 265 -268 231 -264 295 -268 273 -268 231 -264 261 -262 255 -258 293 -262 269 -268 231 -264 293 -234 299 -266 267 -268 233 -262 261 -262 261 -262 295 -236 301 -266 267 -234 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -264 269 -234 263 -262 257 -258 291 -262 269 -234 261 -290 263 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -290 267 -266 273 -274 235 -264 263 -260 257 -288 265 -266 233 -264 257 -290 265 -266 263 -236 301 -266 267 -234 263 -264 261 -264 255 -290 265 -266 233 -264 295 -234 299 -266 267 -234 259 -290 265 -238 303 -268 267 -234 263 -264 261 -262 255 -292 265 -266 233 -262 295 -236 299 -264 269 -234 263 -262 263 -262 263 -262 295 -268 273 -236 265 -262 261 -262 261 -262 257 -290 267 -266 233 -264 293 -234 301 -264 269 -234 263 -262 257 -290 265 -234 259 -292 265 -266 265 -268 273 -236 265 -264 261 -262 255 -290 265 -234 261 -258 291 -262 267 -268 265 -236 299 -266 267 -234 257 -260 289 -264 267 -268 265 -236 299 -264 269 -232 265 -264 261 -262 261 -262 261 -264 257 -290 265 -268 265 -236 299 -266 267 -234 263 -262 263 -262 257 -258 291 -262 269 -234 265 -264 293 -234 299 -266 269 -232 265 -262 257 -290 265 -266 265 -236 301 -230 295 -264 267 -234 263 -264 255 -290 265 -234 261 -290 265 -238 303 -234 295 -262 267 -234 263 -264 263 -262 261 -262 289 -264 277 -274 235 -264 261 -260 255 -290 265 -268 265 -236 299 -264 269 -234 263 -262 263 -262 263 -262 255 -258 291 -264 267 -268 265 -270 273 -236 263 -262 261 -262 261 -264 295 -234 301 -264 269 -234 263 -264 261 -262 263 -262 257 -288 265 -268 231 -264 297 -268 271 -236 265 -262 261 -262 255 -258 293 -262 269 -268 231 -262 295 -270 273 -234 265 -264 259 -262 261 -264 257 -290 265 -266 265 -270 273 -236 265 -262 261 -262 261 -264 255 -292 265 -266 231 -264 295 -268 273 -236 265 -262 261 -262 261 -264 257 -256 293 -262 269 -234 299 -234 299 -266 -RAW_Data: 267 -234 263 -264 261 -262 261 -264 255 -258 293 -262 269 -234 299 -268 273 -234 265 -264 259 -262 261 -264 257 -290 265 -266 265 -236 299 -232 295 -264 267 -234 263 -264 255 -258 291 -230 295 -264 267 -234 299 -268 273 -236 259 -258 291 -260 269 -240 303 -266 269 -232 263 -264 255 -258 289 -264 267 -268 231 -266 295 -234 299 -266 267 -234 263 -264 261 -262 263 -262 257 -288 273 -276 273 -234 263 -228 287 -288 265 -268 233 -264 261 -262 293 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 263 -262 295 -270 271 -236 265 -264 261 -260 263 -262 261 -264 255 -292 265 -266 233 -264 293 -234 301 -266 269 -232 259 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 255 -258 293 -262 269 -234 297 -236 301 -264 267 -234 263 -264 261 -262 255 -290 267 -234 259 -292 265 -232 265 -264 295 -270 271 -236 265 -262 261 -262 261 -262 263 -262 295 -236 301 -266 267 -234 263 -262 263 -262 255 -290 265 -268 231 -264 257 -290 265 -234 297 -270 273 -236 265 -262 259 -262 261 -264 263 -294 269 -274 235 -266 257 -288 265 -232 259 -292 267 -238 303 -266 269 -232 265 -262 259 -262 263 -262 257 -290 267 -266 265 -236 301 -264 267 -234 263 -264 261 -262 263 -262 261 -264 295 -234 301 -232 295 -262 269 -232 265 -262 261 -262 257 -290 265 -268 231 -264 295 -268 273 -236 265 -262 261 -262 261 -262 263 -262 263 -262 261 -264 295 -234 301 -264 269 -234 263 -262 263 -262 263 -262 287 -266 269 -270 271 -234 265 -262 261 -260 257 -290 267 -234 265 -264 293 -234 301 -266 267 -234 263 -264 255 -256 293 -230 295 -264 267 -234 265 -264 293 -234 299 -266 269 -234 263 -264 261 -260 257 -290 267 -234 259 -292 265 -232 297 -270 273 -236 265 -262 255 -290 265 -266 233 -264 295 -236 299 -230 295 -264 267 -234 263 -264 255 -258 291 -262 269 -234 265 -264 295 -268 271 -238 265 -262 255 -290 265 -266 265 -270 273 -236 263 -264 259 -262 257 -290 267 -266 233 -264 293 -234 301 -266 269 -232 265 -262 259 -262 257 -258 291 -264 267 -268 273 -274 235 -266 255 -256 289 -230 295 -264 269 -234 297 -236 299 -266 267 -234 263 -264 261 -262 257 -290 265 -234 259 -292 265 -232 265 -264 295 -268 273 -236 265 -264 259 -262 255 -258 291 -264 267 -236 297 -236 301 -230 295 -262 267 -234 265 -264 255 -290 265 -240 303 -266 269 -234 263 -262 261 -262 255 -258 291 -264 267 -236 297 -270 273 -236 263 -264 -RAW_Data: 259 -262 261 -264 257 -256 293 -262 269 -234 299 -236 299 -264 269 -232 265 -262 261 -264 261 -262 257 -256 291 -264 269 -240 303 -266 267 -234 263 -264 259 -262 257 -290 265 -234 261 -292 263 -234 263 -264 295 -270 273 -236 265 -230 287 -290 265 -234 259 -292 265 -266 271 -274 237 -266 261 -260 257 -256 289 -264 269 -234 299 -236 299 -264 269 -234 263 -262 261 -264 261 -262 257 -290 265 -268 265 -236 299 -264 269 -234 263 -262 261 -262 295 -236 301 -266 267 -234 263 -262 257 -290 265 -234 259 -292 265 -232 299 -236 299 -266 267 -234 263 -264 261 -262 257 -290 265 -266 233 -264 255 -290 265 -240 303 -268 267 -234 263 -262 261 -262 255 -290 267 -266 265 -270 273 -236 265 -262 261 -262 261 -262 257 -290 265 -268 265 -270 273 -236 263 -264 259 -262 261 -264 261 -264 255 -290 265 -268 265 -236 299 -266 267 -234 263 -264 261 -264 255 -290 265 -234 259 -292 265 -232 299 -270 273 -236 263 -262 257 -256 291 -262 269 -266 233 -264 295 -236 299 -264 269 -234 263 -262 261 -262 257 -290 267 -232 265 -264 261 -264 295 -268 273 -236 265 -262 261 -260 289 -266 269 -272 271 -234 263 -230 287 -258 291 -262 269 -234 265 -264 293 -234 301 -266 269 -232 265 -262 259 -262 263 -262 257 -290 267 -266 265 -270 273 -236 259 -290 265 -232 263 -264 297 -234 301 -230 295 -264 267 -234 263 -264 261 -262 257 -256 291 -264 269 -234 299 -236 299 -264 269 -232 265 -262 261 -262 261 -264 261 -264 261 -264 293 -236 299 -266 269 -234 263 -262 261 -262 255 -258 293 -262 269 -240 303 -266 269 -234 263 -262 261 -260 257 -290 267 -266 233 -264 293 -236 299 -264 269 -234 265 -264 259 -262 255 -292 265 -266 233 -264 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 255 -292 265 -234 265 -264 293 -268 273 -236 267 -262 261 -260 263 -262 257 -290 265 -234 265 -264 295 -234 299 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -266 267 -270 271 -236 261 -288 265 -264 233 -264 295 -268 273 -236 265 -262 261 -262 255 -258 293 -230 295 -262 269 -240 303 -266 267 -234 263 -262 257 -256 291 -262 269 -274 277 -238 265 -228 287 -256 291 -262 269 -236 265 -264 293 -234 299 -266 269 -234 263 -262 261 -262 257 -258 291 -264 267 -234 299 -236 299 -266 267 -234 259 -290 265 -232 297 -236 301 -264 269 -234 263 -264 259 -262 257 -290 267 -234 265 -264 261 -262 293 -236 301 -266 267 -234 263 -264 259 -262 263 -262 -RAW_Data: 257 -258 291 -264 269 -234 265 -262 293 -236 299 -266 269 -232 265 -262 261 -264 255 -290 267 -232 261 -292 265 -238 303 -266 269 -232 263 -264 259 -262 257 -290 267 -234 259 -292 265 -232 297 -236 301 -266 267 -234 263 -262 261 -264 261 -264 261 -262 257 -288 267 -266 265 -270 273 -236 265 -262 261 -260 257 -258 323 -238 279 -276 235 -230 287 -288 265 -234 263 -264 263 -262 295 -270 273 -236 265 -262 259 -262 257 -256 291 -230 295 -264 269 -234 299 -236 299 -264 269 -232 259 -290 265 -232 299 -236 301 -264 269 -234 263 -262 261 -264 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -234 263 -262 263 -262 255 -258 291 -262 269 -234 299 -236 299 -264 269 -234 263 -262 263 -262 261 -262 263 -262 295 -236 299 -266 267 -234 265 -262 255 -290 265 -234 259 -292 265 -234 297 -236 299 -266 269 -234 263 -262 257 -256 291 -262 269 -236 259 -290 265 -232 299 -236 299 -266 267 -234 263 -264 261 -262 261 -264 261 -264 261 -262 295 -270 273 -236 265 -262 261 -260 257 -290 265 -236 265 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 261 -264 257 -290 265 -234 299 -270 273 -234 259 -290 265 -232 265 -264 295 -270 273 -236 263 -264 259 -262 261 -264 257 -290 265 -234 297 -236 301 -264 269 -232 265 -262 261 -264 261 -262 261 -296 269 -274 269 -232 261 -262 261 -262 261 -264 255 -292 265 -234 265 -264 293 -234 301 -266 269 -232 263 -264 261 -262 263 -262 255 -290 265 -268 231 -264 295 -270 273 -236 263 -264 261 -260 261 -264 255 -290 267 -234 265 -264 295 -234 299 -232 295 -230 293 -230 295 -264 267 -240 303 -266 267 -234 263 -262 261 -262 261 -264 261 -264 261 -262 295 -236 301 -232 295 -262 267 -232 265 -264 261 -262 257 -256 293 -262 269 -234 265 -264 293 -234 301 -266 269 -234 263 -262 255 -258 291 -262 269 -234 299 -270 271 -236 265 -230 287 -290 265 -234 259 -292 265 -238 303 -268 267 -234 263 -262 261 -262 255 -258 293 -230 293 -264 269 -268 271 -274 235 -266 261 -262 261 -260 257 -290 267 -234 299 -234 301 -264 267 -234 265 -262 257 -288 265 -234 261 -292 265 -232 297 -236 301 -266 267 -234 263 -262 261 -262 295 -234 301 -266 269 -234 263 -262 261 -262 257 -290 265 -234 261 -292 265 -232 297 -236 301 -266 267 -234 263 -262 255 -258 291 -230 295 -264 267 -236 297 -236 299 -264 269 -234 263 -262 261 -262 261 -264 257 -290 265 -240 303 -268 267 -234 263 -230 -RAW_Data: 287 -290 265 -234 259 -292 265 -234 297 -236 301 -266 267 -234 263 -262 255 -290 265 -236 259 -260 289 -262 267 -240 305 -232 295 -262 267 -234 263 -264 261 -264 261 -262 257 -256 291 -264 267 -240 305 -266 267 -234 257 -290 265 -232 265 -264 257 -290 265 -234 299 -236 299 -264 269 -234 263 -262 261 -264 255 -258 291 -262 269 -234 299 -236 299 -266 267 -234 259 -290 265 -232 297 -270 273 -236 265 -264 261 -260 257 -256 293 -262 269 -234 299 -236 299 -264 269 -232 265 -262 261 -264 261 -264 261 -262 257 -288 273 -276 273 -234 263 -228 287 -290 263 -268 233 -264 295 -236 299 -264 269 -232 265 -262 261 -264 261 -264 261 -262 263 -262 293 -236 301 -266 267 -234 263 -230 289 -288 265 -268 233 -264 255 -290 265 -234 299 -236 299 -264 269 -232 265 -262 257 -288 265 -268 265 -236 299 -266 269 -234 263 -262 261 -262 261 -264 255 -258 293 -262 269 -240 303 -266 269 -232 265 -262 259 -262 257 -256 293 -262 269 -234 261 -292 265 -232 297 -236 301 -264 269 -234 263 -262 261 -262 255 -292 265 -240 303 -266 269 -232 265 -262 261 -260 263 -262 257 -292 265 -232 291 -260 267 -238 303 -266 269 -232 265 -262 261 -260 263 -262 263 -262 261 -296 271 -272 237 -234 287 -288 265 -232 299 -230 295 -270 273 -236 263 -264 261 -260 261 -264 263 -262 255 -290 267 -234 297 -236 301 -264 269 -232 265 -262 261 -264 295 -234 301 -264 269 -234 263 -262 261 -262 261 -264 263 -262 255 -290 267 -234 299 -236 299 -264 269 -232 265 -262 257 -256 291 -230 295 -264 267 -240 305 -232 295 -262 265 -234 263 -264 263 -262 255 -290 265 -234 265 -264 295 -270 271 -268 233 -262 257 -256 291 -264 269 -266 233 -262 295 -234 299 -264 269 -266 231 -264 261 -264 255 -258 291 -262 269 -268 265 -236 299 -232 293 -264 267 -266 231 -264 261 -264 255 -290 265 -234 259 -292 265 -272 269 -266 269 -234 257 -292 263 -234 259 -292 265 -266 265 -236 299 -232 293 -264 267 -268 231 -264 261 -262 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 261 -262 297 -268 273 -270 231 -264 261 -260 261 -264 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 255 -258 291 -230 295 -296 241 -276 239 -268 257 -256 291 -262 267 -268 231 -264 293 -234 301 -266 269 -232 263 -264 261 -262 261 -264 257 -256 293 -262 269 -268 265 -268 273 -268 231 -262 261 -262 257 -256 293 -262 269 -268 231 -264 295 -234 299 -266 -RAW_Data: 269 -232 265 -262 257 -288 267 -266 265 -270 273 -236 263 -262 261 -262 263 -262 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -262 255 -258 291 -262 269 -234 261 -292 265 -264 265 -236 301 -264 269 -232 263 -264 261 -264 261 -262 255 -292 265 -274 275 -236 265 -262 261 -262 255 -258 293 -262 269 -234 261 -290 265 -266 263 -236 301 -266 267 -234 263 -264 255 -256 293 -230 293 -264 269 -274 275 -236 259 -258 289 -262 269 -268 265 -268 273 -268 231 -264 261 -260 263 -262 257 -290 265 -266 231 -266 295 -234 301 -264 267 -266 231 -264 257 -258 289 -270 273 -270 273 -234 263 -262 255 -290 265 -232 261 -292 265 -266 263 -236 301 -266 267 -234 263 -264 259 -262 257 -290 265 -268 233 -264 293 -234 301 -266 267 -234 263 -264 261 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 259 -262 263 -262 297 -234 299 -266 267 -234 263 -264 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -232 265 -262 261 -264 255 -258 291 -230 295 -264 267 -268 265 -236 299 -264 267 -268 231 -262 257 -258 291 -262 269 -268 265 -234 299 -232 295 -262 269 -266 231 -264 261 -262 257 -290 265 -266 233 -262 295 -236 299 -266 267 -234 263 -262 263 -262 289 -264 269 -270 271 -236 263 -262 255 -258 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 263 -262 257 -290 265 -272 269 -266 269 -234 259 -290 265 -264 231 -264 297 -234 301 -264 267 -266 231 -264 263 -262 261 -262 257 -258 291 -264 267 -240 303 -266 267 -234 263 -264 261 -260 263 -262 263 -262 263 -262 295 -268 273 -268 233 -262 261 -262 261 -264 257 -258 291 -262 267 -268 265 -270 273 -234 265 -262 261 -262 261 -264 257 -290 265 -268 231 -264 295 -268 271 -270 233 -262 261 -260 263 -262 257 -258 291 -264 267 -268 265 -236 299 -232 293 -262 269 -266 233 -262 257 -256 291 -262 269 -268 231 -296 269 -272 235 -266 263 -260 261 -262 257 -292 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 269 -234 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 263 -262 257 -256 291 -264 267 -236 259 -292 265 -236 303 -266 269 -232 265 -262 257 -258 291 -262 269 -274 275 -236 265 -262 255 -258 289 -262 269 -268 231 -264 263 -260 295 -236 301 -266 267 -234 263 -262 257 -256 291 -264 267 -268 233 -262 295 -268 271 -270 233 -262 261 -262 -RAW_Data: 255 -258 293 -262 269 -268 231 -262 295 -234 301 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 261 -264 293 -268 273 -268 233 -262 261 -262 257 -256 293 -262 269 -268 265 -234 301 -232 293 -262 267 -268 231 -264 261 -262 255 -292 265 -266 233 -264 295 -236 299 -264 267 -266 231 -264 261 -264 261 -262 295 -234 301 -266 267 -234 263 -264 261 -262 257 -290 265 -268 231 -264 255 -290 265 -274 269 -266 267 -234 263 -264 255 -258 291 -230 295 -304 243 -274 235 -264 257 -288 263 -266 231 -266 295 -234 301 -264 269 -234 263 -262 263 -262 257 -256 291 -230 295 -264 267 -268 265 -270 271 -268 231 -264 255 -290 265 -266 265 -236 299 -266 269 -234 263 -262 263 -262 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -292 265 -264 265 -236 301 -266 267 -234 263 -264 261 -264 255 -290 265 -266 233 -264 295 -268 273 -268 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 265 -270 273 -268 231 -264 261 -262 255 -258 291 -264 267 -268 233 -262 295 -234 299 -266 269 -232 265 -262 261 -264 255 -292 265 -266 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -232 295 -262 267 -268 231 -264 261 -260 263 -262 257 -292 263 -266 265 -270 273 -236 261 -258 289 -262 267 -268 265 -236 299 -266 267 -234 263 -264 261 -262 263 -262 261 -262 261 -264 263 -262 295 -236 299 -264 269 -234 263 -262 263 -262 257 -258 289 -264 267 -268 233 -264 293 -234 299 -266 269 -234 263 -262 263 -262 261 -264 261 -262 295 -270 273 -268 233 -262 261 -262 261 -262 257 -290 265 -266 231 -298 263 -234 301 -264 267 -268 231 -264 261 -260 257 -290 267 -234 259 -292 231 -298 265 -236 301 -264 235 -300 231 -262 263 -262 261 -264 255 -292 265 -264 271 -274 237 -266 261 -262 257 -256 293 -230 293 -230 295 -264 267 -266 265 -268 273 -268 233 -264 259 -262 257 -292 231 -298 231 -298 229 -262 295 -268 273 -268 233 -262 257 -290 265 -264 233 -296 263 -236 299 -264 269 -266 231 -264 261 -264 261 -262 255 -292 265 -266 231 -296 263 -270 271 -268 231 -264 261 -264 255 -258 291 -262 267 -268 233 -264 295 -234 299 -264 269 -266 233 -262 261 -262 257 -290 265 -266 265 -270 273 -268 231 -264 261 -262 261 -264 257 -258 291 -262 269 -266 265 -236 299 -264 269 -266 231 -264 261 -262 257 -258 291 -264 267 -268 231 -262 295 -234 299 -266 235 -300 -RAW_Data: 231 -264 261 -262 255 -258 293 -262 269 -272 269 -266 235 -300 231 -262 261 -294 229 -264 257 -258 291 -262 267 -268 265 -236 301 -264 267 -266 231 -264 257 -290 265 -266 231 -264 263 -294 269 -272 235 -266 259 -288 265 -264 265 -236 301 -266 233 -300 231 -264 261 -262 261 -262 263 -262 257 -290 265 -266 265 -270 271 -270 233 -262 261 -262 295 -234 301 -266 267 -266 231 -264 261 -264 255 -290 265 -266 233 -264 257 -290 263 -266 265 -270 273 -268 233 -262 261 -262 257 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -262 257 -290 265 -266 233 -262 297 -268 271 -268 233 -262 257 -258 291 -264 267 -268 263 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -232 259 -292 267 -266 263 -236 301 -266 233 -300 231 -262 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 233 -268 259 -290 265 -232 259 -292 265 -266 265 -234 301 -266 235 -300 231 -262 261 -262 257 -258 291 -230 295 -264 267 -274 267 -266 235 -300 231 -262 261 -294 229 -296 263 -270 271 -270 231 -264 261 -262 255 -290 267 -232 259 -260 293 -262 267 -268 263 -270 273 -268 231 -264 261 -262 255 -258 291 -264 267 -268 273 -272 235 -266 257 -256 291 -260 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -266 265 -236 299 -232 293 -264 267 -268 231 -262 263 -262 261 -262 263 -262 257 -258 293 -262 267 -274 267 -234 295 -262 267 -266 233 -262 261 -262 263 -262 257 -290 265 -266 265 -236 301 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -266 267 -236 299 -264 269 -266 231 -264 261 -262 261 -262 257 -258 291 -264 267 -274 269 -266 235 -300 231 -262 261 -262 289 -264 269 -270 271 -266 231 -262 263 -262 257 -290 265 -234 259 -260 291 -262 267 -266 267 -268 273 -270 231 -262 261 -262 257 -256 293 -262 269 -268 271 -272 235 -266 257 -258 289 -228 295 -264 267 -268 265 -236 299 -264 269 -266 231 -264 261 -264 255 -258 291 -262 269 -266 265 -236 299 -266 267 -266 231 -264 263 -262 257 -256 291 -262 269 -268 263 -236 299 -232 295 -264 267 -266 231 -264 261 -262 257 -290 265 -272 269 -266 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -260 257 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 257 -288 265 -266 263 -236 301 -266 269 -266 231 -262 261 -264 257 -290 265 -232 259 -292 -RAW_Data: 265 -266 231 -264 297 -268 271 -268 233 -262 261 -262 257 -258 291 -262 269 -268 271 -274 235 -266 257 -288 265 -264 233 -264 295 -236 299 -264 267 -266 231 -264 263 -262 257 -290 265 -232 259 -294 265 -266 263 -236 301 -264 269 -266 231 -264 261 -262 295 -234 299 -266 269 -266 231 -262 263 -262 263 -262 257 -256 291 -264 267 -268 265 -270 271 -268 231 -264 261 -262 261 -262 259 -290 265 -232 261 -292 231 -300 263 -236 301 -266 233 -300 231 -262 263 -262 261 -264 255 -290 265 -266 233 -264 295 -234 299 -266 269 -266 231 -262 257 -258 291 -230 295 -264 267 -272 269 -266 267 -266 231 -264 261 -264 255 -290 265 -232 261 -292 265 -266 263 -236 301 -232 295 -262 267 -268 229 -264 257 -258 291 -230 295 -296 241 -276 239 -266 257 -290 263 -234 259 -258 293 -262 269 -272 269 -266 235 -300 229 -264 261 -264 259 -262 257 -258 293 -262 269 -266 265 -236 299 -264 269 -234 257 -292 265 -266 263 -236 301 -266 235 -300 229 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -264 269 -268 231 -264 257 -256 291 -230 293 -264 269 -268 265 -234 299 -266 267 -266 231 -264 263 -262 257 -288 265 -266 265 -268 273 -270 233 -262 257 -288 265 -234 259 -292 265 -266 265 -236 299 -266 235 -298 231 -264 261 -262 263 -262 263 -262 257 -290 265 -264 265 -236 301 -266 235 -300 229 -264 261 -262 261 -264 257 -290 265 -266 233 -264 293 -268 273 -236 259 -292 231 -266 259 -258 293 -262 267 -268 265 -234 299 -266 267 -268 229 -264 263 -262 261 -294 229 -264 261 -264 295 -268 271 -270 231 -264 255 -290 265 -266 265 -270 273 -268 233 -262 257 -290 265 -232 259 -292 265 -266 265 -234 301 -266 269 -266 229 -264 261 -264 261 -262 261 -262 263 -296 269 -272 237 -266 261 -262 261 -262 261 -264 261 -262 295 -236 299 -266 267 -268 229 -264 261 -262 257 -290 265 -234 261 -292 231 -298 265 -236 299 -266 235 -300 231 -262 257 -290 263 -266 231 -264 259 -290 265 -266 265 -236 299 -266 235 -266 259 -258 291 -262 267 -274 269 -266 235 -300 231 -262 261 -262 261 -264 263 -262 261 -264 295 -234 299 -266 267 -268 231 -262 261 -264 255 -290 265 -234 261 -258 291 -262 267 -274 269 -266 267 -266 231 -264 261 -262 263 -262 261 -264 295 -234 299 -266 269 -266 231 -264 261 -260 257 -258 293 -262 269 -234 261 -290 231 -298 265 -270 271 -270 231 -264 261 -262 255 -290 267 -266 233 -296 267 -238 265 -294 233 -298 -RAW_Data: 231 -262 263 -262 295 -270 271 -268 233 -262 263 -262 261 -262 257 -290 265 -268 231 -264 293 -234 301 -266 267 -268 229 -264 257 -290 263 -274 269 -266 269 -266 231 -262 261 -264 255 -292 265 -232 259 -292 265 -272 269 -232 295 -262 267 -268 231 -264 257 -256 291 -230 295 -264 269 -272 267 -266 269 -266 231 -262 261 -262 257 -292 265 -266 231 -264 257 -290 265 -272 275 -272 231 -264 255 -256 291 -264 267 -268 231 -264 293 -236 299 -264 269 -266 231 -262 257 -258 291 -264 267 -268 233 -262 295 -232 299 -266 269 -268 231 -264 259 -262 257 -258 291 -264 267 -236 259 -292 263 -272 267 -268 269 -266 231 -262 257 -290 263 -266 231 -266 295 -234 301 -232 295 -262 269 -266 231 -262 261 -264 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 295 -268 271 -270 231 -264 261 -264 259 -262 257 -292 265 -266 265 -236 301 -264 267 -266 231 -264 261 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 261 -262 255 -292 265 -266 267 -234 299 -266 267 -266 231 -264 263 -262 261 -262 261 -264 261 -262 295 -236 299 -266 269 -266 231 -262 257 -258 291 -230 295 -264 267 -266 265 -234 301 -230 295 -264 267 -266 231 -264 263 -262 261 -260 295 -236 301 -266 267 -266 231 -264 261 -262 261 -264 257 -258 291 -262 269 -274 267 -268 267 -266 231 -262 261 -262 263 -262 257 -258 291 -264 267 -268 265 -236 299 -230 295 -262 267 -268 231 -264 261 -262 257 -258 291 -264 267 -274 275 -270 231 -262 261 -262 255 -292 265 -234 259 -260 291 -260 267 -274 269 -268 267 -266 231 -264 261 -262 257 -290 265 -266 233 -264 293 -268 273 -268 233 -262 257 -290 263 -266 265 -270 273 -270 231 -264 261 -262 259 -262 263 -262 263 -262 263 -262 295 -236 299 -264 269 -268 231 -262 261 -262 295 -236 299 -266 269 -266 231 -262 261 -262 257 -290 265 -266 233 -264 261 -264 295 -234 299 -266 267 -268 229 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 263 -262 255 -292 265 -232 259 -292 267 -264 265 -270 273 -236 259 -258 291 -228 293 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -290 265 -266 267 -236 299 -264 267 -268 229 -264 263 -262 257 -288 265 -234 259 -292 265 -266 265 -236 299 -266 269 -266 231 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 233 -262 261 -262 255 -258 291 -230 295 -264 269 -266 265 -236 299 -264 269 -266 -RAW_Data: 231 -262 263 -262 295 -268 273 -268 233 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -264 269 -266 231 -264 261 -264 255 -290 265 -234 259 -292 265 -272 269 -266 269 -266 231 -262 257 -290 265 -266 265 -236 299 -266 267 -266 231 -264 263 -262 261 -262 261 -264 257 -290 265 -272 269 -266 269 -266 231 -262 257 -258 289 -264 267 -268 233 -264 293 -234 299 -266 267 -268 231 -264 261 -262 255 -290 267 -266 233 -264 293 -268 273 -268 233 -262 261 -262 257 -290 265 -234 261 -290 265 -272 269 -266 269 -266 231 -262 261 -262 257 -258 291 -264 269 -266 233 -262 295 -234 299 -266 267 -268 231 -262 261 -262 257 -258 291 -230 295 -264 267 -268 271 -272 235 -266 263 -260 261 -262 257 -258 291 -264 267 -268 265 -236 299 -232 293 -264 267 -266 231 -264 257 -290 265 -232 259 -294 265 -272 269 -232 295 -230 293 -264 267 -268 265 -268 273 -268 231 -264 261 -260 257 -290 265 -234 261 -292 265 -264 265 -236 299 -266 269 -266 231 -262 257 -256 291 -230 295 -306 243 -272 235 -264 257 -288 265 -264 233 -264 257 -290 265 -266 265 -236 301 -264 269 -234 263 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -234 301 -230 297 -262 267 -268 231 -264 259 -262 295 -236 299 -234 295 -262 267 -266 233 -262 257 -258 289 -262 269 -268 265 -234 299 -266 267 -268 233 -262 261 -262 255 -258 291 -264 269 -266 233 -262 295 -268 271 -270 233 -262 261 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -264 269 -266 231 -264 261 -262 257 -290 265 -232 261 -292 265 -266 265 -268 273 -268 233 -262 261 -262 263 -262 257 -290 263 -274 275 -270 233 -262 257 -256 291 -262 269 -266 267 -236 299 -264 267 -268 229 -264 261 -264 255 -290 265 -268 231 -264 295 -236 297 -266 269 -266 231 -262 257 -290 265 -272 269 -234 295 -262 267 -266 231 -264 263 -262 257 -290 263 -234 259 -292 265 -274 267 -268 267 -266 231 -264 261 -260 263 -262 263 -262 261 -264 295 -236 299 -264 269 -266 231 -264 261 -264 261 -260 261 -264 257 -290 265 -272 275 -272 231 -264 259 -262 257 -290 265 -234 261 -290 265 -272 269 -266 269 -266 231 -262 261 -262 257 -292 265 -232 259 -292 267 -264 265 -236 301 -266 267 -266 231 -262 263 -262 261 -264 255 -258 291 -264 267 -274 267 -266 269 -266 231 -264 255 -258 291 -262 269 -266 231 -264 295 -236 299 -264 267 -268 231 -262 -RAW_Data: 263 -262 261 -262 263 -262 261 -264 295 -236 299 -264 269 -234 259 -290 265 -266 265 -236 299 -266 267 -266 231 -264 255 -290 267 -232 259 -292 265 -274 267 -234 295 -262 267 -266 231 -264 257 -258 291 -230 295 -296 241 -276 237 -268 257 -258 289 -262 267 -234 261 -292 265 -264 265 -236 299 -266 267 -266 231 -264 263 -262 257 -256 291 -230 295 -264 267 -268 263 -270 273 -268 231 -264 261 -262 255 -290 265 -234 259 -260 291 -262 269 -266 265 -236 299 -232 295 -264 267 -266 231 -264 295 -234 299 -266 267 -268 229 -264 261 -264 255 -290 265 -266 231 -266 295 -234 299 -266 267 -268 229 -264 261 -264 261 -262 257 -258 291 -264 267 -268 231 -264 295 -268 271 -268 233 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 301 -266 267 -266 231 -262 257 -290 265 -234 259 -324 239 -276 237 -268 263 -262 261 -262 255 -292 265 -264 233 -264 295 -236 299 -264 267 -268 231 -262 263 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -268 233 -262 261 -262 255 -292 265 -234 261 -292 263 -266 231 -264 295 -236 299 -264 269 -232 259 -260 289 -262 267 -268 265 -236 301 -264 267 -266 231 -264 257 -290 265 -232 259 -292 265 -266 265 -236 301 -264 269 -266 231 -262 263 -262 257 -290 265 -234 259 -292 265 -266 231 -262 297 -268 271 -270 231 -264 261 -260 263 -262 263 -262 295 -234 301 -266 267 -268 229 -264 261 -264 255 -258 291 -262 269 -234 259 -292 265 -272 267 -266 269 -268 229 -264 255 -258 291 -264 267 -274 275 -270 231 -264 255 -290 263 -266 231 -266 295 -236 299 -264 269 -266 231 -262 261 -264 261 -262 257 -258 291 -264 267 -268 265 -270 271 -268 231 -264 261 -262 261 -262 297 -234 299 -266 267 -266 231 -264 263 -262 257 -256 293 -262 269 -234 259 -292 231 -298 265 -270 271 -270 231 -262 261 -262 257 -258 291 -230 295 -264 269 -266 263 -236 299 -266 267 -268 231 -262 261 -264 261 -262 295 -236 299 -266 269 -266 231 -262 261 -264 257 -288 265 -266 231 -264 297 -234 299 -266 267 -268 229 -264 261 -264 257 -288 265 -268 231 -264 257 -290 265 -266 263 -236 301 -266 269 -266 231 -262 261 -262 257 -258 291 -264 267 -268 271 -274 235 -264 263 -260 257 -258 291 -262 269 -268 265 -234 299 -232 295 -262 267 -268 231 -264 261 -262 255 -258 293 -262 269 -268 265 -268 271 -236 261 -290 231 -298 265 -236 299 -266 269 -266 -RAW_Data: 231 -262 261 -262 257 -290 267 -264 231 -264 295 -236 301 -266 267 -266 231 -262 263 -262 261 -264 261 -296 269 -272 235 -266 263 -260 261 -262 257 -258 291 -264 267 -268 265 -270 271 -268 231 -264 261 -262 257 -290 265 -266 233 -264 261 -262 293 -270 273 -268 233 -264 261 -260 263 -262 257 -290 265 -268 231 -264 261 -262 293 -236 301 -266 267 -234 259 -258 291 -262 267 -268 265 -234 301 -264 267 -266 231 -264 263 -262 261 -262 261 -264 261 -264 295 -234 301 -264 267 -268 233 -262 261 -260 263 -262 257 -258 293 -262 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 261 -262 263 -262 295 -236 299 -264 269 -268 231 -262 261 -262 255 -292 265 -234 261 -258 291 -262 267 -274 269 -266 267 -266 231 -264 261 -262 263 -262 263 -294 269 -272 237 -266 261 -262 261 -262 255 -292 265 -272 269 -266 269 -266 231 -262 263 -262 257 -290 263 -234 261 -292 265 -264 265 -270 273 -268 231 -264 261 -262 261 -262 297 -268 271 -270 231 -264 261 -264 259 -262 257 -292 265 -264 233 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -258 291 -264 269 -266 233 -262 295 -268 271 -270 233 -262 261 -262 255 -292 265 -234 261 -292 263 -272 275 -270 233 -262 261 -262 255 -290 267 -232 259 -292 265 -266 265 -270 273 -268 231 -264 255 -258 291 -264 267 -234 261 -292 263 -266 263 -236 301 -266 267 -266 231 -264 261 -260 257 -292 265 -232 259 -294 265 -266 263 -236 301 -266 267 -234 259 -258 289 -262 267 -268 233 -264 293 -234 301 -266 269 -266 229 -264 261 -264 255 -258 291 -230 295 -264 267 -266 265 -270 271 -270 231 -264 255 -290 263 -274 269 -266 269 -266 231 -262 261 -262 257 -258 293 -262 269 -268 265 -234 299 -264 269 -266 231 -264 255 -290 265 -232 259 -292 267 -266 271 -274 237 -266 261 -262 255 -290 263 -266 231 -266 295 -234 299 -266 269 -266 231 -262 263 -262 261 -264 261 -262 263 -262 295 -268 271 -270 233 -264 261 -260 257 -258 291 -264 267 -268 231 -264 293 -236 299 -264 269 -268 231 -262 261 -262 261 -264 261 -264 295 -268 271 -270 233 -264 261 -260 261 -262 257 -290 267 -266 231 -264 295 -234 301 -264 269 -266 231 -264 259 -262 257 -258 293 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 261 -262 255 -258 293 -262 269 -274 267 -266 269 -266 229 -264 261 -264 261 -262 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 257 -290 265 -264 233 -264 263 -294 -RAW_Data: 269 -270 237 -266 257 -258 289 -262 269 -274 269 -266 267 -268 229 -262 261 -262 263 -262 263 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 295 -234 301 -266 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 259 -290 263 -266 265 -270 271 -270 233 -262 261 -262 257 -290 265 -266 231 -264 295 -236 299 -266 269 -266 231 -262 261 -262 261 -264 257 -290 265 -266 231 -264 297 -268 271 -270 231 -264 255 -290 265 -232 259 -292 267 -272 267 -234 295 -262 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 257 -290 265 -234 259 -290 265 -266 265 -236 301 -266 233 -268 259 -290 265 -232 259 -292 265 -266 265 -234 301 -266 269 -266 231 -262 261 -264 255 -258 291 -264 267 -268 231 -264 293 -236 299 -264 269 -266 231 -264 257 -290 265 -272 275 -270 233 -262 261 -262 255 -290 267 -266 231 -264 257 -290 265 -266 265 -268 273 -270 231 -264 261 -262 255 -258 291 -264 267 -268 273 -272 235 -266 257 -288 265 -232 259 -292 265 -266 265 -270 273 -268 231 -264 261 -262 255 -292 265 -232 261 -292 265 -264 265 -236 301 -266 267 -266 231 -262 261 -262 263 -262 257 -292 265 -264 233 -264 295 -234 299 -266 269 -266 231 -262 257 -290 265 -272 269 -266 269 -266 231 -262 261 -262 263 -262 263 -262 257 -290 265 -274 267 -268 267 -266 231 -264 261 -262 261 -262 259 -258 291 -262 267 -268 265 -236 299 -264 269 -266 231 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -268 233 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -236 299 -264 269 -266 231 -264 257 -288 265 -234 259 -292 265 -266 265 -234 301 -234 293 -264 267 -268 263 -234 299 -266 267 -268 231 -264 257 -288 265 -232 261 -258 293 -262 269 -266 263 -270 273 -268 231 -264 261 -262 257 -256 293 -304 243 -274 235 -264 261 -262 261 -262 261 -264 257 -290 265 -264 265 -236 301 -266 269 -266 229 -264 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 267 -268 229 -264 261 -264 255 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -268 231 -264 259 -262 261 -264 295 -234 299 -266 269 -266 231 -264 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 255 -258 291 -264 267 -268 231 -264 293 -236 299 -266 269 -266 229 -264 261 -264 261 -262 257 -290 265 -266 233 -264 -RAW_Data: 295 -268 271 -270 231 -262 261 -262 257 -258 293 -302 243 -274 235 -266 255 -256 289 -230 295 -264 267 -268 265 -270 271 -270 231 -262 261 -262 261 -264 255 -258 293 -262 269 -274 267 -268 233 -300 231 -262 261 -262 295 -236 299 -264 269 -266 231 -262 263 -262 263 -262 257 -290 265 -266 233 -264 295 -268 271 -270 231 -262 261 -262 257 -292 265 -264 273 -274 235 -266 263 -262 259 -262 255 -260 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 263 -262 257 -258 291 -230 293 -264 269 -272 269 -264 269 -266 231 -262 263 -262 257 -290 265 -232 259 -294 265 -266 263 -236 301 -266 233 -268 259 -290 265 -264 265 -236 301 -266 267 -266 231 -264 261 -262 261 -264 255 -292 265 -264 265 -236 301 -266 267 -268 229 -264 261 -262 255 -292 265 -234 261 -290 265 -266 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -268 263 -236 301 -264 267 -266 231 -264 263 -262 255 -290 265 -234 261 -260 291 -260 267 -268 265 -270 273 -268 231 -264 261 -260 257 -258 291 -264 267 -268 273 -238 263 -294 231 -266 259 -292 263 -274 267 -268 233 -300 231 -264 261 -262 257 -258 291 -230 295 -262 269 -266 265 -268 273 -268 233 -262 261 -262 257 -290 265 -274 275 -270 231 -264 259 -262 261 -264 257 -290 265 -266 231 -264 263 -262 295 -270 271 -268 233 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 235 -300 231 -262 261 -262 261 -264 263 -262 257 -290 265 -266 263 -270 273 -268 233 -262 261 -262 263 -262 257 -290 265 -266 265 -236 301 -266 267 -266 231 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 235 -300 231 -262 257 -256 291 -230 293 -264 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 257 -288 265 -266 231 -264 297 -234 299 -266 267 -268 229 -264 263 -262 257 -288 265 -268 231 -264 261 -262 295 -268 273 -270 233 -262 261 -262 287 -264 269 -270 273 -268 231 -262 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -262 255 -290 265 -232 261 -292 265 -266 231 -296 269 -240 263 -294 231 -300 231 -262 263 -262 295 -268 273 -268 233 -262 261 -262 261 -264 263 -262 261 -264 261 -262 295 -236 299 -264 269 -266 231 -264 261 -262 261 -262 263 -262 263 -262 295 -236 299 -266 267 -266 231 -264 261 -262 257 -258 291 -264 267 -274 269 -266 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 261 -264 255 -258 291 -264 -RAW_Data: 267 -268 229 -264 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 261 -264 261 -262 295 -270 271 -270 231 -264 261 -262 261 -262 257 -258 291 -264 267 -268 263 -236 299 -266 267 -268 229 -264 255 -258 293 -230 293 -264 269 -274 267 -266 269 -232 259 -290 265 -266 263 -236 301 -266 267 -266 231 -264 255 -290 265 -268 231 -264 257 -290 263 -266 265 -236 301 -266 267 -266 231 -264 255 -290 265 -272 269 -268 235 -268 263 -262 255 -290 265 -266 231 -264 257 -292 265 -266 263 -270 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -272 269 -266 269 -266 231 -262 261 -262 261 -264 257 -292 265 -264 231 -264 263 -262 295 -270 271 -270 231 -264 255 -258 289 -264 267 -274 269 -234 293 -264 267 -266 231 -262 263 -262 257 -258 291 -262 269 -266 265 -236 299 -266 267 -266 231 -262 263 -262 261 -264 257 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -262 263 -262 257 -290 265 -266 263 -236 301 -266 269 -266 231 -262 257 -290 265 -232 261 -292 265 -266 263 -236 301 -266 233 -300 231 -264 255 -258 291 -262 269 -274 275 -270 231 -262 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 261 -264 257 -290 265 -232 261 -290 265 -272 277 -270 233 -262 261 -260 257 -290 265 -266 265 -270 273 -268 233 -264 259 -262 257 -290 265 -234 261 -290 265 -266 265 -236 299 -266 267 -266 231 -264 255 -258 291 -230 295 -264 267 -268 229 -264 295 -234 299 -266 269 -266 231 -262 261 -262 263 -262 297 -268 273 -268 231 -264 261 -262 255 -292 265 -234 259 -292 265 -266 263 -270 273 -268 233 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -266 267 -266 231 -262 263 -262 257 -290 265 -234 259 -292 265 -266 271 -274 237 -266 261 -260 257 -256 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -262 261 -262 257 -258 293 -262 269 -266 265 -234 301 -232 295 -262 267 -266 233 -262 295 -268 273 -268 233 -262 261 -262 257 -290 265 -266 233 -264 261 -262 295 -268 273 -270 231 -264 261 -260 257 -290 265 -234 293 -266 267 -270 271 -268 229 -264 261 -262 255 -290 267 -266 233 -262 297 -234 299 -264 269 -266 231 -264 261 -264 261 -262 257 -290 265 -264 265 -236 301 -266 269 -266 229 -264 261 -264 255 -290 265 -234 259 -260 291 -262 267 -268 265 -270 271 -268 233 -262 261 -262 261 -264 295 -268 273 -268 231 -264 261 -262 261 -264 261 -264 261 -262 -RAW_Data: 263 -262 295 -234 301 -266 267 -268 229 -264 261 -262 263 -262 263 -262 257 -290 265 -266 231 -264 295 -268 273 -268 231 -264 261 -262 257 -290 265 -266 265 -236 301 -264 267 -266 231 -264 263 -262 261 -264 257 -256 291 -262 269 -266 265 -270 273 -268 231 -264 261 -262 261 -262 263 -294 269 -272 237 -266 263 -260 261 -262 257 -258 291 -264 269 -272 269 -264 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 257 -290 263 -272 269 -266 269 -266 231 -262 257 -290 265 -266 265 -270 273 -270 231 -262 261 -262 261 -264 257 -256 293 -262 269 -266 265 -236 299 -264 269 -266 231 -264 255 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -268 231 -264 261 -260 261 -264 295 -234 299 -266 269 -266 233 -262 255 -290 265 -234 259 -292 265 -266 263 -236 301 -266 235 -300 229 -264 255 -292 263 -234 259 -260 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -294 229 -264 257 -258 291 -264 267 -272 269 -266 235 -300 229 -264 255 -292 265 -232 261 -292 231 -298 265 -234 301 -266 235 -300 231 -262 261 -264 255 -258 293 -262 269 -266 263 -236 301 -264 235 -266 259 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -262 257 -258 291 -296 241 -276 237 -268 263 -264 255 -288 265 -266 231 -298 263 -234 299 -266 233 -300 231 -264 263 -262 261 -294 229 -264 255 -292 265 -272 269 -266 235 -300 231 -262 261 -264 255 -258 293 -262 267 -268 265 -236 299 -264 267 -268 231 -264 261 -262 261 -296 229 -262 263 -262 295 -268 273 -268 233 -262 261 -262 257 -258 291 -230 293 -264 269 -274 269 -266 233 -300 231 -262 261 -264 255 -258 293 -262 267 -236 259 -292 231 -298 265 -234 299 -266 235 -300 231 -262 257 -258 293 -262 267 -274 267 -234 293 -264 267 -266 233 -262 263 -262 257 -258 289 -262 269 -268 231 -296 263 -268 271 -268 233 -296 229 -264 261 -294 229 -262 257 -324 239 -274 239 -266 259 -258 257 -294 267 -266 267 -270 271 -270 231 -264 259 -296 229 -294 231 -262 257 -290 231 -300 263 -270 267 -266 235 -298 231 -264 257 -290 265 -272 267 -268 235 -300 229 -296 229 -262 257 -258 261 -294 267 -234 261 -292 231 -298 263 -236 301 -264 235 -300 231 -296 229 -296 229 -294 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -268 291 -300 241 -272 235 -266 261 -262 259 -262 257 -292 231 -300 -RAW_Data: 265 -236 299 -266 233 -300 231 -296 229 -264 261 -294 229 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -294 229 -296 263 -268 273 -268 231 -296 231 -262 257 -290 231 -298 231 -298 229 -296 261 -268 265 -266 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -268 267 -266 233 -300 231 -296 231 -262 293 -268 271 -270 233 -296 229 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -264 255 -258 259 -296 267 -272 275 -270 231 -264 257 -290 229 -300 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 259 -262 295 -264 233 -300 265 -270 271 -268 233 -264 257 -290 229 -266 261 -258 261 -294 233 -302 265 -234 299 -266 233 -300 231 -298 227 -296 229 -296 229 -294 263 -270 271 -268 231 -296 231 -294 229 -262 257 -258 259 -296 267 -274 267 -266 235 -300 231 -294 229 -296 229 -296 227 -262 257 -260 259 -296 233 -302 265 -234 299 -264 235 -300 231 -264 257 -258 259 -294 233 -300 267 -268 271 -270 231 -296 229 -264 255 -258 259 -294 233 -268 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -274 275 -236 261 -292 229 -298 231 -296 263 -270 265 -264 235 -300 231 -296 229 -264 255 -258 259 -296 233 -300 265 -270 265 -264 235 -300 231 -264 257 -258 291 -270 269 -272 235 -302 231 -296 227 -296 229 -262 257 -292 231 -298 231 -296 263 -270 265 -266 233 -300 231 -264 257 -290 231 -298 231 -298 231 -294 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -296 229 -296 227 -296 229 -296 261 -270 265 -264 235 -300 231 -264 257 -258 259 -294 233 -302 231 -296 263 -266 265 -266 235 -302 231 -264 261 -260 257 -258 261 -294 233 -268 261 -292 263 -272 267 -268 235 -300 229 -264 257 -258 259 -294 235 -300 231 -296 263 -234 299 -266 233 -302 231 -296 229 -262 255 -292 231 -300 229 -298 263 -268 265 -266 235 -300 231 -262 257 -258 291 -270 271 -270 237 -302 231 -264 255 -258 257 -294 267 -268 265 -270 267 -264 235 -298 231 -296 229 -296 229 -294 229 -296 229 -296 263 -234 299 -266 233 -268 259 -292 231 -300 231 -296 263 -268 265 -264 235 -302 231 -296 227 -296 229 -262 257 -258 259 -296 267 -272 269 -266 233 -300 231 -296 231 -294 229 -296 229 -296 227 -296 229 -296 263 -268 265 -266 233 -300 231 -296 231 -262 257 -290 231 -300 -RAW_Data: 263 -270 273 -268 231 -296 231 -294 229 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -296 231 -294 229 -262 257 -258 259 -296 233 -302 265 -234 299 -232 263 -296 233 -300 231 -264 257 -290 231 -298 231 -298 229 -296 269 -272 235 -298 231 -262 255 -290 231 -266 259 -260 261 -294 267 -274 267 -268 233 -300 231 -296 229 -294 229 -296 229 -262 257 -292 265 -272 267 -234 263 -262 261 -296 233 -300 265 -268 265 -266 235 -302 231 -264 261 -260 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 269 -272 275 -270 231 -296 229 -262 255 -292 231 -300 231 -296 229 -296 263 -234 299 -264 235 -302 231 -262 257 -258 291 -230 293 -264 235 -300 265 -270 271 -268 231 -264 261 -262 257 -290 231 -266 261 -292 231 -298 231 -298 263 -234 299 -266 233 -268 259 -260 259 -294 233 -300 265 -236 299 -264 235 -300 233 -262 261 -262 257 -290 233 -298 231 -298 263 -234 299 -232 295 -264 233 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -264 235 -300 231 -296 229 -264 261 -294 263 -234 301 -264 235 -300 231 -296 229 -262 257 -258 291 -262 269 -234 261 -290 265 -272 267 -266 235 -300 231 -262 257 -290 265 -232 293 -266 269 -268 271 -268 233 -262 255 -258 291 -264 267 -268 231 -264 261 -262 295 -268 273 -268 231 -264 261 -262 257 -258 291 -230 295 -264 267 -266 265 -236 299 -264 267 -268 231 -262 263 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -268 231 -264 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -262 257 -258 291 -264 269 -266 265 -236 299 -232 293 -264 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 233 -262 295 -268 271 -270 231 -264 261 -260 263 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -258 293 -262 269 -240 301 -266 267 -234 263 -264 255 -290 265 -240 303 -266 269 -232 265 -262 261 -264 261 -262 255 -292 265 -266 265 -270 271 -270 231 -262 261 -262 257 -258 293 -262 267 -268 231 -264 293 -268 273 -270 231 -264 261 -262 261 -264 295 -234 299 -266 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -230 295 -262 269 -266 263 -236 301 -264 267 -268 231 -262 263 -262 263 -262 261 -262 295 -268 273 -270 233 -262 261 -260 257 -290 265 -234 261 -292 265 -264 265 -270 273 -268 233 -262 261 -262 -RAW_Data: 255 -290 267 -234 259 -260 289 -262 267 -268 265 -236 301 -264 267 -268 229 -264 261 -264 261 -262 257 -290 265 -266 273 -274 235 -266 261 -262 255 -258 291 -262 269 -234 261 -290 265 -264 265 -236 301 -266 267 -234 263 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -236 261 -258 289 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -262 255 -292 265 -266 233 -264 261 -262 295 -268 273 -270 231 -264 261 -260 257 -290 265 -274 275 -270 233 -262 261 -262 261 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 263 -262 261 -262 261 -262 263 -262 263 -262 295 -236 299 -266 269 -232 263 -264 261 -264 255 -290 265 -232 261 -260 291 -262 267 -268 265 -270 271 -268 233 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -264 269 -266 231 -264 257 -256 291 -264 269 -234 259 -292 263 -266 265 -236 299 -266 269 -232 265 -262 257 -256 291 -264 269 -266 233 -262 295 -268 271 -270 231 -264 255 -290 265 -266 265 -236 299 -266 269 -234 263 -262 263 -262 257 -288 267 -266 231 -264 261 -262 295 -270 273 -268 231 -264 261 -260 289 -264 271 -270 271 -236 263 -262 261 -260 257 -258 291 -264 267 -236 259 -292 265 -266 263 -270 273 -268 231 -264 261 -262 255 -290 267 -266 233 -264 293 -234 299 -266 269 -234 265 -262 261 -262 261 -264 255 -258 293 -262 269 -268 263 -270 271 -268 233 -262 257 -290 265 -232 259 -292 265 -272 269 -266 235 -300 231 -262 261 -262 257 -292 265 -266 231 -264 261 -264 295 -234 299 -266 269 -266 231 -262 261 -294 229 -264 263 -262 257 -290 265 -272 269 -266 269 -268 229 -264 255 -290 265 -234 261 -292 263 -266 265 -234 301 -266 269 -266 229 -264 261 -262 257 -290 265 -266 233 -264 261 -262 295 -270 271 -270 231 -262 261 -262 295 -236 299 -266 269 -266 231 -262 257 -290 265 -266 231 -264 263 -262 295 -268 271 -270 231 -264 261 -262 261 -262 263 -262 257 -290 265 -268 265 -270 271 -270 231 -264 255 -288 265 -266 267 -236 299 -232 293 -264 267 -266 231 -264 263 -262 255 -290 265 -234 261 -292 265 -266 263 -270 273 -268 233 -262 261 -262 255 -290 267 -266 233 -262 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 263 -262 293 -236 299 -266 269 -234 263 -262 257 -288 265 -234 261 -292 265 -266 263 -270 273 -268 233 -262 261 -262 261 -264 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -264 -RAW_Data: 267 -240 303 -266 267 -268 229 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -262 257 -290 263 -234 259 -292 265 -266 265 -236 299 -234 295 -262 267 -264 231 -264 295 -270 271 -270 231 -264 255 -290 265 -232 259 -260 293 -262 267 -268 265 -268 273 -268 233 -262 259 -262 257 -258 293 -304 243 -274 235 -264 261 -262 255 -290 265 -232 259 -294 265 -266 263 -236 301 -266 269 -232 265 -262 259 -262 257 -290 265 -268 231 -266 295 -234 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -234 263 -264 261 -262 261 -264 295 -234 299 -266 269 -266 231 -262 261 -264 257 -256 293 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -262 261 -264 255 -258 293 -262 269 -268 231 -264 293 -268 271 -270 233 -262 261 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -264 261 -262 257 -258 291 -230 295 -262 269 -274 273 -238 263 -262 261 -262 255 -258 293 -304 243 -274 235 -266 255 -256 289 -230 295 -264 267 -268 265 -270 271 -268 233 -262 261 -262 255 -292 265 -266 231 -264 263 -262 295 -268 273 -268 233 -262 257 -288 265 -266 267 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 257 -290 263 -274 269 -266 269 -234 263 -262 257 -290 265 -232 259 -292 267 -266 263 -270 273 -268 231 -264 261 -262 255 -292 265 -266 233 -264 257 -288 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -262 261 -264 263 -262 295 -268 273 -268 233 -262 257 -256 291 -262 269 -234 261 -290 265 -266 265 -236 299 -264 269 -234 263 -264 255 -292 263 -266 231 -266 263 -262 293 -268 273 -236 261 -292 263 -266 263 -236 299 -266 269 -266 231 -262 263 -262 261 -264 255 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 259 -262 257 -290 265 -274 275 -238 265 -262 261 -262 255 -258 293 -262 269 -234 259 -292 263 -266 265 -236 299 -266 269 -234 263 -262 261 -262 257 -258 291 -262 269 -274 269 -266 267 -234 263 -264 261 -262 261 -264 255 -258 293 -262 269 -268 231 -262 295 -234 299 -266 267 -268 231 -262 261 -262 295 -270 271 -270 231 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 257 -290 265 -268 -RAW_Data: 231 -264 295 -234 299 -266 269 -266 231 -262 261 -264 261 -264 261 -262 295 -236 299 -264 269 -234 263 -264 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -268 233 -262 261 -262 257 -258 291 -296 241 -278 239 -266 263 -260 257 -288 265 -268 231 -264 295 -268 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -264 295 -268 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -270 273 -268 233 -262 261 -262 261 -264 261 -264 255 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -264 255 -258 291 -262 269 -240 303 -266 267 -234 263 -264 261 -260 257 -290 267 -266 233 -264 293 -234 299 -266 269 -266 231 -264 261 -262 261 -262 257 -290 265 -268 265 -236 299 -232 295 -262 267 -268 231 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -234 259 -292 263 -232 259 -260 293 -262 267 -266 265 -236 301 -264 267 -234 263 -264 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 267 -234 259 -292 265 -264 265 -270 271 -270 231 -264 255 -258 291 -262 269 -234 261 -290 265 -264 265 -270 273 -268 233 -262 261 -262 261 -264 261 -264 261 -296 267 -274 235 -266 261 -262 261 -262 261 -264 261 -264 295 -234 301 -264 269 -266 231 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 255 -290 265 -234 261 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 255 -258 291 -264 267 -240 303 -266 267 -234 263 -264 261 -262 261 -264 261 -264 261 -260 295 -236 301 -266 267 -234 263 -264 261 -262 257 -290 265 -232 261 -258 293 -262 267 -268 265 -234 299 -266 269 -234 263 -262 261 -262 263 -262 295 -236 299 -266 269 -234 263 -262 261 -262 261 -264 257 -290 265 -234 259 -292 265 -266 263 -270 273 -270 231 -264 261 -260 263 -262 261 -264 261 -296 269 -272 237 -266 261 -262 261 -260 263 -262 297 -268 273 -268 233 -262 261 -262 261 -264 255 -292 265 -264 233 -264 295 -236 299 -264 269 -234 263 -264 261 -262 295 -236 299 -264 269 -234 263 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -236 299 -266 267 -266 231 -264 255 -290 265 -232 261 -292 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -268 265 -268 273 -268 231 -264 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 255 -258 291 -264 267 -268 231 -264 293 -234 -RAW_Data: 299 -266 269 -266 231 -264 261 -262 257 -258 291 -262 269 -234 261 -290 263 -272 269 -266 269 -234 263 -264 261 -262 255 -292 265 -266 267 -236 299 -232 293 -262 267 -268 231 -264 261 -262 255 -258 293 -262 269 -268 265 -234 301 -264 267 -266 231 -264 261 -264 295 -268 273 -268 231 -264 261 -262 261 -262 257 -290 267 -266 263 -236 301 -266 267 -234 263 -264 261 -262 257 -290 265 -232 261 -292 265 -272 275 -270 233 -262 261 -262 255 -290 265 -268 265 -270 273 -268 231 -264 259 -262 257 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 265 -262 257 -256 291 -230 295 -264 267 -268 231 -262 297 -234 299 -266 267 -266 231 -264 261 -264 261 -262 295 -234 299 -266 269 -234 263 -264 261 -262 261 -262 257 -258 291 -264 267 -274 269 -266 269 -232 263 -264 261 -264 261 -262 255 -258 293 -262 269 -268 265 -234 299 -232 295 -262 269 -266 231 -262 263 -262 257 -258 291 -262 269 -274 275 -236 263 -264 259 -262 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 273 -268 231 -262 261 -262 257 -290 265 -234 261 -290 265 -274 275 -270 231 -264 255 -288 265 -266 265 -270 273 -268 231 -264 261 -262 261 -264 261 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 289 -266 267 -270 273 -234 263 -262 261 -262 255 -292 265 -268 231 -262 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -266 267 -236 299 -264 267 -268 231 -262 263 -262 261 -264 261 -262 255 -258 291 -264 269 -266 265 -270 273 -236 259 -258 291 -260 269 -266 233 -262 295 -234 299 -266 269 -234 263 -264 255 -290 265 -234 259 -292 265 -266 265 -236 299 -232 295 -262 269 -266 231 -262 263 -262 263 -262 261 -262 295 -236 299 -266 269 -266 231 -262 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 269 -266 231 -262 261 -264 257 -256 293 -262 269 -268 231 -264 293 -234 299 -264 269 -266 231 -264 263 -262 289 -264 269 -268 273 -268 231 -262 255 -290 265 -232 259 -294 265 -266 263 -236 301 -266 267 -266 231 -262 263 -262 263 -262 257 -290 263 -266 265 -270 273 -268 233 -262 257 -290 263 -266 231 -266 295 -268 273 -268 231 -264 261 -262 261 -264 255 -292 265 -232 259 -292 267 -264 265 -270 273 -268 233 -262 261 -262 261 -264 257 -290 265 -266 263 -236 301 -232 295 -262 267 -268 229 -264 263 -262 263 -262 293 -268 273 -270 233 -264 261 -260 257 -290 265 -234 259 -292 265 -272 267 -268 -RAW_Data: 267 -266 231 -264 261 -262 263 -262 257 -290 265 -266 233 -264 293 -234 299 -266 267 -268 233 -262 261 -262 257 -258 291 -230 295 -264 267 -266 271 -274 235 -266 261 -262 261 -262 257 -258 291 -264 267 -268 233 -262 295 -232 299 -266 269 -266 231 -264 255 -292 265 -232 259 -292 265 -274 267 -234 263 -262 293 -262 269 -268 263 -270 271 -268 231 -264 261 -262 257 -290 265 -234 259 -292 231 -300 263 -236 301 -266 235 -300 229 -264 255 -258 291 -230 295 -304 243 -274 233 -266 255 -256 293 -262 267 -234 259 -292 265 -266 263 -236 301 -266 233 -302 229 -264 261 -262 257 -290 265 -266 231 -296 263 -270 271 -268 231 -264 263 -262 261 -262 257 -290 265 -266 231 -264 297 -234 299 -232 295 -264 267 -266 231 -264 261 -264 261 -262 295 -234 301 -266 233 -300 231 -262 257 -258 293 -228 295 -264 267 -266 265 -236 299 -266 267 -268 229 -264 261 -262 261 -264 257 -258 291 -262 267 -268 231 -264 295 -268 271 -270 233 -262 257 -288 265 -268 231 -264 295 -268 271 -268 233 -264 261 -264 261 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 229 -264 263 -262 257 -258 291 -230 295 -304 243 -274 233 -266 255 -288 265 -266 231 -264 297 -234 299 -266 267 -266 231 -264 263 -262 255 -290 265 -266 233 -264 295 -234 299 -264 269 -266 231 -264 261 -264 261 -262 295 -234 301 -264 269 -268 229 -264 261 -262 257 -290 265 -268 231 -264 257 -290 263 -266 265 -270 273 -270 231 -264 259 -262 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -272 275 -272 231 -262 261 -262 255 -292 265 -234 259 -292 265 -272 267 -268 269 -234 263 -262 261 -262 255 -292 267 -232 259 -292 265 -266 265 -236 299 -266 267 -266 231 -264 261 -262 257 -290 265 -268 231 -264 261 -264 293 -268 273 -270 231 -264 255 -256 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -264 259 -262 261 -264 263 -262 261 -264 295 -234 301 -266 269 -232 259 -290 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 263 -262 257 -290 263 -268 265 -236 299 -266 267 -266 231 -264 255 -258 291 -230 295 -296 241 -276 239 -268 257 -290 263 -232 259 -292 267 -264 265 -236 301 -232 295 -262 267 -266 231 -264 257 -256 291 -230 295 -264 267 -268 263 -270 273 -268 231 -264 261 -262 255 -258 291 -264 267 -268 231 -264 295 -234 299 -266 269 -266 231 -262 261 -264 255 -290 267 -266 263 -236 301 -266 -RAW_Data: 267 -234 263 -264 255 -290 265 -266 233 -264 261 -264 293 -234 301 -266 267 -268 229 -264 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 257 -288 265 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 257 -258 289 -230 295 -296 241 -278 239 -268 261 -262 255 -288 265 -266 267 -236 299 -264 269 -266 231 -264 257 -256 291 -264 269 -234 259 -290 265 -272 269 -266 269 -234 263 -262 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 257 -258 291 -264 267 -234 261 -290 265 -266 265 -270 271 -270 231 -264 259 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -266 267 -234 265 -262 261 -264 255 -290 265 -234 291 -268 275 -272 235 -264 261 -262 259 -262 257 -292 265 -266 233 -262 295 -268 273 -268 233 -262 257 -288 265 -266 233 -264 257 -288 267 -266 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 257 -256 291 -264 269 -266 233 -262 295 -268 273 -236 259 -260 289 -230 293 -262 269 -268 265 -270 271 -270 231 -262 261 -262 255 -258 291 -230 295 -232 293 -264 269 -238 303 -266 267 -234 263 -264 255 -290 265 -274 267 -266 269 -234 263 -264 255 -290 265 -234 259 -292 265 -272 269 -234 295 -262 267 -266 231 -262 257 -290 265 -266 231 -266 261 -296 269 -238 263 -296 265 -232 263 -264 261 -264 295 -234 299 -232 295 -264 267 -266 231 -264 255 -258 291 -264 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 255 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -268 265 -270 273 -268 231 -264 259 -262 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 261 -262 261 -264 257 -292 263 -234 259 -292 265 -266 263 -236 301 -266 269 -232 263 -264 261 -264 261 -262 261 -262 295 -236 301 -266 267 -234 263 -262 263 -262 257 -256 291 -264 267 -268 231 -264 295 -234 299 -266 267 -268 229 -264 261 -264 261 -262 263 -262 263 -294 269 -272 237 -266 261 -262 261 -262 293 -236 301 -266 267 -266 231 -264 257 -288 265 -234 261 -292 265 -266 263 -236 301 -264 269 -234 263 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -234 261 -292 263 -266 265 -270 271 -270 231 -264 261 -262 255 -290 267 -232 259 -260 291 -262 269 -274 267 -268 267 -234 263 -262 257 -288 267 -266 233 -264 255 -290 265 -266 265 -270 -RAW_Data: 273 -268 233 -262 261 -262 255 -292 265 -266 233 -264 293 -268 273 -268 233 -262 261 -262 257 -258 291 -262 269 -268 265 -234 301 -230 295 -264 267 -266 231 -264 261 -262 255 -258 291 -264 269 -266 233 -264 293 -268 273 -270 231 -264 261 -260 263 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -258 291 -264 269 -238 303 -266 267 -234 263 -264 261 -262 261 -262 295 -270 273 -268 231 -264 261 -262 261 -262 263 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 255 -258 291 -230 295 -264 267 -268 271 -274 233 -266 257 -256 289 -262 269 -268 265 -236 299 -266 267 -234 263 -262 263 -262 261 -264 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 257 -256 291 -262 269 -268 231 -264 293 -236 299 -266 267 -234 263 -264 261 -264 255 -290 265 -274 269 -266 269 -232 265 -262 255 -290 265 -234 261 -290 265 -266 265 -236 299 -266 269 -234 263 -262 263 -262 255 -290 265 -266 233 -264 261 -264 293 -234 301 -266 267 -266 231 -264 261 -262 261 -264 255 -290 267 -266 273 -274 235 -266 261 -260 257 -256 293 -262 269 -234 259 -292 265 -264 265 -236 301 -264 269 -234 263 -262 257 -256 293 -262 269 -268 231 -264 293 -234 299 -266 269 -234 259 -290 265 -272 267 -268 267 -234 265 -262 255 -290 265 -268 231 -264 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 261 -264 289 -264 277 -274 235 -264 261 -260 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 231 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 255 -290 267 -266 233 -264 255 -290 265 -266 265 -236 299 -266 269 -232 259 -260 289 -230 293 -264 269 -266 265 -270 271 -270 231 -264 261 -260 261 -264 261 -264 261 -262 295 -236 299 -266 269 -232 265 -262 261 -264 255 -258 291 -264 267 -236 259 -290 265 -266 265 -270 271 -270 231 -264 261 -260 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 263 -262 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 295 -270 273 -268 233 -262 261 -262 255 -258 291 -264 267 -274 269 -234 293 -262 267 -268 231 -264 257 -288 265 -268 231 -264 263 -260 295 -270 271 -270 231 -264 261 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -270 273 -236 263 -264 259 -262 257 -290 265 -268 231 -264 263 -262 293 -234 301 -266 -RAW_Data: 269 -234 263 -262 263 -262 257 -256 293 -262 269 -238 303 -266 267 -234 263 -264 261 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 261 -262 259 -262 263 -264 255 -292 265 -266 233 -264 295 -234 299 -264 269 -234 263 -264 261 -262 257 -258 291 -264 267 -236 297 -270 271 -268 231 -264 255 -290 265 -232 259 -260 293 -262 269 -268 263 -236 299 -264 269 -232 265 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -236 259 -260 289 -262 267 -240 303 -266 269 -232 265 -262 255 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -234 265 -262 261 -262 257 -258 291 -296 241 -276 273 -234 263 -262 255 -256 291 -262 269 -268 231 -264 293 -234 301 -266 269 -232 265 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 257 -290 265 -234 259 -292 265 -266 231 -262 295 -236 299 -266 269 -234 263 -262 263 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 255 -290 265 -234 261 -258 291 -262 269 -240 303 -232 295 -262 267 -266 233 -262 261 -262 261 -264 295 -234 301 -264 269 -234 263 -264 261 -260 257 -292 265 -266 233 -264 257 -288 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 289 -266 269 -270 271 -234 265 -262 261 -262 259 -262 257 -292 265 -268 265 -234 301 -264 267 -268 231 -262 263 -262 257 -290 263 -268 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -234 259 -290 265 -266 231 -262 263 -262 295 -236 299 -264 269 -234 265 -262 261 -262 257 -290 265 -268 231 -264 261 -262 295 -234 301 -266 269 -232 265 -262 261 -262 257 -290 265 -266 233 -264 263 -262 293 -268 273 -270 231 -264 255 -258 289 -262 269 -268 265 -236 299 -264 269 -234 263 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -268 273 -268 233 -262 261 -262 257 -258 291 -304 243 -274 235 -264 255 -290 263 -234 259 -292 265 -266 265 -270 273 -268 233 -262 261 -262 255 -290 267 -232 261 -292 265 -266 263 -270 273 -236 265 -262 255 -290 265 -266 265 -236 301 -264 267 -268 229 -264 261 -262 257 -258 291 -230 295 -264 269 -266 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -290 267 -272 269 -266 269 -234 263 -262 255 -290 267 -234 259 -292 -RAW_Data: 265 -264 265 -270 271 -270 233 -262 261 -262 255 -290 267 -266 233 -264 255 -290 265 -266 265 -236 301 -264 269 -232 265 -262 261 -264 261 -262 263 -262 261 -262 295 -270 273 -268 233 -262 255 -258 291 -262 269 -234 261 -290 265 -234 297 -236 299 -264 269 -234 263 -262 263 -262 261 -264 261 -264 261 -262 295 -270 271 -236 261 -290 265 -264 265 -236 299 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -266 265 -236 301 -266 233 -300 231 -262 263 -262 261 -262 257 -290 265 -274 275 -270 231 -264 261 -262 255 -290 265 -234 261 -292 231 -298 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -258 291 -262 267 -274 271 -266 235 -298 231 -264 261 -262 261 -262 259 -258 289 -262 267 -268 233 -264 293 -236 299 -264 269 -266 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -270 273 -268 231 -264 261 -262 255 -258 291 -264 267 -236 259 -292 231 -298 265 -236 301 -266 267 -266 231 -262 261 -262 263 -262 261 -264 295 -234 301 -266 269 -266 229 -264 261 -264 261 -262 255 -292 265 -264 233 -264 295 -270 271 -268 233 -262 261 -262 257 -256 293 -296 241 -276 239 -268 263 -262 261 -260 261 -264 261 -264 295 -268 271 -270 233 -262 261 -262 255 -290 267 -232 259 -294 265 -266 265 -236 299 -264 267 -268 231 -262 263 -262 295 -234 299 -266 269 -266 231 -264 261 -260 257 -290 267 -232 261 -292 265 -266 263 -270 273 -268 233 -262 261 -262 261 -262 257 -260 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 255 -258 291 -264 269 -274 267 -268 267 -234 263 -262 261 -262 255 -258 293 -262 269 -268 265 -236 299 -264 269 -266 231 -262 261 -264 261 -264 255 -290 265 -268 265 -234 301 -232 295 -262 267 -266 231 -264 263 -262 255 -290 265 -268 231 -264 295 -236 299 -264 269 -232 259 -292 265 -232 259 -260 291 -262 267 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -262 255 -290 267 -266 265 -236 301 -264 267 -234 259 -290 265 -266 265 -270 271 -270 231 -264 255 -290 265 -232 259 -292 267 -264 265 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 261 -296 269 -272 237 -266 261 -262 261 -262 261 -264 261 -264 295 -234 301 -264 269 -266 231 -262 263 -262 261 -264 261 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 261 -264 261 -262 257 -290 265 -266 265 -236 301 -264 269 -232 259 -292 265 -266 -RAW_Data: 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 261 -264 295 -234 301 -266 269 -232 265 -262 259 -262 257 -292 265 -234 259 -260 291 -260 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -264 293 -234 301 -266 267 -266 231 -264 261 -264 255 -290 265 -266 231 -264 263 -264 293 -234 301 -266 269 -234 263 -262 261 -262 261 -264 261 -264 261 -296 269 -272 235 -266 259 -288 265 -264 231 -264 295 -270 273 -268 231 -264 261 -262 261 -262 257 -290 267 -266 231 -262 297 -234 301 -266 267 -234 263 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 261 -262 257 -290 267 -266 233 -262 295 -234 299 -266 267 -268 231 -264 257 -256 291 -230 295 -264 267 -238 303 -266 267 -268 229 -264 261 -262 257 -258 291 -262 269 -234 261 -290 265 -272 275 -238 265 -262 255 -258 289 -262 269 -268 231 -264 295 -236 299 -264 269 -266 231 -262 261 -262 257 -258 291 -264 267 -268 233 -262 295 -234 299 -266 269 -234 263 -262 263 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 257 -290 265 -266 265 -236 299 -232 295 -262 269 -266 231 -264 261 -264 255 -258 291 -262 269 -268 265 -234 299 -266 267 -268 229 -264 261 -264 293 -268 273 -270 231 -264 261 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -272 275 -270 233 -262 255 -256 291 -264 267 -268 265 -236 301 -264 267 -234 263 -262 263 -262 263 -262 261 -264 261 -262 295 -234 301 -266 267 -234 263 -264 255 -258 291 -230 295 -264 267 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 295 -234 301 -264 269 -234 263 -262 261 -262 263 -262 257 -290 267 -266 231 -264 295 -268 273 -268 231 -264 261 -262 257 -290 265 -232 261 -292 265 -266 263 -270 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -270 273 -270 231 -264 261 -260 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 273 -268 231 -264 259 -262 257 -290 265 -234 259 -292 265 -272 275 -238 261 -290 263 -232 259 -292 265 -266 231 -264 297 -234 299 -264 269 -266 231 -262 263 -262 257 -258 291 -230 295 -264 267 -266 265 -270 271 -270 231 -264 255 -256 291 -262 269 -234 261 -290 265 -266 265 -236 299 -266 267 -234 263 -264 261 -264 261 -262 263 -262 295 -268 273 -268 231 -264 261 -262 255 -258 291 -230 295 -264 269 -238 305 -232 295 -262 267 -266 -RAW_Data: 231 -262 263 -262 257 -290 265 -266 233 -264 261 -262 293 -236 301 -266 267 -234 263 -264 255 -258 291 -262 269 -274 269 -232 295 -262 267 -266 231 -264 263 -262 261 -262 255 -292 265 -266 233 -264 295 -268 271 -270 231 -264 261 -260 263 -262 257 -258 291 -296 241 -276 239 -268 257 -258 289 -262 267 -268 265 -270 273 -268 231 -262 261 -262 261 -264 261 -264 255 -292 265 -266 263 -236 301 -266 267 -234 263 -264 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 255 -292 265 -234 259 -258 293 -262 267 -274 269 -266 269 -232 263 -264 255 -290 265 -266 231 -264 263 -264 295 -234 299 -266 267 -234 263 -264 261 -264 255 -258 291 -262 269 -268 265 -270 271 -268 231 -264 259 -262 263 -262 257 -292 265 -264 233 -264 295 -268 273 -268 231 -264 261 -262 261 -264 255 -290 267 -266 231 -264 295 -234 301 -264 269 -266 231 -264 261 -262 255 -258 293 -262 269 -268 263 -236 299 -266 267 -234 259 -290 265 -234 259 -258 291 -264 267 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -262 261 -264 261 -264 295 -234 299 -266 267 -268 231 -262 261 -264 295 -234 299 -266 267 -268 231 -262 263 -262 257 -288 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 257 -258 291 -262 269 -268 231 -296 269 -270 237 -266 257 -288 265 -232 259 -260 293 -262 267 -268 265 -234 299 -266 267 -268 229 -264 255 -258 293 -262 269 -268 231 -264 293 -234 299 -266 267 -268 231 -264 259 -262 263 -262 257 -292 265 -264 265 -236 301 -266 269 -232 263 -264 261 -262 261 -262 295 -270 273 -270 231 -262 261 -262 257 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 257 -288 265 -266 231 -264 259 -288 265 -266 265 -236 301 -266 267 -234 263 -262 257 -290 265 -266 233 -264 295 -234 299 -266 267 -268 231 -262 261 -262 257 -290 265 -234 261 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -258 291 -262 269 -274 275 -238 259 -290 265 -264 231 -264 295 -236 299 -264 269 -234 263 -264 255 -258 291 -230 295 -264 267 -268 263 -236 299 -266 267 -266 231 -264 255 -258 293 -268 271 -272 271 -234 263 -262 261 -262 261 -264 255 -258 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 255 -292 263 -266 231 -264 263 -262 295 -270 271 -270 231 -264 259 -262 257 -258 291 -264 267 -236 259 -290 265 -266 265 -270 271 -270 231 -264 261 -260 263 -262 263 -262 295 -236 299 -264 269 -266 231 -262 -RAW_Data: 257 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -266 231 -262 263 -262 263 -262 257 -290 265 -232 259 -292 267 -266 263 -270 273 -270 231 -262 261 -262 257 -290 265 -268 231 -264 293 -234 301 -266 267 -268 229 -264 261 -262 263 -262 263 -262 261 -264 295 -234 299 -266 269 -266 231 -262 257 -256 293 -268 273 -270 271 -234 265 -262 261 -260 257 -290 267 -266 265 -236 299 -266 267 -266 231 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -234 259 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -264 261 -260 263 -262 259 -290 263 -266 231 -266 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 261 -264 263 -262 295 -268 273 -268 231 -264 261 -262 257 -290 265 -266 231 -264 263 -264 293 -268 273 -270 231 -264 261 -260 263 -262 261 -264 255 -292 265 -266 265 -236 299 -234 293 -262 267 -266 231 -264 263 -262 263 -262 261 -264 261 -296 269 -272 235 -266 263 -260 257 -288 265 -268 231 -266 255 -290 265 -266 265 -234 301 -266 269 -232 263 -264 261 -264 261 -262 263 -262 295 -234 301 -232 293 -264 267 -234 259 -292 265 -272 267 -268 267 -234 263 -264 261 -262 255 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 255 -292 263 -234 259 -292 265 -266 265 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -260 289 -262 267 -268 265 -236 301 -264 267 -234 259 -258 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -288 265 -268 265 -236 299 -266 267 -266 231 -264 263 -262 257 -288 265 -266 231 -264 259 -288 265 -266 265 -270 273 -270 231 -264 259 -262 261 -264 261 -264 295 -234 299 -266 269 -266 231 -262 261 -262 263 -262 257 -290 265 -234 259 -292 265 -274 267 -266 269 -234 263 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 261 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -262 263 -262 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -260 263 -262 295 -236 299 -266 269 -232 265 -262 261 -262 261 -264 255 -258 293 -262 269 -268 263 -236 299 -266 267 -266 231 -264 257 -288 265 -234 259 -292 265 -266 231 -264 297 -234 299 -264 269 -266 231 -262 263 -262 257 -290 265 -274 275 -270 231 -264 259 -262 -RAW_Data: 257 -256 293 -262 269 -268 231 -264 293 -268 273 -270 231 -264 261 -260 263 -262 257 -290 265 -268 231 -262 295 -236 299 -264 269 -268 229 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -290 265 -266 265 -236 301 -232 295 -262 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 265 -270 271 -234 261 -292 263 -266 263 -236 301 -266 267 -234 263 -264 261 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 263 -294 269 -272 237 -266 261 -262 261 -262 261 -264 255 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 263 -262 263 -262 261 -264 295 -268 273 -268 231 -264 261 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -266 269 -232 259 -292 263 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 261 -262 263 -262 295 -236 299 -266 269 -234 263 -262 261 -262 261 -264 263 -262 257 -290 265 -266 231 -264 295 -268 273 -268 233 -264 259 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -232 259 -260 293 -262 267 -274 269 -266 267 -234 263 -262 261 -262 261 -264 263 -294 269 -274 235 -266 257 -290 263 -234 259 -292 265 -272 269 -266 269 -232 265 -262 261 -262 257 -290 265 -266 233 -264 261 -264 293 -268 273 -270 231 -264 261 -262 259 -262 295 -236 301 -266 267 -266 231 -264 261 -264 255 -258 291 -262 269 -268 231 -264 293 -268 273 -270 231 -264 261 -260 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -232 261 -258 291 -268 273 -270 273 -234 263 -262 261 -262 255 -290 267 -266 233 -264 293 -234 299 -266 269 -268 231 -264 255 -256 291 -262 267 -268 233 -264 295 -234 301 -264 267 -266 231 -264 261 -264 261 -262 261 -262 263 -262 295 -270 271 -270 231 -264 261 -262 263 -262 257 -290 263 -266 231 -266 295 -234 301 -264 269 -234 263 -262 263 -262 257 -290 263 -234 259 -292 267 -264 265 -270 273 -268 231 -264 257 -288 265 -266 265 -270 273 -268 233 -262 261 -262 257 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 257 -290 265 -232 259 -292 265 -266 273 -272 237 -264 257 -256 291 -228 295 -264 269 -268 263 -236 299 -266 267 -234 265 -262 261 -262 261 -264 257 -290 265 -268 265 -270 271 -268 233 -262 261 -260 257 -258 291 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 -RAW_Data: 263 -262 261 -264 261 -264 293 -270 271 -270 231 -264 261 -260 261 -264 257 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 261 -264 261 -264 255 -290 265 -266 233 -264 295 -234 299 -266 267 -268 229 -264 261 -264 255 -258 291 -264 267 -240 303 -266 269 -232 263 -264 259 -262 263 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -266 231 -262 263 -262 257 -256 291 -264 269 -266 273 -272 235 -266 263 -262 259 -262 255 -292 265 -266 267 -236 299 -264 267 -268 231 -262 263 -262 257 -258 289 -230 295 -264 267 -268 265 -236 299 -264 267 -268 229 -264 257 -290 265 -232 259 -260 293 -262 269 -266 265 -234 301 -264 269 -266 231 -262 261 -264 261 -264 295 -234 299 -266 267 -268 229 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -264 255 -292 265 -264 233 -264 257 -290 265 -266 265 -270 271 -270 231 -264 259 -262 257 -290 267 -266 265 -236 299 -266 267 -266 231 -262 263 -262 263 -262 257 -290 265 -266 231 -264 261 -264 295 -234 301 -264 269 -266 231 -262 261 -264 255 -258 293 -270 271 -270 271 -234 263 -262 257 -288 267 -232 261 -260 289 -262 267 -268 265 -270 273 -268 233 -262 261 -260 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 263 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 231 -262 263 -262 263 -262 295 -234 299 -232 295 -262 267 -268 233 -262 257 -258 289 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 261 -264 261 -264 255 -290 265 -266 231 -264 297 -268 271 -270 231 -264 261 -260 263 -262 257 -292 265 -264 265 -236 301 -266 267 -234 263 -264 261 -262 261 -264 261 -264 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 255 -258 291 -306 243 -274 235 -264 255 -290 263 -234 259 -292 265 -266 265 -270 273 -268 231 -264 261 -262 255 -290 265 -234 261 -292 265 -264 265 -270 273 -268 233 -262 257 -288 265 -266 265 -236 301 -266 267 -266 231 -262 263 -262 257 -256 291 -230 295 -264 269 -266 265 -234 299 -266 267 -268 231 -262 263 -262 261 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -264 261 -262 261 -264 255 -290 265 -274 269 -266 269 -234 263 -262 257 -290 265 -232 261 -292 265 -264 265 -270 273 -270 231 -262 261 -262 255 -290 267 -266 233 -264 255 -290 265 -266 265 -236 299 -266 269 -234 263 -262 261 -264 261 -264 261 -262 263 -262 295 -268 271 -270 233 -262 -RAW_Data: 261 -262 255 -292 265 -266 233 -264 261 -262 295 -234 301 -264 269 -266 231 -264 261 -262 261 -262 257 -292 265 -264 265 -270 273 -236 261 -290 265 -264 265 -236 299 -266 269 -234 263 -262 263 -262 261 -264 261 -262 261 -262 295 -236 299 -266 269 -266 231 -262 261 -264 261 -264 261 -296 267 -274 235 -266 263 -260 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -266 231 -264 261 -260 257 -290 267 -232 259 -292 267 -272 269 -266 267 -266 231 -262 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 269 -232 259 -258 289 -230 293 -264 269 -266 265 -270 273 -268 231 -264 261 -262 261 -262 263 -262 261 -264 261 -264 293 -236 299 -266 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 269 -232 265 -262 261 -264 261 -264 261 -262 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 257 -290 263 -268 231 -264 295 -268 271 -270 233 -264 261 -260 257 -290 265 -266 271 -274 237 -266 263 -260 261 -262 261 -264 261 -264 295 -268 271 -270 231 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -260 295 -236 301 -266 267 -234 263 -264 261 -260 257 -290 267 -232 261 -292 265 -266 263 -236 301 -266 267 -234 263 -264 255 -258 291 -230 295 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 261 -262 257 -290 265 -274 269 -266 269 -232 265 -262 257 -256 293 -228 295 -264 269 -266 263 -236 299 -266 269 -234 263 -262 261 -262 261 -264 257 -292 265 -264 265 -236 301 -232 295 -262 267 -266 231 -264 261 -264 255 -290 265 -266 233 -264 295 -234 299 -266 269 -232 259 -292 265 -232 259 -260 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -234 259 -290 265 -266 265 -268 273 -270 231 -264 255 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 259 -262 263 -262 263 -262 261 -296 269 -272 237 -266 261 -262 255 -290 265 -268 231 -264 295 -236 297 -266 269 -266 231 -262 263 -262 261 -264 261 -262 261 -264 295 -234 301 -266 267 -234 263 -264 261 -262 257 -290 265 -266 233 -264 261 -262 295 -234 299 -266 269 -266 231 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 261 -264 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 257 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -266 267 -234 -RAW_Data: 263 -262 257 -290 265 -266 265 -236 301 -264 267 -266 231 -264 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 261 -264 255 -290 265 -234 261 -292 265 -272 275 -270 231 -262 261 -262 255 -292 265 -266 265 -236 301 -264 267 -268 231 -262 261 -264 261 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -268 231 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -268 231 -264 263 -260 295 -236 299 -266 269 -234 263 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 267 -268 231 -262 263 -262 261 -262 261 -264 289 -266 275 -272 235 -264 261 -262 261 -262 257 -290 265 -266 265 -236 301 -266 267 -266 231 -262 263 -262 257 -256 291 -230 295 -264 269 -266 265 -236 299 -264 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 231 -266 295 -234 299 -232 295 -264 267 -234 259 -292 263 -266 265 -236 299 -266 267 -268 229 -264 257 -288 265 -234 259 -260 291 -262 269 -266 267 -268 273 -268 231 -264 261 -260 257 -290 265 -274 275 -270 233 -262 261 -262 255 -290 265 -234 261 -292 265 -264 265 -236 301 -232 295 -262 267 -266 231 -264 257 -256 291 -230 295 -296 241 -276 239 -268 257 -256 291 -262 267 -268 231 -264 295 -234 299 -266 267 -268 231 -262 261 -262 263 -262 257 -258 293 -262 267 -266 265 -270 273 -268 233 -262 261 -262 255 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 257 -290 265 -266 263 -270 273 -270 233 -262 261 -262 255 -292 265 -232 259 -292 267 -264 231 -264 295 -236 299 -264 269 -266 231 -264 261 -264 255 -290 265 -266 231 -264 263 -264 295 -268 271 -270 231 -264 261 -260 263 -262 257 -290 265 -274 275 -270 233 -262 261 -262 255 -290 267 -266 231 -262 257 -290 267 -266 263 -270 273 -268 233 -262 261 -262 255 -292 265 -234 261 -292 265 -264 271 -240 265 -260 293 -228 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 263 -262 259 -262 263 -262 263 -262 295 -236 301 -264 269 -266 231 -262 257 -290 263 -274 269 -268 267 -234 263 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -264 261 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -262 263 -262 261 -264 255 -290 265 -266 267 -270 271 -270 231 -264 255 -290 265 -264 233 -264 295 -234 299 -266 269 -266 231 -262 263 -262 261 -262 257 -290 265 -266 231 -266 295 -234 299 -266 269 -266 231 -262 -RAW_Data: 263 -262 255 -290 265 -268 231 -264 261 -262 295 -236 299 -266 269 -232 259 -292 265 -264 231 -264 295 -236 299 -232 295 -264 267 -266 231 -264 261 -262 255 -292 265 -268 231 -264 293 -234 301 -264 269 -266 231 -264 261 -264 287 -266 269 -270 271 -234 263 -262 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -264 261 -262 257 -290 267 -272 267 -268 269 -232 259 -258 291 -260 269 -268 265 -236 299 -264 269 -266 231 -262 263 -262 263 -262 257 -256 291 -264 267 -274 269 -266 269 -232 265 -262 255 -290 265 -266 233 -264 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 255 -258 293 -262 269 -268 263 -270 273 -268 233 -262 261 -262 255 -258 293 -262 269 -268 263 -236 299 -264 269 -266 231 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -234 299 -266 269 -266 231 -264 261 -262 257 -258 289 -264 267 -268 265 -270 273 -268 231 -262 261 -262 261 -264 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 257 -290 265 -272 269 -234 295 -228 293 -264 267 -268 263 -270 273 -268 233 -262 261 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -272 269 -266 269 -234 263 -262 263 -262 261 -264 255 -290 265 -266 265 -236 301 -266 267 -234 263 -262 257 -258 291 -264 267 -268 231 -262 295 -268 273 -270 231 -264 261 -260 263 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -262 261 -260 263 -262 297 -234 299 -266 267 -268 233 -262 255 -258 291 -230 295 -264 267 -266 265 -234 301 -266 267 -234 263 -264 261 -262 263 -262 261 -264 255 -290 265 -268 231 -264 295 -270 271 -268 233 -262 255 -290 265 -266 231 -264 297 -234 299 -266 267 -268 229 -264 261 -264 255 -290 267 -266 231 -262 263 -262 295 -236 299 -264 269 -266 231 -264 255 -258 291 -230 295 -306 243 -274 233 -264 257 -288 265 -264 231 -266 295 -236 299 -264 269 -266 231 -264 261 -262 255 -292 265 -234 259 -292 265 -266 263 -270 273 -268 233 -262 257 -290 265 -264 265 -236 301 -266 267 -234 263 -264 261 -262 255 -258 291 -230 295 -264 269 -238 303 -266 267 -266 231 -264 261 -262 261 -264 261 -262 263 -262 295 -236 301 -264 269 -234 263 -262 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 267 -266 231 -264 263 -262 257 -290 263 -266 231 -266 295 -268 273 -268 233 -262 261 -262 255 -290 -RAW_Data: 267 -266 233 -264 261 -264 293 -234 299 -268 269 -232 265 -262 261 -260 257 -292 265 -266 231 -264 263 -262 295 -268 273 -268 233 -262 261 -262 255 -290 267 -266 233 -264 295 -234 299 -266 267 -266 231 -264 261 -264 261 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -234 259 -292 263 -266 265 -236 301 -264 269 -234 263 -262 261 -262 257 -258 291 -230 295 -264 267 -240 303 -266 267 -234 263 -262 261 -262 263 -262 295 -270 273 -268 231 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -232 293 -264 267 -268 231 -262 257 -290 265 -232 259 -292 267 -266 263 -270 273 -268 231 -264 261 -262 257 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -232 295 -230 291 -264 269 -266 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 261 -264 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 263 -262 257 -290 265 -264 233 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 261 -264 261 -262 257 -290 265 -266 265 -236 301 -264 267 -266 231 -264 257 -256 291 -264 267 -268 273 -272 235 -266 261 -262 261 -262 261 -264 295 -234 299 -266 269 -266 231 -264 255 -258 291 -264 267 -234 261 -292 263 -266 263 -270 273 -270 231 -262 261 -262 261 -264 295 -268 273 -270 231 -262 261 -262 255 -258 293 -262 269 -234 261 -290 265 -266 263 -270 273 -270 231 -264 259 -262 257 -256 293 -262 269 -268 265 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -258 291 -262 269 -274 275 -270 231 -262 261 -262 255 -290 265 -234 261 -292 265 -264 265 -270 273 -270 231 -262 257 -256 291 -264 267 -236 259 -290 265 -266 263 -236 301 -266 269 -232 265 -262 261 -262 261 -262 257 -292 265 -266 231 -264 295 -268 273 -268 231 -264 257 -288 265 -266 233 -264 263 -262 295 -234 299 -266 267 -268 231 -262 263 -262 261 -264 255 -258 291 -262 267 -268 265 -270 273 -268 233 -262 255 -290 265 -272 269 -268 267 -234 263 -264 255 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 255 -258 291 -230 295 -264 267 -266 271 -274 235 -266 263 -262 255 -256 291 -264 269 -238 303 -266 269 -234 263 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -270 271 -268 233 -262 261 -262 257 -290 265 -268 231 -264 261 -262 295 -236 299 -266 269 -234 263 -262 261 -262 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -262 255 -290 -RAW_Data: 267 -234 259 -292 265 -266 265 -234 301 -264 269 -266 231 -264 261 -260 257 -290 267 -232 261 -292 265 -266 263 -236 301 -266 267 -234 263 -264 255 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -234 263 -264 257 -290 265 -264 233 -264 263 -294 269 -272 237 -266 257 -288 265 -264 265 -236 301 -266 267 -234 263 -264 255 -290 265 -266 231 -264 263 -262 295 -234 299 -268 269 -232 265 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 271 -270 231 -264 259 -262 255 -258 293 -262 269 -268 265 -234 299 -266 269 -232 265 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 255 -258 291 -262 269 -268 231 -264 293 -268 271 -270 233 -264 261 -260 261 -264 257 -290 265 -266 265 -236 299 -232 295 -264 267 -266 231 -264 261 -264 255 -256 291 -264 269 -266 267 -236 299 -232 293 -262 267 -234 261 -258 291 -262 267 -268 265 -234 301 -232 293 -264 267 -266 231 -264 257 -258 289 -262 269 -234 261 -292 263 -266 265 -236 299 -266 267 -234 265 -262 261 -264 261 -262 295 -234 301 -266 267 -234 263 -264 261 -262 263 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 257 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 263 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 261 -262 261 -262 263 -262 257 -258 291 -264 267 -268 265 -234 301 -264 267 -266 231 -264 263 -262 257 -288 265 -274 269 -266 267 -268 229 -264 261 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -266 231 -264 261 -264 255 -290 265 -234 259 -292 265 -266 263 -236 301 -232 295 -262 267 -268 231 -262 263 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -232 293 -264 267 -268 231 -262 263 -262 261 -264 261 -262 295 -234 301 -264 269 -266 231 -264 261 -262 263 -262 263 -262 257 -290 263 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 257 -256 291 -270 273 -272 269 -234 263 -262 261 -262 257 -290 265 -268 231 -264 263 -262 293 -234 301 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -268 231 -264 295 -234 299 -266 269 -232 265 -262 261 -262 257 -258 291 -230 295 -264 267 -266 231 -264 -RAW_Data: 263 -262 293 -236 299 -266 269 -234 263 -262 261 -262 263 -262 257 -290 265 -274 269 -266 269 -234 263 -262 261 -262 255 -292 265 -266 231 -264 257 -290 265 -266 265 -236 301 -264 269 -266 231 -262 263 -262 261 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -262 263 -262 257 -258 291 -230 295 -264 267 -266 265 -270 271 -270 231 -262 261 -262 257 -290 265 -234 259 -292 265 -266 231 -264 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 263 -262 257 -290 265 -268 265 -234 301 -264 269 -266 231 -262 261 -264 261 -264 255 -290 265 -266 265 -236 301 -266 267 -234 263 -264 255 -258 291 -230 295 -264 267 -268 229 -264 295 -234 301 -264 269 -234 263 -264 261 -262 257 -290 265 -232 261 -258 291 -304 243 -274 235 -264 261 -262 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -264 259 -262 261 -264 263 -262 257 -290 265 -266 263 -236 301 -234 293 -264 267 -266 231 -262 263 -262 257 -256 291 -230 295 -264 269 -268 263 -236 299 -264 269 -234 263 -264 261 -260 257 -290 267 -232 261 -292 265 -266 271 -274 235 -266 261 -262 261 -262 255 -290 267 -266 233 -264 295 -234 299 -266 267 -266 231 -264 263 -262 261 -262 255 -290 267 -232 261 -260 291 -294 239 -278 273 -234 263 -262 259 -262 257 -290 265 -266 233 -264 295 -234 299 -232 295 -264 267 -266 231 -264 261 -264 255 -290 265 -232 261 -292 265 -266 265 -236 299 -264 269 -234 263 -264 261 -262 261 -262 257 -292 265 -266 231 -264 257 -290 265 -266 265 -236 299 -266 269 -266 229 -264 261 -264 261 -262 257 -290 265 -232 293 -266 277 -274 233 -264 261 -260 261 -264 255 -258 293 -262 269 -268 265 -234 299 -232 295 -262 267 -266 233 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -258 291 -264 267 -240 301 -266 269 -266 231 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 257 -290 265 -266 231 -266 295 -234 299 -266 267 -268 231 -262 263 -262 257 -256 291 -264 269 -266 233 -296 267 -272 235 -266 263 -262 255 -256 293 -262 269 -268 231 -262 295 -234 301 -264 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 257 -290 263 -266 265 -236 301 -266 267 -234 263 -264 259 -262 263 -262 257 -258 291 -264 267 -268 265 -268 273 -268 233 -262 261 -262 261 -262 257 -290 267 -266 265 -236 299 -266 267 -266 -RAW_Data: 231 -264 261 -262 255 -258 293 -230 293 -264 269 -268 265 -234 299 -232 295 -262 267 -268 231 -262 261 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -234 259 -290 265 -264 231 -266 295 -236 299 -264 269 -266 231 -262 261 -264 261 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -264 261 -262 295 -268 273 -270 231 -264 261 -260 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 261 -264 293 -268 273 -270 231 -264 261 -260 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -262 261 -262 257 -292 265 -266 265 -236 299 -266 269 -232 265 -262 257 -288 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -232 259 -294 265 -266 265 -234 301 -266 269 -232 263 -264 255 -290 265 -268 231 -296 269 -272 235 -266 263 -260 261 -262 261 -264 263 -262 261 -264 295 -234 301 -266 269 -232 265 -262 255 -256 293 -262 269 -268 231 -264 293 -268 273 -268 233 -264 255 -290 263 -266 265 -236 301 -266 267 -268 229 -264 261 -262 261 -264 261 -262 257 -290 267 -266 265 -270 273 -236 263 -264 259 -262 257 -258 291 -304 243 -274 235 -266 261 -260 261 -262 261 -264 257 -290 265 -266 265 -236 299 -266 269 -234 263 -262 257 -256 293 -262 269 -268 265 -268 271 -270 231 -262 255 -290 265 -268 231 -264 295 -236 299 -264 269 -266 231 -264 261 -260 263 -262 263 -262 261 -264 295 -234 301 -266 269 -232 265 -262 259 -262 257 -258 291 -264 267 -268 267 -234 301 -264 267 -268 231 -262 261 -262 257 -258 291 -270 271 -272 269 -234 263 -264 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 233 -262 255 -256 293 -262 269 -234 259 -292 265 -266 263 -236 301 -264 269 -234 263 -264 261 -262 261 -262 263 -262 261 -264 295 -268 273 -236 259 -292 265 -232 259 -292 265 -266 263 -236 301 -266 267 -234 263 -262 263 -262 263 -262 261 -262 261 -264 295 -268 273 -268 233 -262 261 -264 293 -234 301 -266 269 -234 263 -262 261 -262 257 -290 265 -268 231 -264 257 -290 265 -264 265 -270 273 -270 231 -264 261 -260 263 -262 257 -256 293 -296 241 -276 239 -268 257 -290 263 -232 259 -260 291 -262 267 -268 265 -270 273 -268 231 -262 261 -262 257 -256 293 -262 269 -268 -RAW_Data: 265 -236 299 -264 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -266 267 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 255 -292 265 -234 261 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -262 261 -264 295 -234 301 -266 269 -232 263 -264 261 -262 261 -262 257 -290 267 -266 233 -262 295 -268 273 -268 233 -262 257 -290 263 -266 231 -264 263 -262 295 -236 299 -232 295 -264 267 -266 231 -262 295 -236 299 -234 295 -262 267 -266 231 -264 261 -264 255 -256 293 -262 269 -268 265 -268 273 -268 231 -264 261 -260 257 -290 265 -274 269 -268 267 -234 263 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 257 -258 291 -262 269 -266 265 -270 271 -236 261 -290 263 -266 231 -264 261 -264 295 -234 301 -264 269 -234 263 -264 259 -262 263 -262 263 -262 263 -262 295 -236 299 -264 269 -266 231 -264 255 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -266 231 -264 261 -264 255 -290 265 -274 269 -266 269 -232 265 -262 255 -258 291 -262 269 -234 261 -290 265 -266 265 -236 299 -266 267 -234 263 -264 261 -262 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 255 -258 291 -264 267 -268 231 -296 269 -272 235 -266 263 -260 257 -256 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 263 -262 263 -262 295 -270 271 -270 231 -264 261 -260 263 -262 295 -236 299 -266 269 -232 265 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 255 -290 265 -234 291 -268 277 -272 233 -264 261 -262 259 -262 263 -262 257 -292 265 -266 231 -264 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 257 -290 265 -274 269 -266 269 -234 263 -262 261 -262 261 -264 257 -290 265 -266 231 -264 297 -234 299 -232 295 -230 293 -230 295 -264 267 -268 263 -270 271 -270 231 -264 259 -262 257 -258 291 -230 295 -264 267 -240 303 -232 295 -262 267 -266 231 -264 263 -262 255 -290 265 -268 231 -264 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 261 -264 295 -268 273 -268 233 -262 261 -262 261 -264 257 -290 265 -232 259 -294 265 -266 263 -270 273 -268 233 -262 261 -262 261 -264 257 -290 265 -266 271 -240 265 -294 231 -298 231 -264 257 -290 265 -266 265 -236 299 -266 267 -266 -RAW_Data: 231 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 293 -234 301 -266 269 -266 229 -264 255 -258 293 -230 293 -264 269 -266 265 -268 271 -270 233 -262 261 -262 255 -290 267 -266 233 -296 269 -272 235 -266 261 -262 261 -264 255 -290 267 -266 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 261 -264 295 -234 299 -266 267 -234 263 -264 255 -290 265 -266 231 -264 259 -290 265 -266 263 -236 301 -266 269 -232 263 -264 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 261 -264 255 -258 293 -262 269 -268 231 -264 293 -268 271 -270 233 -264 261 -260 261 -264 261 -264 261 -262 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 263 -262 257 -256 293 -304 243 -274 235 -264 261 -262 255 -290 265 -266 233 -264 255 -290 265 -266 265 -268 273 -270 233 -262 261 -262 255 -258 291 -264 267 -268 265 -270 271 -236 259 -292 263 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 257 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 295 -236 299 -266 267 -268 229 -264 261 -264 261 -262 257 -290 265 -232 261 -292 265 -266 265 -234 301 -266 269 -232 263 -264 261 -262 261 -264 255 -290 267 -266 233 -264 293 -268 273 -268 233 -262 261 -262 261 -264 295 -268 273 -268 233 -262 261 -262 257 -290 265 -268 231 -264 257 -290 263 -266 265 -270 273 -270 231 -262 261 -262 257 -290 265 -266 233 -264 261 -294 269 -272 237 -266 257 -256 291 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 255 -292 265 -266 267 -234 301 -264 267 -268 231 -262 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 257 -290 267 -264 233 -264 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -292 263 -274 275 -236 261 -290 265 -264 231 -266 261 -262 295 -234 301 -232 295 -262 267 -268 231 -262 257 -290 265 -234 259 -292 265 -272 269 -266 269 -232 265 -262 257 -288 265 -234 259 -292 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -274 269 -266 269 -232 265 -262 261 -264 261 -262 255 -290 267 -266 233 -264 293 -236 299 -264 269 -268 231 -262 261 -262 257 -290 265 -234 261 -290 265 -266 231 -264 295 -270 271 -268 233 -262 261 -262 261 -264 257 -290 265 -266 271 -274 235 -268 255 -258 289 -262 -RAW_Data: 267 -268 231 -264 297 -234 299 -266 267 -234 263 -262 263 -262 263 -262 257 -290 265 -266 263 -236 301 -266 269 -232 265 -262 261 -264 293 -234 301 -266 269 -232 265 -262 261 -264 261 -264 261 -262 257 -290 265 -266 265 -270 273 -268 231 -264 261 -260 261 -264 261 -264 295 -270 271 -270 231 -264 259 -262 257 -258 291 -262 269 -268 265 -234 299 -266 269 -266 229 -264 261 -264 255 -258 291 -264 269 -266 233 -262 295 -234 299 -264 269 -268 231 -262 263 -262 261 -262 255 -258 293 -262 269 -268 265 -234 301 -230 295 -264 267 -266 231 -264 261 -264 293 -234 301 -266 269 -232 263 -264 261 -262 261 -264 261 -264 255 -290 267 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -268 231 -264 261 -262 295 -268 273 -270 231 -264 261 -260 263 -262 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 257 -290 265 -266 231 -264 295 -268 273 -268 233 -262 261 -262 257 -290 265 -268 231 -298 269 -270 235 -266 257 -290 263 -266 231 -266 261 -264 293 -234 301 -266 267 -266 231 -264 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 269 -232 265 -262 261 -264 295 -234 299 -266 269 -234 263 -262 261 -264 261 -264 255 -258 291 -264 267 -274 269 -266 269 -232 263 -264 261 -262 255 -258 293 -296 239 -278 239 -268 263 -260 259 -262 261 -264 257 -292 265 -264 265 -236 301 -266 267 -234 263 -264 261 -262 255 -258 291 -264 269 -266 265 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 261 -258 291 -262 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -266 231 -264 263 -262 295 -268 273 -268 233 -264 259 -262 257 -258 291 -262 269 -274 275 -270 231 -264 259 -262 255 -290 267 -266 233 -264 257 -290 263 -266 265 -270 273 -268 231 -264 261 -262 255 -290 265 -234 261 -292 265 -272 275 -270 233 -262 255 -290 265 -264 233 -264 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 267 -266 231 -264 257 -290 263 -274 267 -234 295 -264 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -234 263 -264 255 -290 265 -266 233 -264 263 -262 293 -234 301 -264 269 -268 231 -264 255 -258 289 -262 269 -234 261 -292 265 -272 275 -236 261 -290 263 -264 233 -264 261 -264 295 -234 299 -266 269 -232 -RAW_Data: 265 -262 261 -264 261 -262 255 -260 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -262 261 -262 261 -264 261 -262 263 -262 295 -270 271 -270 231 -264 261 -260 257 -290 265 -274 269 -266 269 -234 263 -262 261 -262 261 -264 257 -258 291 -260 269 -268 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 261 -262 295 -236 301 -266 267 -234 263 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 263 -262 295 -270 271 -270 231 -264 255 -256 293 -262 269 -268 231 -262 295 -234 301 -264 269 -234 263 -264 261 -262 255 -258 291 -264 269 -274 267 -266 269 -234 263 -262 261 -262 261 -264 263 -262 257 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 261 -262 295 -268 273 -270 231 -264 261 -260 257 -290 265 -234 261 -290 265 -266 231 -264 295 -270 271 -268 233 -262 261 -262 257 -290 265 -268 231 -264 257 -288 265 -266 267 -270 271 -270 231 -262 261 -262 261 -264 263 -262 261 -296 269 -272 235 -268 255 -290 263 -266 231 -264 263 -262 295 -236 299 -264 269 -234 263 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -262 257 -256 293 -230 295 -262 269 -268 231 -264 293 -268 273 -268 233 -262 261 -262 257 -290 265 -268 231 -264 257 -290 265 -264 265 -236 301 -266 267 -234 263 -262 263 -262 263 -262 261 -264 295 -268 271 -270 231 -264 261 -260 263 -262 257 -290 265 -234 259 -292 265 -266 265 -270 273 -268 231 -264 261 -260 257 -290 265 -268 231 -264 263 -260 295 -236 301 -264 269 -234 263 -262 261 -262 261 -264 263 -262 295 -268 273 -268 233 -264 259 -262 261 -264 261 -264 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 257 -290 263 -268 231 -264 295 -236 299 -232 293 -230 295 -262 267 -268 231 -264 295 -234 299 -266 269 -232 265 -262 261 -262 261 -264 261 -264 257 -290 265 -272 269 -266 269 -234 263 -264 255 -258 291 -270 271 -272 269 -234 263 -264 259 -262 257 -258 293 -262 269 -268 263 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -268 273 -234 261 -258 289 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 261 -264 261 -264 261 -262 295 -270 271 -270 231 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 261 -262 255 -258 291 -264 269 -274 273 -238 265 -262 -RAW_Data: 261 -260 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 267 -268 229 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -236 299 -266 267 -266 231 -264 261 -262 257 -258 291 -264 267 -268 265 -270 271 -234 261 -290 265 -264 231 -264 263 -262 295 -236 299 -264 269 -268 231 -262 263 -262 261 -262 261 -264 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 263 -262 263 -262 257 -290 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -262 257 -290 265 -274 275 -270 231 -264 261 -260 257 -258 291 -262 269 -268 231 -264 293 -268 273 -270 231 -264 255 -258 289 -262 269 -268 231 -264 295 -270 271 -270 231 -262 261 -262 261 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 257 -290 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -264 269 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -270 271 -270 231 -264 261 -260 263 -262 261 -264 295 -268 273 -268 233 -262 263 -262 261 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -266 231 -264 261 -264 261 -262 257 -290 265 -272 277 -236 261 -290 263 -266 231 -264 261 -264 293 -234 301 -266 269 -234 263 -264 259 -262 257 -258 291 -230 295 -264 269 -238 303 -266 267 -234 263 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 255 -290 267 -234 259 -292 265 -266 263 -270 273 -268 231 -264 255 -290 265 -266 231 -264 259 -290 265 -266 265 -268 273 -270 231 -262 261 -262 261 -264 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -260 257 -290 265 -268 231 -264 263 -262 293 -234 301 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -266 231 -264 297 -234 299 -232 295 -264 267 -266 231 -264 255 -258 291 -264 267 -268 265 -236 299 -230 295 -264 267 -234 259 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -290 265 -268 263 -236 301 -264 267 -266 231 -264 257 -258 291 -268 273 -270 271 -234 263 -262 261 -262 263 -262 257 -290 265 -266 231 -266 295 -234 299 -266 269 -266 231 -262 257 -258 291 -262 269 -268 231 -296 267 -238 265 -294 265 -266 231 -264 259 -262 295 -270 273 -268 233 -262 261 -262 257 -290 265 -234 259 -292 265 -272 269 -266 269 -234 263 -262 261 -262 263 -262 257 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -266 -RAW_Data: 231 -264 261 -262 261 -264 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -236 299 -266 269 -266 231 -262 261 -262 263 -262 263 -262 257 -290 265 -266 265 -270 273 -268 231 -264 261 -262 261 -262 257 -258 291 -270 273 -270 271 -234 263 -262 261 -262 261 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -290 265 -274 267 -268 267 -234 263 -264 255 -290 265 -266 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -258 291 -264 267 -240 303 -266 267 -234 263 -264 261 -260 295 -236 301 -266 267 -234 263 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -268 273 -268 233 -262 257 -256 291 -264 267 -268 231 -264 295 -268 271 -236 261 -290 265 -264 231 -264 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -266 231 -264 295 -236 301 -266 267 -234 263 -262 257 -256 291 -230 295 -264 269 -266 265 -268 273 -268 233 -262 261 -262 261 -262 257 -290 267 -266 265 -268 273 -268 233 -262 261 -262 263 -262 257 -290 265 -266 231 -264 295 -270 271 -270 231 -264 261 -260 257 -290 267 -266 231 -264 257 -290 265 -266 263 -236 301 -266 269 -232 265 -262 257 -290 263 -234 259 -292 265 -274 275 -270 231 -264 255 -256 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 261 -262 257 -290 265 -234 261 -292 265 -238 301 -268 267 -234 263 -262 261 -262 295 -236 301 -266 267 -234 263 -262 263 -262 257 -290 263 -268 231 -264 257 -290 265 -272 269 -266 269 -234 263 -262 257 -290 265 -266 231 -296 271 -272 235 -266 263 -260 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 261 -262 295 -236 299 -234 295 -262 267 -266 231 -264 261 -264 255 -290 265 -266 267 -236 299 -232 293 -262 267 -268 231 -264 261 -264 261 -262 263 -262 263 -262 293 -236 299 -266 269 -234 263 -262 261 -262 257 -290 267 -232 259 -292 265 -266 265 -236 299 -232 295 -262 267 -268 231 -264 261 -264 255 -290 265 -266 233 -264 295 -236 299 -264 267 -266 231 -264 261 -264 255 -292 265 -272 267 -234 295 -264 265 -266 233 -264 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 267 -266 231 -264 255 -290 265 -268 231 -296 269 -272 235 -266 263 -260 263 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -234 263 -264 255 -292 265 -264 233 -264 263 -262 293 -268 273 -270 231 -264 261 -262 261 -262 -RAW_Data: 295 -236 301 -232 295 -262 267 -266 231 -264 261 -262 257 -290 265 -266 233 -264 295 -268 273 -268 231 -264 261 -262 255 -290 267 -266 233 -264 261 -262 293 -270 273 -268 233 -262 261 -262 255 -258 293 -262 269 -274 275 -270 231 -262 261 -262 261 -264 255 -258 291 -264 269 -238 303 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 261 -262 293 -236 301 -266 267 -234 263 -264 259 -262 257 -292 265 -232 261 -292 265 -264 265 -236 301 -264 269 -232 259 -292 265 -266 231 -262 297 -234 299 -266 267 -268 231 -262 261 -264 255 -290 267 -232 259 -292 267 -272 267 -268 267 -234 263 -264 255 -290 265 -272 269 -268 267 -234 263 -264 261 -260 263 -262 263 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 255 -290 265 -232 259 -292 267 -266 271 -274 235 -266 257 -256 289 -264 267 -268 265 -236 301 -264 267 -234 263 -264 261 -262 257 -290 265 -266 231 -264 295 -270 271 -270 231 -262 261 -262 257 -290 265 -274 275 -270 233 -262 261 -262 261 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -234 263 -264 261 -264 259 -262 263 -262 263 -262 263 -262 295 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 273 -268 231 -264 261 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 267 -266 231 -264 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -234 263 -262 261 -264 261 -262 255 -292 265 -234 259 -292 273 -276 237 -266 263 -262 255 -290 265 -266 233 -264 295 -234 299 -266 267 -266 231 -264 263 -262 261 -262 261 -262 263 -262 295 -270 273 -268 231 -264 261 -262 261 -262 295 -236 299 -266 269 -234 263 -262 263 -262 257 -290 263 -266 231 -266 261 -264 293 -268 273 -270 231 -264 261 -262 261 -264 261 -262 263 -262 295 -236 299 -232 295 -262 267 -268 231 -262 257 -290 265 -234 259 -260 289 -304 243 -274 235 -264 261 -262 261 -262 257 -290 265 -268 265 -234 301 -264 267 -266 231 -264 263 -262 257 -256 291 -264 267 -268 233 -262 295 -234 299 -234 293 -264 267 -266 233 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -268 231 -264 257 -288 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -264 261 -262 261 -262 295 -236 301 -266 267 -234 263 -262 263 -262 261 -262 295 -270 273 -268 233 -262 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 263 -262 -RAW_Data: 263 -262 295 -268 273 -268 233 -262 257 -288 265 -268 265 -236 299 -266 267 -266 231 -264 255 -258 291 -230 295 -264 269 -266 263 -270 271 -270 231 -264 261 -260 257 -290 265 -274 275 -270 233 -262 261 -262 261 -262 257 -290 265 -268 231 -264 261 -262 295 -236 299 -266 269 -232 265 -262 257 -256 291 -230 295 -262 269 -274 269 -266 267 -234 263 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -260 295 -236 301 -232 295 -262 267 -266 233 -262 257 -256 291 -264 269 -234 259 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -262 257 -258 291 -262 269 -240 303 -266 267 -234 263 -264 255 -288 267 -266 265 -236 301 -264 267 -266 231 -264 257 -290 265 -264 233 -264 257 -290 265 -266 263 -270 273 -270 233 -262 261 -260 263 -262 261 -264 257 -322 239 -278 237 -268 257 -290 263 -232 259 -292 265 -266 265 -236 299 -266 269 -234 263 -262 261 -262 261 -264 263 -262 261 -264 295 -268 273 -268 233 -262 261 -262 261 -264 295 -234 299 -266 269 -266 231 -264 261 -262 255 -290 267 -266 233 -264 293 -268 273 -268 233 -264 261 -262 261 -262 263 -262 261 -296 269 -272 235 -268 255 -258 289 -262 269 -268 231 -264 293 -236 299 -266 269 -266 231 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 257 -290 265 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 295 -234 299 -266 269 -234 263 -262 263 -262 257 -290 263 -234 261 -292 265 -238 301 -268 267 -234 265 -262 259 -262 263 -262 257 -292 265 -266 231 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -266 231 -296 271 -272 235 -266 263 -260 257 -288 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 257 -290 263 -234 261 -292 265 -266 263 -270 273 -268 231 -264 255 -290 265 -272 269 -268 269 -232 265 -262 261 -262 261 -264 255 -290 267 -266 233 -264 293 -268 273 -270 231 -264 261 -260 257 -258 291 -296 241 -276 239 -268 263 -262 259 -262 257 -290 265 -266 233 -264 295 -234 299 -232 295 -262 269 -266 231 -264 255 -290 265 -266 231 -264 297 -268 273 -268 231 -264 261 -262 261 -262 295 -270 273 -268 231 -264 261 -262 261 -262 257 -292 265 -266 231 -262 297 -268 273 -268 233 -262 261 -262 261 -262 263 -262 263 -262 295 -236 299 -264 269 -268 229 -264 261 -262 257 -290 265 -234 261 -290 265 -238 303 -266 269 -234 -RAW_Data: 263 -262 257 -256 291 -264 269 -266 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 261 -264 261 -262 295 -236 299 -264 269 -266 231 -264 257 -256 291 -262 269 -268 265 -270 271 -236 265 -262 261 -262 261 -262 263 -262 263 -262 295 -270 271 -270 231 -262 261 -262 257 -290 265 -268 233 -264 255 -290 263 -274 269 -266 269 -234 263 -262 261 -262 263 -262 261 -296 269 -272 235 -268 263 -262 255 -288 265 -234 259 -294 265 -266 263 -236 299 -266 267 -268 229 -264 255 -292 265 -232 259 -292 267 -264 265 -270 273 -268 233 -262 257 -288 265 -274 267 -268 267 -234 263 -264 261 -262 261 -264 255 -292 265 -264 233 -264 295 -268 273 -268 233 -262 261 -262 257 -258 291 -296 241 -276 239 -268 263 -262 255 -256 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 261 -262 261 -264 261 -264 261 -264 295 -268 271 -270 233 -264 259 -262 261 -262 295 -268 273 -270 233 -262 261 -262 255 -258 293 -262 269 -234 259 -292 265 -264 265 -270 273 -268 231 -264 261 -262 261 -262 263 -262 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 263 -262 261 -264 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 257 -258 291 -262 269 -268 231 -264 293 -234 301 -266 269 -232 265 -262 257 -290 263 -266 231 -266 257 -290 263 -266 265 -236 301 -266 267 -266 231 -264 261 -262 257 -258 291 -264 267 -240 303 -266 267 -234 259 -290 265 -264 233 -264 295 -234 299 -266 269 -232 265 -262 261 -264 255 -292 265 -232 259 -292 265 -272 269 -266 269 -234 263 -264 261 -262 295 -234 301 -264 269 -266 231 -264 261 -262 255 -292 265 -232 259 -260 293 -262 267 -268 265 -268 273 -268 231 -264 261 -262 261 -262 257 -290 265 -268 271 -274 235 -266 257 -290 263 -232 259 -292 265 -274 267 -268 269 -232 265 -262 261 -262 257 -256 293 -262 269 -268 265 -268 273 -268 231 -264 261 -262 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 261 -264 263 -262 257 -290 265 -266 263 -270 273 -270 233 -262 261 -262 255 -290 265 -234 261 -292 265 -238 301 -268 267 -234 263 -262 263 -262 261 -264 255 -258 291 -264 267 -268 265 -270 271 -268 233 -262 261 -262 261 -264 295 -234 301 -264 269 -234 263 -262 257 -258 291 -262 269 -234 261 -290 263 -266 265 -236 301 -266 267 -234 263 -262 263 -262 257 -256 291 -264 267 -268 231 -264 293 -268 273 -270 233 -262 261 -262 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 261 -264 -RAW_Data: 255 -258 291 -264 267 -268 233 -262 295 -268 271 -270 231 -264 255 -290 265 -232 259 -326 239 -276 237 -268 257 -258 289 -262 267 -268 231 -264 263 -262 293 -236 299 -266 269 -266 231 -262 261 -262 263 -262 263 -262 261 -264 295 -268 273 -268 231 -264 255 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 293 -268 273 -270 231 -264 259 -262 261 -264 261 -296 269 -272 237 -266 261 -262 255 -290 265 -234 261 -258 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 255 -290 265 -266 233 -264 295 -236 299 -264 269 -266 231 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -232 295 -264 267 -268 231 -262 257 -290 265 -272 269 -268 267 -234 263 -264 259 -262 257 -290 267 -232 261 -258 291 -262 267 -274 269 -266 269 -266 229 -264 261 -264 261 -262 257 -290 265 -234 261 -290 265 -238 303 -266 269 -232 265 -262 261 -264 261 -262 295 -236 299 -264 269 -234 263 -264 261 -262 257 -290 265 -234 259 -258 293 -262 267 -268 265 -270 271 -270 231 -262 261 -262 261 -264 257 -290 265 -266 271 -274 237 -266 261 -262 255 -290 265 -266 233 -264 293 -236 299 -264 269 -266 231 -264 261 -264 255 -258 291 -262 267 -268 265 -236 301 -264 267 -266 231 -264 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -234 261 -292 263 -272 275 -272 231 -262 261 -262 255 -258 291 -230 295 -264 269 -266 263 -236 299 -266 269 -234 263 -262 257 -256 291 -264 267 -236 291 -266 275 -274 233 -264 261 -262 255 -290 265 -266 233 -264 295 -236 299 -264 267 -266 231 -264 263 -262 257 -288 265 -234 261 -292 265 -264 265 -236 301 -232 295 -262 267 -266 231 -264 255 -258 293 -230 293 -264 267 -268 265 -268 273 -268 231 -264 261 -262 259 -264 295 -234 301 -266 269 -232 265 -262 261 -264 255 -292 265 -232 259 -292 265 -266 265 -270 271 -270 231 -264 261 -262 255 -258 291 -306 243 -274 235 -264 261 -262 259 -262 261 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -264 259 -262 263 -262 257 -290 265 -266 265 -270 273 -236 261 -290 265 -264 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 257 -290 265 -232 259 -294 265 -264 265 -236 301 -266 267 -234 263 -262 263 -262 261 -264 261 -264 261 -262 293 -236 301 -266 267 -234 263 -264 261 -262 263 -262 263 -262 295 -234 299 -266 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 233 -264 263 -262 -RAW_Data: 293 -268 273 -270 231 -264 261 -260 257 -290 265 -268 231 -264 257 -290 265 -272 269 -266 269 -234 263 -262 257 -290 263 -268 231 -264 295 -270 271 -270 231 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -262 261 -262 263 -262 261 -264 295 -268 273 -268 233 -262 261 -262 261 -262 263 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -268 231 -264 295 -268 273 -268 233 -262 261 -262 261 -264 295 -234 299 -266 269 -266 231 -264 255 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 255 -258 291 -262 267 -268 267 -236 299 -264 267 -234 259 -292 265 -264 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -266 231 -264 297 -234 299 -266 269 -232 265 -262 257 -256 291 -230 295 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 287 -266 269 -272 271 -234 263 -264 255 -290 265 -266 233 -264 255 -290 265 -266 267 -234 301 -264 267 -268 231 -262 261 -262 257 -290 265 -268 231 -264 263 -262 293 -234 301 -266 267 -266 231 -264 257 -256 291 -264 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 255 -290 265 -268 265 -236 299 -266 267 -266 231 -264 263 -262 255 -290 265 -266 233 -264 295 -268 273 -268 233 -262 261 -262 295 -234 301 -266 267 -234 263 -264 261 -262 261 -264 255 -290 265 -234 261 -292 265 -264 265 -270 273 -268 231 -264 261 -260 257 -290 265 -268 231 -298 269 -272 235 -266 261 -262 261 -262 255 -292 265 -272 271 -266 269 -232 265 -262 261 -262 261 -264 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 261 -264 263 -262 261 -264 295 -234 299 -232 295 -264 267 -266 231 -264 257 -290 263 -234 259 -292 265 -240 301 -268 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 255 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -264 295 -234 299 -266 267 -234 263 -264 257 -290 265 -232 259 -260 291 -262 269 -266 265 -270 273 -268 231 -262 261 -262 261 -264 263 -262 261 -264 261 -296 269 -238 265 -260 293 -260 267 -268 231 -264 295 -234 299 -264 269 -268 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -260 295 -236 301 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 293 -234 299 -266 269 -268 231 -262 257 -288 265 -234 259 -292 -RAW_Data: 265 -272 269 -266 269 -234 263 -264 259 -262 257 -290 267 -266 231 -264 261 -264 293 -234 301 -266 269 -234 263 -264 255 -256 293 -230 293 -264 269 -266 233 -262 295 -234 299 -264 269 -268 231 -264 255 -290 265 -266 231 -264 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 257 -290 263 -268 231 -264 263 -262 295 -268 271 -236 261 -290 265 -232 259 -292 265 -274 267 -268 267 -234 263 -264 261 -260 257 -258 291 -230 295 -264 269 -266 265 -270 271 -268 233 -262 261 -262 255 -292 265 -272 277 -270 231 -264 259 -262 257 -290 265 -234 259 -260 291 -260 269 -268 263 -270 273 -268 233 -262 261 -262 255 -258 291 -230 295 -264 269 -272 275 -238 259 -292 263 -232 259 -292 265 -272 269 -266 269 -232 265 -262 261 -262 261 -264 255 -292 265 -266 265 -236 299 -266 267 -234 263 -264 261 -264 287 -266 275 -274 233 -266 259 -262 261 -262 261 -264 257 -258 291 -262 269 -268 263 -236 301 -264 267 -266 231 -264 263 -262 257 -288 265 -266 231 -264 297 -268 271 -270 231 -264 261 -262 261 -264 255 -292 265 -264 233 -264 261 -264 295 -268 271 -236 261 -292 263 -232 259 -292 265 -272 269 -266 269 -234 263 -262 261 -262 263 -262 263 -262 263 -262 261 -264 295 -234 299 -266 269 -266 231 -262 261 -262 257 -290 265 -268 231 -264 263 -260 295 -270 271 -270 231 -264 261 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 261 -262 261 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 261 -264 257 -290 265 -266 231 -264 297 -234 299 -264 269 -266 231 -264 261 -264 261 -262 261 -264 295 -234 299 -266 269 -266 231 -262 257 -290 265 -266 263 -236 301 -266 269 -232 265 -262 261 -264 255 -290 265 -234 261 -292 265 -238 301 -268 267 -234 263 -262 261 -262 263 -262 263 -262 261 -264 295 -234 301 -264 269 -266 231 -264 259 -262 257 -290 267 -272 269 -268 267 -234 263 -264 255 -258 289 -262 269 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 231 -264 259 -262 261 -264 263 -262 261 -264 295 -268 273 -236 259 -258 291 -260 269 -234 261 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -262 257 -256 293 -262 269 -268 265 -234 299 -266 267 -268 229 -264 261 -262 295 -236 299 -234 295 -262 267 -266 233 -262 261 -262 255 -292 265 -234 259 -292 -RAW_Data: 265 -272 269 -266 269 -234 263 -262 257 -256 291 -230 295 -306 243 -274 235 -264 259 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 229 -264 257 -256 293 -228 295 -264 269 -266 263 -270 273 -268 231 -264 261 -262 261 -262 257 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 261 -262 295 -236 299 -266 269 -234 263 -262 263 -262 261 -262 257 -258 291 -264 267 -268 265 -268 273 -268 233 -262 261 -262 261 -262 257 -290 267 -266 233 -262 295 -234 299 -266 267 -268 233 -262 261 -262 255 -290 267 -272 269 -266 269 -234 263 -262 257 -256 291 -264 269 -234 259 -292 263 -266 265 -236 301 -266 267 -234 263 -262 257 -256 291 -264 269 -266 265 -270 271 -270 231 -264 255 -256 291 -262 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 255 -290 265 -274 269 -266 269 -234 263 -262 255 -290 265 -266 233 -264 263 -262 293 -234 301 -266 269 -232 263 -264 261 -264 261 -260 257 -290 267 -272 277 -236 265 -262 257 -256 291 -262 269 -266 233 -264 295 -234 299 -264 269 -266 231 -264 261 -264 255 -258 291 -262 269 -274 269 -232 295 -262 267 -266 233 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -266 231 -262 261 -264 255 -292 265 -264 233 -264 295 -270 271 -268 233 -262 261 -262 261 -264 293 -270 273 -270 231 -262 261 -262 261 -264 255 -292 265 -232 259 -292 267 -264 265 -236 299 -266 269 -234 263 -262 263 -262 261 -264 257 -288 265 -274 275 -270 233 -262 255 -256 291 -264 269 -272 269 -266 269 -234 263 -262 261 -262 261 -264 257 -290 265 -266 265 -236 299 -266 269 -234 263 -262 257 -256 291 -270 273 -272 269 -234 263 -264 259 -262 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 273 -268 231 -264 257 -256 289 -262 269 -268 231 -264 295 -268 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -264 295 -234 301 -264 269 -234 263 -262 261 -262 263 -262 257 -290 267 -266 231 -264 295 -234 299 -266 269 -234 263 -262 257 -290 265 -232 261 -292 265 -266 231 -264 295 -268 273 -268 231 -264 255 -258 291 -264 267 -240 303 -266 269 -232 263 -264 259 -262 -RAW_Data: 263 -262 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -262 255 -290 267 -266 233 -296 269 -272 235 -266 257 -256 291 -262 269 -266 233 -262 295 -234 299 -268 267 -234 263 -264 261 -262 257 -290 265 -232 261 -292 265 -266 263 -236 301 -264 269 -266 231 -262 257 -290 265 -266 265 -236 299 -266 269 -266 231 -262 261 -262 257 -258 291 -230 295 -264 269 -238 303 -264 269 -234 263 -262 263 -262 261 -264 255 -292 265 -264 233 -264 295 -268 273 -268 231 -264 261 -262 255 -292 265 -266 267 -268 273 -236 265 -262 259 -262 263 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -234 301 -264 269 -266 231 -264 261 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 257 -256 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 261 -264 261 -264 255 -290 265 -266 265 -236 301 -266 267 -234 263 -264 259 -262 295 -236 301 -266 267 -234 263 -264 261 -262 261 -262 257 -258 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 261 -264 255 -256 293 -296 239 -278 239 -268 257 -290 263 -266 229 -264 263 -262 295 -236 299 -264 269 -266 231 -264 255 -290 265 -234 261 -290 265 -274 267 -266 269 -232 265 -262 257 -290 263 -274 269 -234 295 -262 267 -268 231 -264 261 -260 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 255 -290 265 -266 231 -264 263 -262 293 -236 299 -266 269 -266 231 -264 261 -262 255 -290 267 -266 233 -264 293 -268 273 -268 233 -264 261 -262 255 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 263 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 255 -258 291 -262 267 -236 259 -292 265 -272 267 -268 267 -234 263 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 257 -256 291 -262 269 -234 261 -290 265 -274 275 -270 231 -264 259 -262 261 -264 261 -262 295 -268 273 -270 231 -264 255 -290 265 -232 259 -260 293 -262 269 -268 263 -236 299 -264 269 -266 231 -264 259 -262 263 -262 257 -290 265 -274 269 -266 269 -234 257 -290 265 -266 231 -264 295 -270 271 -268 233 -262 261 -262 261 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 257 -288 267 -266 265 -270 273 -268 231 -264 259 -262 263 -262 257 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -268 231 -262 255 -258 291 -230 295 -264 267 -268 265 -236 299 -264 267 -268 -RAW_Data: 229 -264 255 -258 291 -264 269 -234 259 -292 263 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -258 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -266 231 -262 263 -262 295 -234 301 -266 267 -234 263 -264 261 -262 257 -258 291 -262 269 -234 261 -290 265 -270 269 -266 269 -234 263 -264 255 -258 291 -262 269 -274 275 -236 265 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -264 269 -266 231 -264 255 -258 291 -230 295 -264 267 -268 263 -270 273 -268 231 -264 255 -290 265 -266 265 -236 301 -264 267 -266 231 -264 263 -262 257 -288 265 -234 261 -292 265 -264 265 -270 273 -268 231 -264 261 -260 257 -258 291 -264 267 -268 265 -236 301 -264 267 -234 263 -262 263 -262 257 -290 265 -232 293 -266 277 -272 235 -264 261 -262 255 -290 265 -266 233 -264 261 -262 293 -236 301 -266 267 -234 263 -264 259 -262 257 -258 291 -264 267 -268 233 -262 295 -268 271 -270 233 -262 261 -262 255 -292 265 -266 233 -264 293 -236 299 -264 269 -266 231 -264 261 -264 261 -262 261 -264 295 -234 299 -266 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 257 -258 291 -270 271 -272 269 -234 263 -264 261 -262 261 -262 257 -258 293 -262 269 -268 263 -236 299 -264 269 -266 231 -262 257 -290 265 -234 259 -292 265 -266 265 -270 271 -270 231 -262 261 -262 261 -264 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -264 261 -262 261 -262 297 -268 273 -268 233 -262 261 -262 261 -264 257 -290 265 -264 233 -264 295 -236 299 -264 269 -234 263 -264 261 -260 263 -262 297 -234 301 -264 269 -234 263 -262 263 -262 257 -288 265 -268 231 -264 263 -262 293 -236 299 -266 269 -234 263 -264 261 -260 263 -262 257 -258 291 -264 267 -268 265 -270 271 -268 233 -262 261 -262 261 -264 261 -262 295 -270 273 -268 231 -264 261 -262 255 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 257 -290 265 -268 231 -264 297 -268 271 -268 233 -262 261 -262 261 -264 261 -264 295 -234 299 -266 269 -266 229 -264 261 -264 255 -258 291 -230 295 -264 269 -238 303 -266 267 -234 263 -262 263 -262 263 -262 293 -236 299 -266 269 -234 263 -262 263 -262 261 -264 255 -258 291 -264 267 -268 265 -270 271 -268 233 -262 259 -262 -RAW_Data: 257 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 257 -292 265 -266 265 -270 271 -270 231 -264 255 -256 293 -262 269 -268 231 -262 295 -234 299 -266 267 -268 233 -262 261 -262 261 -262 263 -262 261 -264 261 -264 295 -234 301 -264 269 -266 231 -262 263 -262 257 -256 293 -262 269 -268 263 -236 299 -264 269 -232 259 -292 265 -266 231 -262 297 -234 301 -264 269 -234 263 -262 263 -262 257 -290 265 -232 259 -292 265 -266 265 -236 299 -266 269 -266 231 -262 263 -262 261 -262 295 -270 271 -270 233 -262 261 -262 261 -262 263 -262 257 -290 265 -266 231 -264 297 -268 273 -268 231 -264 255 -290 265 -232 259 -292 267 -266 271 -240 265 -292 265 -266 231 -262 261 -262 263 -262 297 -234 299 -266 269 -266 231 -262 261 -262 263 -262 263 -262 261 -264 295 -268 273 -268 231 -264 261 -262 255 -258 293 -262 269 -268 231 -264 293 -234 301 -266 267 -234 263 -264 261 -264 259 -262 295 -270 273 -270 231 -264 259 -262 261 -264 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 257 -256 293 -296 239 -278 239 -268 261 -262 255 -258 289 -230 295 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -270 273 -268 231 -262 261 -262 261 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 259 -262 295 -270 273 -270 231 -264 259 -262 257 -256 293 -262 269 -268 265 -234 301 -264 267 -268 231 -262 257 -256 293 -262 269 -268 231 -262 295 -268 271 -270 231 -264 261 -262 261 -264 295 -234 301 -264 269 -266 231 -262 261 -264 255 -258 293 -230 293 -264 269 -266 263 -270 273 -268 231 -264 261 -262 255 -290 267 -266 233 -264 261 -262 295 -268 271 -270 233 -264 261 -260 257 -290 265 -268 265 -270 271 -270 231 -262 261 -260 257 -290 267 -266 233 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 261 -262 263 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 257 -290 265 -266 231 -264 295 -236 299 -232 295 -262 267 -234 261 -290 265 -266 263 -236 301 -266 267 -234 263 -264 261 -260 257 -258 291 -230 295 -264 269 -266 265 -270 271 -236 265 -262 261 -262 255 -292 265 -272 269 -268 267 -234 263 -264 255 -290 265 -266 233 -264 257 -290 263 -266 -RAW_Data: 265 -270 273 -268 231 -264 255 -258 289 -230 295 -264 269 -268 271 -240 263 -262 291 -260 267 -234 261 -290 265 -266 265 -234 301 -264 269 -234 263 -264 261 -262 263 -262 261 -262 261 -264 295 -270 271 -270 231 -264 261 -260 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 269 -234 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -260 257 -290 265 -268 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -292 265 -266 231 -264 295 -236 299 -232 295 -262 269 -266 231 -262 263 -262 257 -256 291 -270 271 -272 271 -236 263 -262 261 -260 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -264 259 -262 257 -258 293 -262 269 -268 265 -268 273 -268 231 -264 255 -290 263 -268 265 -236 299 -264 269 -266 231 -264 261 -264 255 -290 265 -266 233 -264 261 -262 293 -268 273 -270 233 -264 261 -260 263 -262 295 -268 273 -268 233 -262 261 -262 257 -290 265 -266 233 -264 263 -262 293 -268 273 -270 231 -264 261 -262 261 -262 263 -262 261 -264 295 -268 273 -268 233 -262 257 -256 291 -262 269 -268 265 -236 299 -264 269 -232 265 -262 261 -264 255 -258 293 -262 269 -268 263 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -262 295 -268 273 -268 231 -264 261 -262 261 -264 261 -264 255 -290 267 -266 231 -262 297 -234 301 -266 267 -234 263 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -262 261 -262 261 -264 257 -292 263 -274 275 -270 233 -262 255 -256 291 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 257 -256 291 -230 295 -264 269 -238 303 -268 267 -234 263 -262 255 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -260 257 -290 265 -268 231 -264 295 -236 299 -232 293 -264 267 -266 231 -264 257 -258 289 -264 267 -234 261 -292 265 -266 263 -236 299 -266 269 -232 265 -262 261 -264 261 -262 261 -264 295 -236 299 -264 269 -266 231 -264 261 -262 255 -292 265 -266 233 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 261 -264 261 -264 255 -290 265 -266 267 -236 299 -266 267 -234 263 -262 263 -262 257 -258 291 -296 241 -276 239 -268 263 -260 255 -288 265 -234 261 -292 265 -266 263 -236 301 -266 267 -234 263 -264 255 -258 291 -230 295 -262 269 -268 265 -268 273 -268 231 -264 255 -290 265 -264 265 -236 301 -266 -RAW_Data: 269 -232 263 -264 261 -264 261 -262 257 -288 267 -266 233 -264 293 -268 273 -268 233 -262 261 -262 257 -256 293 -304 243 -274 235 -264 261 -262 261 -262 257 -290 265 -266 265 -236 299 -232 295 -262 267 -268 231 -264 263 -262 261 -262 261 -264 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 295 -234 299 -266 267 -268 231 -262 261 -264 255 -292 263 -266 231 -266 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 261 -262 263 -262 295 -270 271 -270 231 -264 261 -260 261 -264 257 -290 265 -274 267 -268 267 -234 263 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -232 295 -262 269 -266 231 -264 255 -290 265 -266 231 -264 257 -290 265 -266 265 -236 301 -264 267 -268 229 -264 263 -262 261 -262 257 -290 265 -274 269 -266 269 -232 259 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 257 -290 265 -232 261 -292 265 -238 303 -266 269 -234 263 -262 261 -262 295 -236 301 -264 269 -234 263 -262 261 -262 257 -290 265 -234 261 -258 291 -262 267 -268 265 -270 273 -268 231 -264 259 -262 261 -264 257 -290 265 -266 271 -274 237 -266 261 -262 255 -290 265 -266 265 -236 299 -266 269 -266 229 -264 261 -264 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 261 -262 257 -256 293 -262 269 -268 231 -264 293 -234 301 -232 295 -262 267 -268 231 -264 255 -290 265 -274 267 -268 269 -232 265 -262 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 263 -262 257 -290 265 -232 259 -292 267 -272 267 -268 267 -234 263 -264 261 -262 255 -292 265 -274 267 -268 267 -234 263 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -268 273 -268 231 -264 255 -258 291 -262 269 -268 231 -264 293 -234 299 -232 295 -264 269 -266 231 -264 295 -234 299 -266 267 -266 231 -264 261 -262 257 -290 265 -266 231 -264 263 -262 295 -270 271 -270 231 -264 261 -260 257 -290 265 -274 275 -270 233 -262 261 -262 255 -290 265 -234 261 -292 265 -264 265 -270 273 -268 231 -264 261 -260 257 -258 291 -264 267 -268 265 -270 271 -270 231 -262 255 -258 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 255 -292 265 -266 233 -264 293 -236 299 -264 269 -266 231 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -264 255 -258 291 -264 267 -234 261 -290 265 -264 -RAW_Data: 265 -236 301 -266 267 -234 263 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 263 -262 257 -290 265 -266 231 -298 269 -272 235 -266 261 -262 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -288 267 -266 265 -236 301 -264 267 -266 231 -264 261 -262 261 -264 295 -236 299 -266 267 -234 263 -262 263 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -268 233 -264 259 -262 257 -258 291 -296 241 -276 239 -268 263 -262 259 -262 255 -292 265 -266 233 -264 261 -262 295 -234 301 -266 267 -234 263 -264 255 -290 265 -232 261 -292 265 -266 265 -236 299 -266 267 -266 231 -264 261 -264 255 -290 265 -266 233 -264 261 -264 293 -234 301 -266 269 -234 257 -258 291 -262 269 -266 265 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -264 255 -258 293 -262 269 -238 303 -266 267 -266 231 -262 263 -262 257 -290 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -264 255 -290 265 -266 265 -270 273 -270 231 -262 257 -288 265 -232 261 -292 265 -266 271 -240 265 -294 265 -232 259 -258 291 -260 269 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -264 257 -290 265 -266 263 -236 301 -266 269 -232 265 -262 261 -264 295 -234 299 -266 267 -234 265 -262 261 -264 255 -290 265 -266 233 -264 295 -268 273 -268 233 -262 261 -262 255 -290 265 -268 231 -298 269 -272 235 -266 257 -256 291 -228 295 -264 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 261 -264 255 -258 291 -264 269 -266 265 -236 299 -264 269 -266 231 -262 261 -262 263 -262 263 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -264 261 -260 257 -290 267 -232 261 -292 265 -238 303 -232 295 -262 267 -266 231 -264 263 -262 261 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 293 -268 273 -268 233 -264 259 -262 295 -236 299 -266 267 -236 263 -262 261 -262 257 -258 291 -262 269 -234 261 -292 263 -266 265 -268 273 -268 233 -262 261 -262 257 -290 265 -268 231 -298 269 -272 235 -264 263 -262 259 -262 257 -292 265 -272 269 -266 269 -234 263 -262 261 -262 263 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 295 -234 299 -266 269 -234 263 -264 261 -262 257 -290 265 -232 261 -292 265 -266 263 -236 301 -264 269 -234 263 -264 -RAW_Data: 261 -262 257 -288 267 -266 265 -236 301 -264 267 -266 231 -264 263 -262 261 -262 261 -264 255 -292 265 -266 265 -270 271 -236 261 -258 289 -230 293 -264 267 -268 265 -236 299 -266 267 -266 231 -262 263 -262 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -262 263 -262 257 -258 291 -230 295 -264 267 -266 265 -234 301 -264 269 -234 259 -290 265 -264 233 -264 295 -236 299 -264 269 -266 231 -262 261 -262 257 -258 291 -230 295 -264 269 -266 265 -270 271 -268 231 -264 261 -262 255 -290 267 -272 275 -272 231 -264 259 -262 261 -264 255 -290 267 -266 233 -262 261 -262 295 -236 301 -266 267 -234 263 -262 257 -258 289 -230 295 -264 267 -274 277 -236 261 -288 263 -266 231 -264 263 -262 295 -234 301 -264 269 -266 231 -264 261 -262 255 -292 265 -266 233 -264 293 -236 299 -264 269 -266 231 -264 257 -256 291 -230 295 -264 267 -266 265 -236 299 -266 267 -266 231 -264 261 -264 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 257 -256 293 -262 269 -268 263 -236 299 -264 269 -266 231 -262 257 -258 291 -230 295 -264 267 -268 271 -274 235 -264 263 -260 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -264 261 -264 261 -260 263 -262 263 -262 295 -270 271 -270 231 -264 261 -262 261 -262 295 -236 299 -266 267 -266 231 -264 261 -264 261 -262 255 -260 291 -262 269 -268 265 -268 271 -270 231 -264 261 -260 261 -264 263 -294 269 -272 237 -266 261 -262 261 -262 255 -260 291 -262 269 -240 303 -266 267 -234 263 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -266 267 -266 231 -264 261 -264 293 -234 301 -232 295 -262 267 -268 231 -264 261 -262 257 -258 289 -262 269 -268 231 -264 295 -234 299 -266 269 -266 231 -262 257 -290 265 -266 231 -264 263 -262 295 -236 299 -264 269 -234 263 -264 261 -262 257 -290 265 -232 261 -292 265 -266 265 -234 301 -264 269 -234 263 -262 257 -258 291 -264 267 -268 233 -262 293 -234 299 -266 269 -268 231 -262 261 -262 255 -258 291 -264 269 -266 233 -264 293 -234 299 -266 269 -266 233 -262 261 -262 257 -290 265 -266 267 -236 299 -264 269 -266 231 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 295 -234 299 -268 269 -232 265 -262 261 -262 -RAW_Data: 255 -258 293 -230 295 -262 269 -240 301 -266 269 -234 263 -262 255 -258 291 -262 269 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -262 261 -262 295 -236 299 -266 269 -234 263 -262 255 -290 265 -266 233 -264 257 -290 265 -264 265 -236 301 -266 269 -232 263 -264 261 -262 261 -264 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 255 -290 265 -266 231 -264 263 -262 295 -270 271 -270 231 -264 255 -258 289 -264 267 -268 265 -236 301 -264 267 -234 263 -262 263 -262 257 -258 291 -264 267 -268 263 -270 273 -268 231 -264 261 -262 261 -262 295 -236 301 -264 269 -234 263 -262 261 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -270 231 -264 261 -262 255 -258 291 -230 295 -298 239 -276 239 -268 263 -262 255 -288 265 -266 231 -264 297 -268 271 -270 231 -264 261 -262 261 -264 261 -264 261 -260 295 -236 301 -266 267 -234 263 -264 255 -290 265 -274 267 -268 267 -234 265 -262 261 -262 255 -258 293 -262 269 -234 261 -290 265 -272 267 -268 269 -232 265 -262 261 -262 261 -264 261 -262 263 -262 295 -236 299 -266 267 -266 231 -264 257 -288 265 -268 231 -264 257 -290 265 -272 275 -270 233 -262 255 -258 289 -264 269 -266 265 -236 299 -266 267 -266 231 -264 261 -264 255 -290 265 -232 261 -292 265 -266 263 -270 273 -270 231 -264 261 -262 255 -290 267 -232 259 -292 265 -266 231 -264 297 -268 271 -270 231 -264 261 -260 257 -290 265 -274 269 -266 269 -232 263 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -236 299 -266 267 -266 231 -262 263 -262 261 -264 295 -234 301 -264 269 -234 263 -264 261 -260 263 -262 257 -292 265 -266 231 -262 297 -234 301 -266 269 -232 263 -264 261 -262 261 -262 257 -290 267 -272 275 -238 265 -262 261 -260 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 257 -258 291 -230 295 -264 267 -238 303 -266 269 -234 263 -262 261 -262 261 -264 257 -258 291 -262 269 -240 303 -232 295 -262 267 -266 231 -264 261 -264 261 -262 295 -268 273 -270 231 -264 261 -260 261 -264 255 -292 265 -266 233 -264 261 -262 295 -268 273 -270 231 -264 261 -260 257 -290 265 -234 259 -258 293 -262 267 -268 265 -270 273 -268 231 -262 261 -262 261 -264 257 -290 265 -266 271 -274 237 -266 257 -256 289 -262 267 -268 233 -264 295 -234 299 -266 267 -266 231 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 261 -262 295 -236 299 -266 -RAW_Data: 269 -232 265 -262 261 -262 257 -290 265 -234 259 -292 265 -266 265 -236 301 -266 267 -234 263 -262 255 -258 291 -230 295 -304 243 -276 235 -264 255 -288 265 -232 259 -292 265 -266 265 -236 301 -266 267 -234 263 -262 263 -262 261 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 261 -264 295 -236 299 -264 269 -234 263 -264 261 -262 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -272 277 -270 231 -264 259 -262 255 -292 265 -266 233 -264 295 -234 299 -264 269 -268 231 -262 261 -262 257 -258 291 -230 295 -264 267 -266 265 -236 301 -264 269 -234 263 -262 261 -262 257 -290 265 -234 261 -292 263 -266 265 -270 271 -270 231 -264 255 -290 265 -264 233 -264 295 -236 299 -264 269 -266 231 -262 263 -262 263 -262 257 -290 265 -266 263 -270 273 -270 231 -264 261 -260 263 -262 295 -270 271 -270 231 -264 259 -262 257 -258 291 -264 267 -236 259 -292 263 -266 265 -236 299 -266 267 -234 263 -264 261 -264 261 -262 257 -290 265 -272 275 -272 231 -262 257 -256 291 -264 267 -240 303 -266 267 -234 263 -264 261 -262 261 -262 257 -292 265 -264 265 -236 301 -266 267 -234 263 -264 255 -258 291 -270 273 -270 271 -234 263 -262 261 -262 257 -290 265 -234 259 -292 265 -266 231 -264 295 -268 273 -268 233 -262 257 -256 291 -262 269 -266 233 -264 295 -268 273 -268 231 -264 261 -262 255 -290 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 261 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 263 -262 257 -290 263 -266 265 -236 301 -266 267 -266 231 -264 261 -262 257 -290 265 -266 233 -264 261 -262 295 -268 273 -270 231 -264 261 -260 261 -264 295 -234 301 -266 269 -232 265 -262 261 -262 261 -264 255 -290 267 -266 233 -264 293 -268 273 -270 231 -264 261 -260 257 -290 265 -268 231 -298 269 -270 235 -266 257 -258 289 -262 269 -268 231 -264 295 -234 299 -264 269 -268 231 -262 261 -262 261 -264 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 295 -234 299 -266 269 -266 231 -262 261 -264 261 -264 261 -262 257 -290 265 -272 269 -266 269 -234 263 -264 261 -262 255 -258 291 -298 241 -278 237 -268 261 -262 255 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 255 -258 291 -228 295 -264 269 -266 265 -236 299 -266 267 -266 -RAW_Data: 231 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 257 -290 263 -266 265 -270 273 -270 231 -264 259 -262 257 -290 265 -234 259 -260 291 -260 269 -266 267 -236 299 -266 267 -266 231 -264 261 -264 259 -262 257 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 289 -264 269 -270 271 -234 263 -264 259 -262 257 -258 291 -264 269 -268 231 -262 295 -234 299 -266 267 -268 231 -264 257 -288 265 -232 259 -294 265 -272 275 -270 233 -262 255 -256 291 -262 269 -266 267 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -232 259 -292 267 -272 269 -266 269 -232 265 -262 255 -290 265 -274 269 -232 295 -264 267 -266 231 -264 261 -264 255 -290 265 -266 231 -264 297 -234 299 -266 267 -266 231 -264 255 -290 265 -268 231 -264 261 -262 295 -234 301 -266 269 -232 265 -262 261 -264 255 -290 265 -266 231 -264 297 -268 273 -268 231 -264 261 -262 261 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 255 -290 265 -234 259 -292 265 -266 265 -236 299 -266 269 -234 263 -262 255 -290 265 -268 265 -270 273 -236 263 -262 261 -262 257 -290 265 -268 231 -264 257 -290 263 -274 275 -270 233 -262 261 -262 261 -262 261 -264 293 -270 271 -270 233 -264 255 -290 263 -234 259 -258 293 -264 267 -268 265 -236 299 -264 267 -266 231 -264 261 -262 261 -264 257 -290 265 -272 269 -266 269 -234 257 -258 291 -262 269 -268 265 -268 273 -268 231 -264 261 -262 261 -262 257 -290 267 -266 263 -236 301 -266 269 -232 263 -264 255 -258 291 -262 269 -274 275 -236 265 -262 257 -288 265 -266 231 -264 263 -264 293 -236 299 -266 267 -234 263 -264 261 -264 255 -258 291 -264 267 -268 265 -268 273 -268 233 -262 261 -262 255 -258 291 -264 269 -234 259 -292 263 -274 267 -266 269 -266 231 -262 257 -258 291 -262 269 -268 265 -234 299 -266 267 -266 231 -264 261 -264 261 -262 257 -256 291 -264 267 -274 269 -266 269 -232 265 -262 261 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 261 -264 255 -290 265 -274 269 -266 269 -234 263 -262 261 -262 255 -292 265 -268 231 -264 261 -264 293 -234 299 -266 269 -266 233 -262 257 -290 263 -234 259 -292 265 -272 275 -272 231 -264 259 -262 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 261 -262 263 -262 -RAW_Data: 295 -236 299 -266 269 -234 263 -262 257 -290 265 -264 265 -270 273 -270 231 -264 257 -288 265 -232 259 -294 265 -264 265 -236 301 -266 267 -234 263 -262 263 -262 257 -290 265 -266 231 -298 269 -272 235 -266 257 -256 291 -262 267 -268 231 -264 295 -234 301 -264 269 -234 263 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 261 -262 261 -264 257 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 261 -262 295 -268 273 -268 233 -264 261 -262 255 -290 265 -234 261 -290 265 -266 231 -264 295 -268 273 -268 233 -262 261 -262 257 -258 291 -262 269 -234 261 -290 265 -266 263 -270 273 -270 231 -264 259 -262 255 -292 265 -266 233 -296 269 -272 235 -268 261 -262 259 -262 263 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -264 255 -258 291 -262 267 -268 267 -234 301 -264 267 -268 231 -262 261 -262 295 -236 299 -266 269 -234 263 -262 261 -262 257 -290 265 -268 231 -264 257 -290 265 -272 269 -266 269 -234 263 -262 257 -256 291 -264 269 -272 277 -236 265 -262 261 -262 255 -290 265 -234 261 -258 291 -262 267 -274 269 -266 269 -232 265 -262 257 -256 291 -230 295 -264 269 -266 265 -268 273 -268 233 -262 261 -262 261 -264 261 -262 257 -290 265 -268 265 -236 299 -232 293 -232 293 -264 267 -234 259 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 255 -258 291 -264 267 -268 233 -262 295 -234 299 -232 295 -264 267 -266 231 -264 261 -262 257 -256 291 -264 269 -266 265 -236 299 -266 267 -266 231 -264 255 -290 265 -266 265 -270 273 -270 231 -262 261 -262 261 -264 261 -264 255 -292 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -258 291 -296 241 -276 239 -268 263 -262 255 -256 289 -264 269 -268 263 -236 301 -230 295 -264 267 -266 231 -264 261 -262 263 -262 257 -290 265 -264 265 -270 273 -270 231 -264 255 -290 265 -266 265 -270 273 -268 231 -264 261 -262 255 -290 267 -232 261 -292 265 -272 269 -266 269 -232 263 -264 255 -290 265 -268 231 -298 267 -272 235 -266 257 -256 291 -228 295 -264 269 -268 231 -264 293 -234 299 -266 269 -266 233 -262 257 -288 265 -266 231 -264 263 -262 295 -270 271 -270 231 -264 261 -260 263 -262 257 -290 265 -268 231 -264 295 -266 273 -268 233 -264 261 -262 261 -262 263 -262 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -268 231 -264 261 -262 295 -234 301 -266 269 -232 265 -262 261 -264 261 -262 -RAW_Data: 255 -258 291 -264 269 -266 265 -270 271 -270 231 -264 261 -260 261 -264 261 -264 295 -268 273 -268 231 -264 261 -262 261 -264 261 -262 257 -290 267 -266 263 -236 301 -266 269 -232 265 -262 261 -262 261 -264 261 -262 295 -236 301 -232 295 -228 293 -264 267 -268 231 -264 293 -236 299 -264 269 -268 231 -264 261 -260 263 -262 257 -256 293 -262 269 -240 303 -266 267 -234 263 -262 261 -262 261 -264 295 -236 299 -264 269 -268 231 -264 261 -260 257 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 257 -256 289 -230 295 -264 267 -268 265 -268 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -270 273 -270 231 -264 259 -262 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 265 -268 273 -270 231 -264 261 -260 257 -290 265 -234 261 -292 265 -264 265 -236 301 -264 269 -234 257 -290 265 -266 231 -264 295 -236 299 -266 267 -266 231 -264 261 -262 257 -290 265 -234 259 -292 265 -272 269 -266 269 -232 265 -262 257 -256 291 -230 295 -306 243 -274 233 -264 257 -256 289 -262 269 -234 261 -290 265 -266 265 -236 299 -266 269 -234 263 -262 257 -256 291 -230 295 -264 267 -268 265 -270 271 -236 259 -290 265 -264 233 -264 295 -268 273 -268 231 -264 261 -262 261 -264 255 -290 267 -266 263 -236 301 -266 269 -232 265 -262 261 -262 261 -264 295 -268 273 -268 231 -264 261 -262 257 -290 265 -234 259 -292 265 -266 231 -264 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 261 -262 295 -270 271 -270 231 -264 261 -260 257 -290 265 -234 261 -292 265 -264 231 -264 295 -236 299 -232 295 -230 293 -264 267 -268 263 -236 299 -266 267 -266 231 -264 261 -262 263 -262 263 -262 257 -290 265 -264 265 -236 301 -266 269 -232 263 -264 261 -264 255 -258 291 -230 295 -264 267 -266 265 -270 271 -270 231 -264 259 -262 261 -264 263 -262 295 -270 271 -270 231 -262 261 -262 261 -264 255 -292 265 -266 233 -264 293 -236 299 -264 269 -266 231 -264 261 -264 261 -262 255 -290 267 -272 275 -238 261 -290 263 -266 231 -264 261 -264 293 -234 301 -266 267 -266 231 -264 261 -264 255 -258 291 -230 295 -264 269 -238 303 -266 267 -234 263 -262 263 -262 261 -262 295 -236 299 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -266 231 -264 297 -268 271 -270 231 -264 255 -258 291 -230 295 -262 269 -266 265 -234 301 -264 269 -234 263 -262 -RAW_Data: 263 -262 257 -256 293 -262 269 -268 265 -268 273 -268 231 -264 261 -260 257 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 263 -264 261 -264 261 -262 257 -258 291 -262 269 -268 265 -234 299 -232 295 -262 269 -266 231 -262 257 -290 265 -266 233 -264 295 -234 299 -232 295 -262 269 -232 259 -292 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 261 -264 255 -292 265 -266 265 -236 299 -266 269 -234 263 -262 257 -256 291 -270 273 -270 271 -234 263 -262 261 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -266 267 -268 231 -262 257 -258 291 -262 269 -268 231 -296 267 -238 263 -294 267 -232 259 -292 263 -266 265 -270 273 -268 231 -264 261 -262 255 -290 267 -266 233 -264 261 -262 295 -268 271 -270 233 -262 261 -262 295 -234 301 -266 269 -232 263 -264 261 -264 261 -262 257 -290 265 -266 231 -264 263 -262 295 -268 271 -270 233 -264 261 -260 261 -262 257 -292 265 -266 265 -236 299 -266 267 -266 231 -264 263 -262 255 -290 265 -234 261 -292 265 -264 265 -270 271 -236 261 -258 291 -260 269 -266 265 -236 299 -266 267 -266 231 -264 263 -262 261 -262 257 -290 265 -266 233 -264 261 -262 295 -234 301 -266 269 -232 263 -264 261 -264 255 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -262 257 -290 267 -266 263 -270 273 -268 233 -262 261 -262 257 -290 265 -268 231 -264 257 -290 265 -272 267 -268 269 -232 265 -262 261 -262 257 -258 291 -296 241 -276 239 -268 263 -262 259 -262 255 -292 265 -266 267 -236 299 -232 293 -262 267 -268 233 -262 261 -262 257 -256 293 -262 269 -268 265 -234 301 -264 267 -234 259 -292 265 -264 265 -236 301 -232 295 -262 267 -266 231 -264 257 -290 263 -234 259 -292 265 -266 265 -270 273 -268 231 -264 261 -262 255 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 255 -292 265 -234 261 -290 265 -272 275 -270 233 -262 261 -262 261 -262 257 -290 265 -268 231 -264 295 -268 271 -270 231 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -236 299 -264 267 -266 231 -264 263 -262 257 -290 265 -232 259 -292 265 -266 265 -270 271 -236 261 -292 263 -266 231 -264 261 -264 295 -234 299 -232 295 -264 267 -266 231 -264 255 -290 265 -234 259 -292 265 -272 269 -266 269 -234 263 -262 263 -262 295 -234 299 -266 269 -234 263 -262 263 -262 257 -290 263 -266 231 -266 257 -290 263 -266 265 -270 273 -270 231 -264 255 -290 265 -266 231 -296 -RAW_Data: 271 -272 235 -266 263 -260 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -264 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 263 -262 295 -236 299 -266 269 -232 265 -262 261 -260 263 -262 263 -262 257 -290 267 -266 265 -270 271 -270 231 -264 261 -260 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 261 -262 263 -262 257 -258 291 -264 267 -274 275 -236 265 -262 255 -290 265 -232 261 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 257 -258 291 -262 269 -268 265 -234 301 -264 267 -266 231 -264 263 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -234 259 -258 291 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -232 263 -264 255 -258 293 -262 269 -272 275 -270 233 -262 261 -262 255 -290 265 -266 233 -264 263 -262 293 -234 301 -266 269 -232 263 -264 261 -264 261 -262 257 -258 291 -264 267 -268 263 -270 273 -268 231 -264 255 -256 293 -262 269 -268 265 -234 299 -264 269 -266 231 -264 261 -264 255 -258 291 -262 269 -268 265 -234 299 -266 267 -268 229 -264 261 -262 257 -290 265 -268 231 -264 295 -234 299 -264 269 -268 231 -262 261 -262 261 -264 295 -270 271 -270 231 -264 255 -258 291 -262 269 -268 231 -264 261 -260 295 -270 273 -268 231 -264 261 -262 255 -290 267 -266 233 -264 255 -290 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -268 271 -274 235 -266 261 -262 261 -262 261 -264 257 -290 265 -264 265 -236 301 -266 269 -232 263 -264 261 -262 255 -258 293 -262 269 -268 265 -268 273 -268 233 -262 261 -260 263 -262 295 -236 299 -266 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -264 287 -266 275 -274 235 -264 261 -260 261 -262 257 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 263 -262 263 -262 295 -270 271 -270 231 -264 261 -260 263 -262 295 -236 299 -234 293 -262 267 -268 231 -264 261 -262 255 -292 265 -266 233 -264 293 -268 273 -270 231 -264 261 -260 263 -262 263 -262 261 -264 261 -262 295 -268 273 -270 233 -262 261 -262 261 -262 257 -290 265 -274 267 -268 269 -234 263 -262 261 -262 255 -258 291 -264 269 -266 267 -236 299 -264 269 -232 263 -264 261 -264 261 -262 261 -262 257 -290 265 -268 231 -264 297 -234 299 -264 269 -266 231 -264 -RAW_Data: 261 -262 261 -262 261 -264 295 -236 301 -232 295 -228 293 -230 295 -264 267 -268 263 -236 301 -264 269 -234 263 -262 261 -262 257 -290 265 -234 261 -290 265 -238 303 -266 269 -232 265 -262 261 -264 293 -234 301 -266 269 -234 263 -264 259 -262 257 -290 267 -232 261 -292 265 -266 229 -264 295 -270 271 -270 231 -264 255 -290 265 -232 259 -292 267 -264 273 -274 235 -266 257 -256 289 -262 269 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -266 231 -264 295 -270 271 -270 231 -262 261 -262 257 -290 265 -274 275 -270 233 -262 261 -260 263 -262 261 -264 255 -292 265 -266 231 -264 295 -236 299 -264 269 -266 231 -264 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 267 -234 263 -264 255 -258 293 -262 269 -234 259 -292 263 -266 265 -270 271 -270 231 -264 259 -262 261 -264 263 -262 261 -264 261 -264 293 -270 271 -270 231 -264 259 -262 263 -262 257 -290 265 -234 261 -292 263 -266 265 -236 299 -266 269 -234 263 -262 257 -256 291 -230 295 -264 267 -268 265 -234 299 -266 267 -266 231 -264 263 -262 293 -236 299 -266 269 -266 231 -262 263 -262 257 -288 265 -268 231 -264 263 -260 295 -270 273 -268 231 -264 261 -262 255 -290 267 -272 275 -272 231 -262 261 -262 255 -292 265 -232 259 -292 267 -264 265 -270 273 -270 231 -262 261 -262 261 -262 257 -290 265 -268 265 -270 273 -268 231 -264 255 -258 289 -262 269 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 257 -292 265 -266 265 -236 299 -266 269 -234 263 -262 261 -262 261 -264 261 -264 261 -262 295 -236 299 -266 269 -232 265 -262 261 -264 261 -262 295 -234 301 -266 269 -232 263 -264 255 -258 293 -262 269 -234 259 -292 263 -266 265 -236 299 -266 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -268 231 -264 261 -260 261 -264 261 -264 295 -268 271 -270 231 -264 255 -290 265 -266 231 -264 263 -262 295 -234 301 -264 269 -234 263 -264 261 -262 257 -258 291 -262 269 -240 303 -266 267 -234 263 -262 261 -262 295 -236 301 -232 295 -262 267 -268 231 -262 261 -262 257 -290 265 -268 231 -264 263 -262 293 -268 273 -270 231 -264 261 -260 257 -258 291 -296 241 -278 239 -266 263 -262 255 -290 265 -264 233 -264 295 -236 299 -264 269 -234 263 -262 263 -262 261 -262 261 -264 263 -262 295 -270 271 -270 231 -264 259 -262 261 -264 295 -234 301 -266 269 -232 265 -262 261 -262 255 -292 265 -266 -RAW_Data: 233 -264 261 -262 295 -234 301 -266 267 -234 263 -264 261 -264 255 -290 265 -232 259 -292 267 -272 269 -266 269 -232 265 -262 257 -288 265 -234 261 -292 265 -272 275 -270 231 -264 261 -260 261 -264 255 -292 265 -266 265 -236 301 -264 269 -234 263 -262 261 -262 257 -258 291 -230 295 -264 267 -268 263 -236 299 -232 295 -262 267 -268 231 -264 261 -262 261 -264 255 -290 267 -266 265 -270 273 -236 263 -264 255 -290 265 -266 265 -236 301 -264 267 -266 231 -264 261 -264 261 -262 261 -262 263 -262 295 -236 299 -266 267 -268 229 -264 261 -264 261 -262 295 -270 273 -268 231 -264 261 -260 263 -262 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 263 -262 261 -262 261 -264 261 -264 295 -268 273 -268 231 -264 255 -290 265 -268 265 -236 299 -264 269 -266 231 -262 257 -258 291 -230 295 -264 267 -266 265 -268 273 -268 233 -262 261 -262 257 -290 265 -274 275 -270 231 -264 261 -260 261 -264 255 -292 265 -266 233 -264 261 -264 293 -234 301 -266 267 -266 231 -264 255 -258 291 -230 295 -262 269 -240 303 -266 267 -234 263 -262 261 -262 257 -290 265 -234 261 -292 263 -266 265 -236 301 -264 269 -234 263 -262 261 -262 295 -236 301 -232 295 -262 267 -266 231 -264 257 -256 291 -264 269 -234 259 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -262 261 -264 255 -258 291 -264 269 -238 303 -268 267 -234 263 -262 255 -290 265 -268 265 -236 299 -264 269 -266 231 -264 257 -288 265 -266 231 -264 259 -288 265 -266 265 -270 273 -270 231 -264 255 -256 291 -228 297 -264 267 -268 271 -274 235 -266 261 -262 255 -290 265 -266 233 -264 295 -234 299 -264 269 -268 231 -262 261 -262 261 -264 261 -264 261 -264 295 -268 273 -268 231 -264 261 -262 261 -262 295 -236 299 -266 269 -266 231 -262 257 -290 265 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 255 -290 267 -266 233 -264 257 -288 265 -266 265 -236 299 -266 269 -232 265 -262 261 -264 255 -258 291 -270 273 -270 271 -234 263 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -268 231 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 257 -290 265 -266 265 -234 301 -266 269 -232 265 -262 261 -262 261 -264 261 -264 261 -264 261 -262 295 -270 271 -270 231 -264 261 -262 293 -236 301 -266 267 -234 263 -262 -RAW_Data: 261 -262 257 -290 265 -268 231 -264 257 -290 265 -266 263 -270 273 -270 231 -264 261 -262 261 -262 263 -262 261 -296 269 -272 237 -266 257 -288 265 -232 259 -292 265 -272 269 -266 269 -234 263 -264 261 -262 255 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -264 293 -234 301 -266 267 -266 231 -264 261 -262 261 -264 261 -264 255 -292 265 -264 265 -270 273 -270 231 -264 261 -260 257 -258 291 -230 295 -264 267 -240 303 -266 267 -234 263 -262 261 -262 263 -262 257 -258 291 -264 269 -266 265 -268 273 -268 233 -262 261 -262 255 -290 267 -266 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -234 263 -264 261 -264 255 -258 291 -230 293 -264 269 -240 301 -266 269 -234 263 -262 257 -290 265 -266 231 -264 295 -234 299 -266 269 -266 233 -262 261 -262 261 -264 261 -262 263 -262 261 -264 295 -268 273 -268 233 -262 261 -262 255 -258 293 -304 243 -274 235 -264 261 -262 255 -258 291 -262 269 -234 259 -292 265 -266 263 -236 301 -264 269 -234 263 -262 257 -258 291 -264 267 -268 263 -270 273 -234 261 -290 265 -264 231 -264 263 -262 295 -236 299 -266 267 -234 263 -264 255 -290 265 -234 261 -292 265 -264 265 -236 299 -266 267 -266 231 -264 261 -264 255 -290 265 -232 261 -292 265 -266 231 -264 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 295 -268 273 -268 233 -262 261 -262 261 -264 261 -264 255 -290 267 -266 231 -264 295 -268 273 -268 231 -264 261 -262 255 -258 291 -264 269 -234 259 -292 265 -264 265 -270 273 -268 233 -262 257 -288 265 -234 259 -324 239 -278 237 -268 261 -262 261 -262 255 -290 267 -266 231 -264 295 -236 299 -232 295 -262 267 -268 231 -262 261 -262 257 -290 265 -268 265 -270 273 -234 265 -262 257 -288 265 -268 265 -236 299 -232 295 -262 269 -266 231 -262 261 -264 255 -292 265 -266 231 -264 295 -270 271 -268 233 -262 261 -262 263 -262 295 -270 271 -270 231 -262 261 -262 263 -262 261 -264 257 -290 265 -266 233 -262 295 -236 299 -264 269 -234 263 -264 261 -262 263 -262 257 -290 265 -272 269 -268 267 -234 263 -264 259 -262 263 -262 257 -290 265 -268 231 -264 295 -234 301 -230 295 -230 295 -230 293 -264 269 -266 263 -236 299 -266 267 -234 263 -264 261 -262 263 -262 257 -290 265 -266 231 -264 297 -234 299 -266 267 -234 265 -262 261 -264 261 -262 255 -258 291 -264 269 -274 267 -266 269 -234 263 -262 -RAW_Data: 263 -262 261 -262 295 -234 301 -266 269 -232 265 -262 261 -262 261 -264 261 -264 255 -292 265 -264 265 -270 273 -270 231 -264 261 -260 261 -264 257 -290 265 -266 271 -274 237 -266 257 -256 289 -262 267 -268 233 -264 295 -234 299 -266 267 -266 231 -264 263 -262 255 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -262 295 -236 301 -266 267 -234 263 -262 257 -256 291 -230 295 -264 269 -268 265 -268 271 -268 233 -262 261 -262 257 -290 265 -266 233 -264 295 -236 299 -264 267 -266 231 -264 263 -262 261 -262 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 263 -262 261 -262 263 -262 261 -264 295 -234 301 -264 269 -234 263 -262 263 -262 257 -290 265 -232 259 -292 267 -264 265 -236 301 -266 267 -234 263 -262 263 -262 257 -290 265 -232 259 -292 267 -266 263 -270 273 -268 231 -264 261 -262 257 -256 293 -262 269 -268 265 -234 299 -266 267 -268 231 -262 261 -262 257 -290 265 -268 231 -264 295 -268 271 -270 231 -264 261 -262 293 -236 301 -266 267 -234 263 -264 261 -260 263 -262 257 -290 265 -266 231 -266 261 -264 293 -268 273 -270 231 -264 261 -262 255 -290 267 -266 233 -296 269 -272 235 -266 261 -264 259 -262 257 -290 265 -268 265 -270 271 -270 231 -262 261 -262 261 -262 257 -290 267 -266 265 -236 299 -266 267 -266 231 -264 261 -264 295 -234 299 -266 267 -234 263 -264 261 -264 255 -290 265 -266 231 -264 257 -290 265 -274 267 -268 269 -232 265 -262 261 -262 257 -258 291 -264 267 -240 303 -266 267 -234 263 -262 263 -262 257 -290 265 -232 261 -258 291 -262 267 -274 277 -236 261 -258 289 -228 295 -262 269 -268 265 -234 301 -264 267 -268 231 -262 261 -264 255 -290 265 -268 231 -264 263 -262 293 -234 301 -232 295 -264 267 -266 231 -264 255 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 255 -290 265 -264 265 -236 301 -266 269 -266 231 -262 261 -262 263 -262 257 -256 293 -262 269 -268 265 -268 273 -268 231 -264 261 -262 255 -258 291 -296 241 -278 239 -268 261 -262 261 -260 257 -290 265 -268 231 -264 295 -236 299 -232 293 -264 267 -266 233 -262 257 -256 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 261 -264 295 -234 299 -266 269 -266 231 -262 257 -258 291 -262 269 -234 261 -290 265 -272 275 -270 233 -262 261 -260 261 -262 263 -262 257 -292 265 -266 265 -270 271 -270 231 -264 259 -262 257 -290 265 -268 231 -264 257 -290 265 -272 269 -266 269 -234 -RAW_Data: 263 -262 263 -262 261 -262 255 -292 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 261 -262 257 -290 265 -234 259 -292 265 -266 231 -264 295 -270 271 -270 231 -264 259 -262 257 -290 265 -268 231 -296 269 -272 235 -266 263 -260 263 -262 257 -290 265 -232 259 -292 267 -266 263 -236 301 -266 267 -234 263 -264 255 -258 289 -264 267 -268 265 -236 301 -264 267 -266 231 -264 257 -290 263 -268 265 -236 299 -266 267 -234 263 -264 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -232 265 -262 255 -258 291 -270 273 -270 271 -234 263 -262 261 -262 263 -262 263 -262 257 -290 265 -266 233 -264 293 -268 273 -270 231 -264 261 -260 257 -290 265 -266 233 -264 295 -268 273 -268 233 -262 261 -260 257 -258 291 -230 295 -264 267 -268 265 -236 299 -232 293 -264 267 -266 231 -264 263 -262 295 -234 299 -266 269 -266 231 -262 261 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -268 229 -264 261 -264 255 -290 265 -268 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 261 -262 295 -236 299 -266 267 -234 263 -264 261 -264 261 -262 257 -290 265 -266 233 -264 295 -268 273 -268 231 -264 261 -260 257 -290 265 -268 231 -298 269 -272 235 -266 257 -288 265 -266 231 -264 261 -262 295 -236 299 -266 269 -234 263 -262 261 -262 257 -290 267 -266 233 -264 261 -262 293 -236 299 -266 269 -232 259 -292 265 -266 231 -262 297 -234 299 -266 267 -234 265 -262 261 -264 261 -262 257 -258 291 -262 269 -240 303 -264 269 -234 263 -262 257 -290 265 -266 231 -264 263 -262 295 -234 299 -266 267 -268 231 -262 263 -262 255 -290 267 -266 265 -270 273 -236 263 -264 259 -262 257 -290 265 -234 261 -292 265 -264 265 -270 271 -270 231 -264 261 -262 255 -290 267 -234 259 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -268 273 -234 261 -258 291 -262 267 -268 231 -264 295 -234 299 -264 269 -236 263 -262 261 -262 261 -264 257 -290 265 -266 265 -270 273 -268 231 -264 261 -262 255 -290 267 -272 269 -266 269 -234 263 -264 261 -260 263 -262 257 -290 267 -266 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 257 -256 293 -230 293 -304 243 -274 235 -266 255 -256 289 -264 267 -234 261 -292 263 -266 265 -236 299 -266 269 -232 265 -262 257 -256 291 -230 295 -264 269 -266 263 -270 273 -268 231 -264 261 -262 261 -262 257 -290 267 -266 231 -264 295 -234 -RAW_Data: 301 -264 269 -234 263 -262 263 -262 261 -264 295 -234 299 -266 269 -232 265 -262 261 -264 255 -258 291 -262 269 -268 231 -264 295 -234 299 -264 269 -268 231 -262 261 -262 263 -262 263 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 255 -290 267 -272 269 -266 269 -234 263 -262 261 -262 257 -290 265 -234 261 -292 265 -264 265 -236 299 -266 267 -234 265 -262 257 -256 293 -230 293 -264 269 -274 275 -236 263 -264 259 -262 255 -292 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 261 -262 261 -264 261 -264 295 -234 301 -264 269 -266 231 -264 255 -290 265 -272 269 -268 269 -232 265 -262 255 -290 265 -234 259 -292 265 -266 265 -236 299 -266 269 -232 265 -262 261 -262 257 -290 265 -268 231 -264 295 -268 271 -270 231 -264 261 -262 261 -262 257 -290 265 -266 231 -266 295 -234 299 -266 269 -234 263 -262 263 -262 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 255 -290 265 -268 231 -264 263 -262 293 -234 301 -266 267 -266 231 -264 257 -256 291 -264 269 -266 233 -262 261 -262 295 -270 271 -236 261 -290 263 -266 231 -266 261 -264 293 -234 299 -266 269 -266 231 -264 261 -262 257 -290 265 -266 233 -264 295 -234 299 -264 269 -266 231 -264 257 -290 263 -274 269 -232 295 -264 267 -266 233 -262 257 -256 291 -262 269 -234 259 -292 265 -272 269 -266 269 -234 263 -262 257 -288 265 -268 231 -298 269 -238 263 -294 265 -234 259 -290 265 -238 303 -266 269 -234 263 -262 261 -262 257 -258 291 -230 295 -264 267 -240 301 -266 269 -232 265 -262 257 -290 265 -272 269 -266 269 -234 263 -262 261 -262 257 -290 265 -268 231 -264 257 -290 263 -268 265 -270 271 -270 231 -264 255 -258 289 -230 295 -264 267 -268 265 -268 273 -268 233 -262 261 -262 255 -290 267 -234 259 -260 289 -262 267 -268 265 -270 273 -268 233 -262 261 -262 261 -264 295 -234 299 -266 269 -234 263 -262 263 -262 257 -290 263 -234 259 -292 265 -266 265 -236 301 -232 295 -262 267 -266 231 -264 261 -264 255 -258 291 -264 267 -268 265 -236 299 -264 267 -268 229 -264 263 -262 261 -262 295 -268 273 -270 233 -262 261 -262 261 -262 263 -262 257 -290 265 -266 265 -270 273 -268 233 -262 261 -262 255 -258 291 -230 295 -298 239 -278 237 -268 263 -262 255 -290 263 -266 231 -266 295 -236 299 -264 269 -234 263 -262 263 -262 263 -262 261 -262 257 -290 265 -274 269 -266 267 -234 263 -264 255 -290 265 -274 -RAW_Data: 267 -268 269 -232 265 -262 261 -262 255 -292 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 263 -264 261 -262 261 -264 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 257 -258 291 -262 269 -274 275 -236 265 -262 261 -260 257 -258 291 -264 267 -268 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 257 -290 265 -232 259 -292 265 -266 265 -236 301 -232 295 -262 267 -266 231 -264 261 -264 261 -260 263 -262 263 -296 267 -274 235 -266 263 -260 257 -290 265 -266 233 -264 295 -268 271 -270 231 -264 261 -260 263 -262 263 -262 263 -262 295 -234 299 -266 269 -266 231 -262 263 -262 295 -234 299 -266 269 -266 231 -264 261 -262 261 -262 263 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 257 -290 265 -264 273 -274 235 -266 263 -262 259 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -268 233 -262 261 -262 263 -262 257 -290 265 -268 231 -264 293 -234 301 -266 269 -232 263 -264 261 -264 255 -290 265 -266 265 -236 301 -264 269 -266 231 -264 259 -262 263 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -266 231 -264 263 -262 257 -290 265 -232 259 -292 265 -266 265 -270 273 -270 231 -262 261 -262 261 -262 263 -262 295 -270 271 -270 231 -264 261 -262 261 -262 257 -290 265 -266 233 -264 295 -234 299 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -274 275 -238 259 -290 265 -264 231 -264 263 -262 295 -236 299 -264 269 -266 231 -262 263 -262 261 -264 255 -258 293 -262 269 -238 303 -266 267 -234 263 -262 257 -290 265 -266 265 -270 273 -270 231 -262 261 -262 255 -258 293 -230 293 -264 269 -266 265 -270 271 -270 231 -264 255 -256 291 -262 269 -268 231 -296 269 -238 263 -262 291 -262 267 -234 261 -292 265 -264 265 -236 299 -266 269 -232 265 -262 261 -262 261 -264 257 -258 291 -264 267 -240 303 -266 267 -234 263 -262 261 -262 263 -262 257 -290 265 -266 231 -264 295 -236 299 -266 269 -234 263 -262 261 -262 257 -290 265 -234 261 -290 265 -266 265 -236 299 -266 269 -232 265 -262 257 -290 263 -234 261 -292 265 -264 265 -236 301 -264 269 -234 263 -262 263 -262 261 -264 255 -290 265 -266 265 -236 301 -264 269 -234 263 -262 257 -258 291 -270 271 -272 271 -234 263 -264 259 -262 261 -264 255 -290 265 -266 231 -266 295 -234 301 -264 269 -234 263 -262 257 -256 293 -262 -RAW_Data: 269 -268 231 -296 269 -238 263 -294 265 -266 231 -264 261 -262 295 -268 273 -270 231 -264 261 -260 257 -290 265 -234 261 -290 265 -274 267 -268 267 -234 263 -262 261 -262 295 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -258 291 -264 269 -266 265 -236 299 -264 269 -266 231 -262 261 -264 255 -290 265 -268 231 -264 263 -262 293 -234 301 -266 269 -234 263 -264 261 -260 263 -262 263 -262 257 -290 265 -266 265 -236 299 -266 267 -266 231 -264 263 -262 255 -290 265 -268 231 -264 263 -262 293 -268 273 -270 233 -262 261 -260 257 -290 265 -268 231 -264 263 -262 295 -234 299 -266 269 -232 265 -262 261 -264 261 -264 255 -290 265 -274 267 -268 269 -232 265 -262 255 -290 265 -268 265 -236 299 -266 267 -266 231 -264 261 -262 261 -262 257 -258 291 -264 267 -274 269 -266 269 -232 263 -264 261 -264 293 -234 301 -266 269 -232 263 -264 261 -264 261 -262 257 -290 265 -266 233 -264 293 -268 273 -268 233 -262 257 -256 291 -264 269 -266 233 -262 295 -268 271 -236 261 -292 263 -266 231 -264 261 -262 295 -236 299 -266 267 -268 231 -262 261 -262 257 -290 265 -268 231 -264 295 -268 271 -270 233 -262 261 -262 257 -256 293 -230 293 -264 269 -266 265 -234 301 -264 269 -232 265 -262 261 -264 255 -292 265 -272 275 -272 231 -264 261 -260 261 -262 261 -264 257 -290 265 -268 231 -264 295 -234 299 -266 267 -268 231 -262 257 -288 267 -266 233 -264 255 -290 265 -266 263 -236 301 -266 269 -232 265 -262 257 -290 265 -234 259 -292 265 -272 275 -238 263 -264 255 -256 291 -262 269 -268 265 -236 299 -264 269 -266 231 -262 261 -262 257 -290 265 -234 261 -292 265 -238 301 -268 269 -234 263 -262 261 -262 295 -234 301 -266 269 -232 263 -264 261 -262 255 -292 265 -266 233 -264 255 -290 265 -272 269 -268 269 -232 265 -262 255 -290 265 -266 233 -296 269 -272 235 -266 261 -262 261 -264 255 -292 265 -266 265 -236 299 -266 269 -234 263 -262 261 -262 257 -290 265 -268 231 -264 295 -268 271 -270 231 -264 261 -262 261 -262 297 -234 299 -266 267 -234 265 -262 261 -264 261 -262 257 -258 291 -264 267 -240 303 -266 267 -234 263 -264 261 -260 257 -290 265 -234 261 -292 265 -266 231 -264 295 -268 271 -270 231 -264 261 -262 261 -262 257 -290 265 -234 259 -292 265 -266 265 -234 301 -266 269 -232 265 -262 261 -264 261 -262 257 -290 263 -268 265 -270 273 -268 231 -264 261 -262 261 -262 263 -262 257 -290 265 -266 265 -236 -RAW_Data: 301 -264 269 -234 263 -262 263 -262 257 -290 265 -266 233 -296 269 -272 235 -266 257 -288 265 -266 231 -264 295 -236 299 -232 293 -264 267 -268 231 -264 255 -258 289 -264 267 -268 265 -236 301 -264 267 -266 231 -264 261 -262 261 -264 295 -234 299 -266 269 -234 265 -262 257 -288 265 -234 259 -292 267 -264 265 -270 271 -270 231 -264 255 -290 265 -232 259 -292 267 -266 271 -240 265 -292 265 -266 231 -262 263 -262 263 -262 293 -234 301 -266 269 -234 263 -262 261 -262 261 -264 257 -290 265 -268 265 -236 299 -264 267 -268 231 -262 257 -258 291 -262 267 -268 233 -264 293 -234 301 -264 269 -234 263 -262 263 -262 261 -264 293 -268 273 -270 233 -262 261 -262 261 -262 257 -290 267 -232 259 -292 265 -266 265 -236 299 -266 267 -268 231 -262 263 -262 261 -262 257 -290 265 -266 233 -264 295 -236 299 -264 267 -266 231 -264 263 -262 255 -258 291 -296 241 -278 239 -268 263 -260 255 -288 265 -268 231 -264 261 -262 295 -236 299 -266 269 -234 263 -262 263 -262 257 -256 291 -264 267 -268 265 -270 271 -236 265 -262 261 -262 261 -264 295 -234 301 -266 267 -234 263 -262 263 -262 263 -262 257 -290 263 -268 265 -236 299 -264 269 -266 231 -262 263 -262 263 -262 289 -264 277 -272 235 -264 261 -262 255 -256 293 -262 269 -268 263 -236 301 -264 267 -266 231 -264 257 -288 265 -268 231 -264 263 -262 293 -268 273 -270 231 -264 261 -262 261 -262 295 -236 299 -266 267 -268 231 -262 263 -262 261 -262 255 -292 265 -266 233 -264 263 -262 293 -268 273 -270 231 -264 261 -262 255 -290 267 -266 231 -264 261 -262 295 -236 299 -266 269 -232 265 -262 261 -264 255 -290 265 -268 233 -262 295 -234 299 -264 269 -268 231 -264 261 -262 255 -290 267 -266 233 -264 261 -262 293 -270 273 -268 233 -262 261 -262 261 -264 257 -288 265 -234 261 -292 265 -238 303 -266 267 -234 263 -262 257 -290 265 -268 231 -296 269 -272 235 -266 257 -290 263 -266 231 -266 257 -290 263 -266 265 -236 301 -266 267 -234 263 -262 257 -290 265 -266 233 -264 261 -262 293 -270 273 -270 231 -264 259 -262 261 -264 295 -236 299 -264 269 -266 231 -264 261 -262 255 -258 293 -262 269 -268 265 -268 273 -236 263 -264 261 -260 257 -258 291 -296 241 -276 239 -270 261 -262 259 -262 261 -264 255 -292 265 -266 233 -264 295 -234 299 -264 269 -234 265 -262 257 -256 291 -264 269 -266 265 -236 299 -264 269 -266 231 -264 255 -258 291 -262 269 -266 233 -262 295 -236 -RAW_Data: 299 -232 295 -262 269 -266 231 -262 263 -262 293 -236 301 -266 267 -266 231 -264 261 -260 263 -262 257 -258 291 -264 267 -268 265 -236 299 -264 267 -268 231 -262 263 -262 257 -290 265 -266 231 -264 261 -264 295 -234 299 -266 269 -232 265 -262 257 -258 291 -262 269 -240 301 -266 269 -234 263 -262 257 -290 265 -266 231 -264 263 -260 295 -236 301 -264 269 -234 263 -262 257 -258 289 -230 295 -264 269 -266 265 -236 299 -264 269 -232 259 -292 265 -266 263 -236 301 -264 269 -234 263 -264 261 -262 257 -290 265 -266 231 -264 263 -262 295 -268 273 -270 231 -262 261 -262 257 -290 265 -272 277 -238 265 -262 259 -262 257 -290 265 -268 231 -264 261 -262 295 -268 273 -270 231 -264 261 -260 263 -262 263 -262 263 -262 293 -270 273 -268 233 -262 255 -258 291 -262 267 -268 265 -236 301 -264 267 -266 231 -264 263 -262 261 -262 257 -290 265 -268 265 -270 273 -234 265 -262 261 -262 255 -258 291 -264 267 -268 265 -236 299 -266 267 -266 231 -264 261 -264 255 -258 291 -262 269 -240 301 -266 269 -234 263 -262 257 -290 265 -232 259 -260 293 -262 269 -266 263 -236 299 -266 269 -234 263 -262 263 -262 257 -290 263 -268 231 -264 263 -262 295 -234 299 -266 269 -232 265 -262 261 -264 261 -262 257 -290 265 -272 277 -270 233 -262 255 -288 265 -266 233 -264 261 -264 293 -234 301 -266 269 -232 265 -262 261 -262 257 -258 291 -264 267 -240 303 -266 269 -232 265 -262 261 -260 295 -236 301 -266 267 -234 263 -264 261 -262 263 -262 257 -290 265 -232 261 -292 265 -264 265 -270 273 -270 231 -262 261 -262 255 -258 291 -298 241 -278 237 -268 261 -262 255 -290 265 -266 231 -264 297 -234 299 -266 269 -232 265 -262 261 -262 261 -264 261 -264 261 -264 295 -268 271 -270 231 -264 261 -262 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -234 265 -262 259 -262 257 -292 265 -266 231 -264 295 -234 299 -232 295 -264 267 -268 231 -262 257 -256 293 -262 269 -234 261 -290 265 -264 265 -236 301 -264 269 -234 263 -262 261 -262 261 -264 263 -262 257 -290 265 -272 269 -266 269 -234 263 -264 255 -290 265 -268 265 -268 273 -268 233 -262 261 -262 255 -292 265 -234 259 -260 291 -260 269 -266 267 -270 271 -236 265 -262 255 -258 291 -262 269 -268 231 -296 269 -272 235 -266 257 -256 289 -264 267 -268 233 -262 295 -234 299 -266 267 -236 263 -264 261 -262 261 -262 263 -262 263 -262 295 -268 273 -268 233 -262 261 -262 261 -264 -RAW_Data: 295 -236 299 -266 267 -234 263 -264 261 -262 257 -290 265 -266 233 -264 293 -268 273 -270 231 -264 261 -262 261 -262 263 -262 261 -296 269 -272 235 -268 257 -256 291 -262 267 -268 233 -262 295 -234 299 -266 269 -234 263 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -234 263 -264 259 -262 263 -262 263 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -264 293 -268 273 -270 231 -264 261 -262 255 -258 291 -264 267 -268 233 -262 295 -236 299 -264 269 -232 265 -262 261 -264 261 -262 263 -262 257 -290 265 -266 265 -236 301 -264 269 -234 263 -262 263 -262 261 -262 261 -296 269 -274 235 -266 263 -260 261 -262 263 -262 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 257 -258 291 -262 269 -268 231 -264 293 -268 273 -270 231 -264 255 -288 265 -268 265 -236 299 -264 269 -266 231 -262 263 -262 261 -262 263 -262 263 -262 261 -264 295 -268 273 -268 233 -262 261 -262 257 -256 293 -304 243 -274 235 -264 261 -262 261 -262 261 -262 257 -290 267 -272 269 -234 295 -262 267 -266 231 -264 261 -262 257 -290 265 -266 267 -236 299 -264 269 -266 231 -262 257 -258 289 -262 269 -268 231 -264 295 -234 299 -266 269 -232 265 -262 261 -264 261 -264 261 -262 257 -290 265 -268 265 -236 299 -264 269 -266 231 -264 261 -262 261 -262 257 -290 265 -268 231 -264 295 -234 299 -266 269 -234 263 -262 263 -262 257 -256 291 -230 295 -264 267 -274 275 -238 263 -264 259 -262 255 -292 265 -266 267 -236 299 -264 269 -266 231 -262 263 -262 255 -258 291 -264 269 -266 233 -262 295 -268 271 -270 231 -264 255 -290 265 -234 259 -292 271 -278 237 -268 261 -262 255 -290 265 -266 233 -264 261 -262 293 -236 301 -266 267 -234 263 -262 261 -262 263 -262 263 -262 263 -262 295 -268 273 -268 233 -264 255 -290 265 -266 263 -236 301 -266 267 -234 263 -264 255 -292 263 -234 261 -292 265 -264 265 -270 271 -270 231 -264 261 -262 261 -264 261 -296 269 -272 235 -266 263 -260 257 -288 265 -268 231 -266 255 -290 265 -266 263 -236 301 -266 269 -232 265 -262 257 -290 265 -266 231 -264 295 -236 299 -264 269 -234 263 -262 263 -262 261 -264 255 -258 293 -262 269 -268 263 -236 299 -232 295 -262 269 -266 231 -262 257 -288 267 -266 233 -264 261 -262 295 -268 273 -268 233 -262 261 -262 255 -290 267 -266 233 -264 261 -262 261 -264 295 -234 299 -266 267 -236 263 -264 255 -290 265 -266 233 -264 261 -264 -RAW_Data: 293 -268 273 -268 233 -262 261 -262 261 -264 295 -234 301 -264 269 -266 231 -264 261 -260 263 -262 257 -290 267 -266 233 -262 295 -268 271 -270 233 -264 261 -260 257 -290 265 -268 271 -274 235 -266 261 -262 261 -262 255 -290 267 -266 265 -236 301 -264 267 -266 231 -264 261 -264 255 -258 293 -262 269 -266 265 -236 299 -264 267 -268 231 -262 263 -262 295 -234 299 -266 269 -266 231 -264 259 -262 263 -262 257 -290 267 -232 259 -292 265 -240 301 -266 269 -266 231 -264 261 -260 257 -258 291 -264 267 -268 233 -262 295 -234 299 -264 269 -234 265 -262 261 -262 263 -262 257 -258 291 -264 267 -268 265 -236 299 -264 269 -232 265 -262 257 -256 293 -262 269 -268 231 -264 293 -234 299 -266 269 -232 265 -262 261 -264 255 -292 265 -232 261 -292 265 -264 265 -270 273 -270 231 -262 261 -262 255 -258 293 -262 269 -268 271 -274 235 -264 263 -260 261 -262 257 -290 265 -268 231 -264 295 -234 299 -234 295 -262 267 -266 233 -262 257 -256 291 -264 269 -266 265 -236 299 -266 267 -234 259 -290 265 -266 231 -264 295 -236 299 -264 267 -268 231 -262 263 -262 261 -264 255 -290 265 -266 267 -236 299 -264 269 -266 231 -262 257 -258 291 -228 295 -264 269 -266 265 -236 299 -266 267 -234 263 -262 263 -262 263 -262 287 -266 269 -270 271 -234 265 -262 255 -290 265 -266 233 -264 257 -288 265 -266 267 -236 299 -264 269 -234 263 -262 263 -262 255 -290 267 -266 233 -264 261 -262 293 -234 301 -266 269 -234 263 -264 255 -256 293 -262 269 -268 231 -262 295 -268 271 -270 231 -264 257 -288 265 -266 267 -236 299 -264 269 -266 231 -264 255 -290 265 -234 259 -292 265 -266 265 -268 273 -270 233 -262 261 -260 257 -290 265 -274 269 -266 269 -232 265 -262 261 -262 261 -264 263 -262 255 -290 267 -266 265 -270 273 -236 263 -264 259 -262 263 -262 257 -290 265 -266 265 -270 273 -236 261 -290 263 -266 231 -264 261 -264 295 -234 299 -266 269 -232 265 -262 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -232 265 -264 259 -262 295 -236 301 -266 267 -234 263 -264 261 -262 257 -288 267 -232 261 -258 291 -262 267 -276 267 -268 269 -232 263 -264 259 -262 257 -258 291 -264 267 -274 269 -266 269 -232 265 -262 261 -264 261 -262 261 -262 257 -290 265 -268 265 -270 273 -234 261 -258 289 -230 293 -264 267 -268 265 -270 273 -236 263 -262 261 -262 255 -258 293 -262 269 -268 231 -264 293 -234 301 -264 269 -234 265 -262 261 -262 -RAW_Data: 261 -264 257 -256 293 -262 269 -268 265 -234 299 -266 267 -266 231 -264 257 -288 265 -266 267 -236 299 -264 269 -234 265 -262 261 -262 261 -264 255 -258 291 -264 267 -268 265 -270 271 -268 233 -262 261 -262 257 -256 293 -296 239 -278 273 -234 263 -260 255 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -266 231 -264 261 -262 257 -258 291 -264 267 -236 297 -234 299 -266 269 -232 259 -290 265 -266 265 -236 301 -264 269 -232 263 -264 255 -292 265 -266 231 -264 257 -290 265 -272 277 -236 265 -262 261 -262 261 -262 261 -264 289 -264 277 -274 235 -264 261 -260 257 -288 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 263 -262 257 -290 265 -274 267 -268 267 -234 263 -264 261 -262 261 -264 255 -258 293 -262 269 -268 265 -234 299 -266 267 -234 257 -260 291 -260 269 -274 269 -268 267 -234 263 -262 261 -262 255 -292 265 -268 231 -264 257 -288 265 -266 265 -270 273 -268 233 -262 261 -262 255 -292 265 -234 259 -292 265 -266 263 -236 301 -266 269 -232 263 -264 261 -262 255 -258 293 -262 269 -274 275 -236 265 -262 261 -262 255 -258 291 -264 267 -236 259 -292 265 -264 265 -270 271 -270 231 -264 261 -262 255 -290 267 -266 233 -296 267 -274 235 -266 261 -262 261 -262 255 -290 267 -266 233 -264 295 -234 299 -266 267 -234 263 -264 255 -292 265 -266 231 -264 295 -234 301 -264 269 -234 263 -264 259 -262 295 -236 301 -232 295 -262 267 -268 231 -264 261 -260 257 -258 291 -264 267 -268 265 -236 299 -264 269 -266 231 -262 257 -258 291 -262 269 -268 231 -264 293 -234 299 -266 269 -268 231 -262 261 -262 255 -258 293 -262 269 -274 275 -236 265 -262 255 -258 291 -230 295 -262 269 -268 231 -264 293 -234 299 -266 269 -234 265 -262 261 -262 261 -262 263 -262 257 -290 265 -266 265 -236 299 -266 269 -266 231 -262 257 -290 265 -266 233 -264 261 -262 293 -270 273 -236 261 -290 263 -234 259 -292 265 -238 303 -266 267 -234 263 -264 261 -262 257 -258 291 -262 269 -234 261 -290 263 -266 265 -236 301 -266 267 -234 263 -262 263 -262 257 -288 265 -274 269 -266 269 -234 263 -262 257 -288 265 -234 261 -292 265 -264 265 -236 301 -266 267 -234 263 -262 261 -262 257 -258 291 -296 243 -276 239 -268 261 -262 261 -260 261 -264 257 -290 265 -266 265 -236 301 -266 267 -234 263 -262 263 -262 257 -256 291 -264 269 -266 265 -270 271 -236 265 -262 255 -258 291 -262 269 -234 261 -290 265 -266 -RAW_Data: 265 -234 301 -266 269 -232 263 -264 261 -262 295 -234 301 -264 269 -234 263 -262 263 -262 257 -290 265 -234 261 -258 291 -260 269 -266 267 -270 271 -236 265 -262 261 -262 261 -264 255 -292 265 -266 233 -264 261 -260 295 -270 273 -268 233 -262 261 -262 255 -258 291 -264 269 -274 275 -236 265 -262 255 -256 291 -230 295 -264 269 -266 265 -236 299 -232 293 -264 269 -232 263 -264 263 -262 261 -264 255 -290 265 -266 233 -296 269 -272 235 -268 255 -258 289 -262 269 -268 265 -236 299 -266 267 -234 263 -262 263 -262 261 -264 261 -262 263 -262 295 -234 301 -266 267 -234 263 -262 257 -290 265 -272 269 -268 269 -232 265 -262 261 -262 261 -262 257 -290 267 -234 259 -292 265 -238 303 -266 267 -234 263 -262 257 -290 265 -266 233 -264 261 -264 293 -234 299 -268 269 -232 265 -262 255 -258 291 -264 267 -236 259 -290 265 -272 275 -238 259 -292 263 -266 231 -264 261 -264 293 -234 301 -266 267 -234 263 -264 261 -262 255 -292 265 -234 259 -292 265 -266 231 -264 295 -234 301 -264 269 -234 263 -262 263 -262 261 -262 263 -262 261 -264 295 -268 273 -236 265 -264 261 -260 257 -290 265 -272 269 -268 269 -232 265 -262 261 -262 255 -292 265 -234 259 -292 265 -266 265 -268 273 -236 265 -262 261 -262 261 -264 261 -296 269 -272 235 -268 261 -262 261 -262 261 -264 255 -290 267 -266 265 -236 301 -264 267 -234 263 -264 261 -262 263 -262 261 -264 255 -292 265 -266 263 -270 273 -236 265 -264 255 -256 291 -264 267 -268 233 -262 295 -234 299 -266 269 -232 265 -262 261 -262 257 -258 291 -264 267 -240 303 -266 269 -232 265 -262 261 -262 261 -264 261 -264 255 -292 265 -266 263 -236 301 -266 267 -234 263 -264 261 -264 261 -262 295 -268 271 -270 233 -264 261 -260 263 -262 257 -258 291 -262 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 257 -290 265 -266 231 -264 259 -290 231 -300 263 -270 273 -268 231 -264 261 -262 261 -264 257 -290 265 -266 271 -274 235 -266 257 -290 231 -298 231 -296 231 -262 295 -234 301 -266 233 -300 231 -264 261 -262 257 -258 291 -264 267 -268 263 -236 299 -264 269 -266 231 -264 261 -264 295 -234 299 -266 267 -266 231 -264 261 -262 257 -290 265 -234 259 -292 233 -298 265 -236 299 -266 233 -300 231 -264 257 -258 289 -230 293 -306 243 -274 235 -264 257 -288 265 -264 233 -296 231 -262 293 -236 299 -266 235 -300 231 -262 263 -262 261 -262 257 -290 265 -272 269 -266 235 -300 231 -296 -RAW_Data: 229 -262 263 -262 295 -234 299 -266 235 -300 231 -294 231 -262 263 -262 257 -290 265 -264 265 -236 301 -266 233 -300 231 -296 229 -262 261 -294 229 -296 231 -294 263 -236 299 -266 235 -300 229 -264 261 -262 263 -262 257 -290 265 -272 275 -270 233 -262 261 -262 261 -264 257 -258 291 -262 269 -266 265 -234 301 -264 269 -266 231 -264 261 -264 255 -290 265 -232 259 -294 231 -300 263 -236 301 -266 235 -300 229 -264 261 -294 229 -264 257 -290 231 -298 265 -270 273 -268 233 -264 255 -290 231 -300 231 -296 263 -234 301 -266 233 -300 231 -262 263 -262 257 -290 265 -266 231 -298 263 -268 271 -268 233 -262 261 -294 229 -264 295 -270 271 -270 231 -264 261 -260 263 -262 257 -292 231 -298 231 -296 231 -262 295 -236 299 -264 269 -266 233 -262 261 -262 263 -262 257 -290 265 -272 275 -270 233 -262 261 -262 261 -264 257 -290 265 -264 265 -270 267 -266 233 -300 231 -264 257 -258 291 -230 293 -264 267 -272 269 -266 235 -300 229 -296 231 -262 261 -262 257 -258 291 -264 267 -274 267 -234 263 -294 267 -266 233 -262 263 -262 257 -290 231 -298 265 -270 273 -268 233 -262 261 -262 257 -258 291 -264 267 -234 261 -290 231 -300 263 -270 267 -266 233 -300 231 -264 261 -262 263 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -264 261 -264 255 -290 231 -300 265 -268 273 -270 233 -262 261 -294 229 -264 257 -258 291 -262 267 -268 265 -234 299 -266 235 -300 231 -262 257 -292 229 -300 231 -298 263 -234 299 -232 295 -230 295 -262 235 -300 231 -264 293 -234 299 -266 269 -268 231 -262 261 -262 261 -294 231 -296 229 -296 263 -234 299 -266 233 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -296 229 -296 263 -234 299 -266 235 -300 229 -296 231 -262 257 -256 293 -262 269 -272 269 -264 235 -268 259 -258 259 -294 267 -266 267 -236 299 -264 235 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 231 -264 261 -264 261 -262 255 -292 265 -266 231 -298 261 -270 271 -268 231 -264 263 -262 257 -256 291 -262 269 -274 275 -270 231 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -266 233 -262 261 -294 231 -294 229 -264 255 -292 231 -298 231 -298 263 -236 299 -264 267 -266 231 -264 257 -258 259 -296 267 -266 265 -270 271 -236 259 -292 231 -300 231 -296 229 -262 295 -268 265 -266 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 -RAW_Data: 265 -236 299 -264 235 -300 231 -296 229 -264 293 -234 299 -266 235 -300 231 -264 261 -294 229 -296 231 -262 257 -290 265 -264 265 -236 301 -266 235 -300 229 -264 261 -262 257 -258 291 -296 239 -278 237 -270 257 -290 229 -300 231 -296 231 -262 293 -236 299 -266 235 -300 229 -264 257 -258 259 -260 295 -264 267 -274 267 -268 235 -300 229 -264 257 -290 265 -272 267 -234 263 -296 233 -298 231 -296 231 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -264 257 -290 231 -266 259 -292 233 -298 265 -236 299 -264 269 -266 233 -262 261 -262 257 -258 291 -264 267 -266 265 -270 271 -270 231 -264 261 -262 255 -290 265 -268 231 -264 295 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -230 295 -262 269 -266 265 -234 299 -266 269 -266 231 -264 261 -262 257 -258 291 -264 267 -268 263 -236 299 -264 235 -300 231 -264 261 -264 261 -294 263 -268 271 -270 233 -262 261 -262 257 -256 293 -262 269 -234 261 -292 263 -272 275 -270 231 -264 261 -260 257 -290 265 -234 293 -266 275 -272 233 -266 261 -262 259 -296 229 -264 257 -290 231 -300 263 -236 301 -266 235 -300 231 -262 261 -294 229 -264 257 -290 265 -272 267 -268 235 -266 259 -292 231 -298 231 -298 261 -270 271 -268 233 -264 261 -294 229 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -262 261 -296 229 -296 261 -270 271 -270 231 -264 261 -294 229 -264 257 -258 291 -262 269 -266 233 -262 295 -266 273 -268 233 -264 257 -258 289 -262 267 -268 231 -298 261 -270 271 -268 231 -264 261 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -232 295 -264 233 -266 259 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 261 -262 257 -258 293 -262 267 -268 265 -236 299 -264 269 -266 231 -264 261 -262 257 -290 265 -232 261 -292 231 -300 263 -270 273 -268 231 -264 261 -262 263 -262 295 -268 265 -234 295 -262 267 -268 231 -264 261 -260 257 -292 231 -298 233 -296 263 -236 299 -264 267 -268 231 -264 257 -290 231 -266 259 -292 265 -272 275 -236 261 -292 229 -266 259 -292 233 -298 263 -236 301 -266 233 -300 231 -264 263 -262 261 -294 229 -264 257 -290 265 -272 267 -268 235 -300 231 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -258 291 -264 267 -268 231 -264 295 -266 271 -270 233 -264 255 -258 291 -264 233 -300 231 -296 269 -238 265 -262 259 -262 293 -230 295 -264 233 -300 265 -234 299 -266 233 -300 231 -298 -RAW_Data: 229 -262 257 -290 265 -264 233 -296 263 -270 271 -268 231 -264 263 -262 257 -290 229 -266 261 -292 233 -298 265 -234 301 -266 235 -300 229 -264 261 -262 261 -296 261 -270 273 -268 233 -262 261 -262 257 -290 233 -266 259 -292 231 -300 231 -296 263 -268 273 -268 231 -264 261 -294 231 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -290 233 -298 231 -296 271 -238 263 -294 233 -298 231 -264 295 -234 299 -266 235 -300 229 -296 231 -262 257 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -262 257 -290 265 -272 269 -266 235 -300 231 -294 229 -264 257 -290 231 -266 259 -260 293 -262 267 -272 269 -266 235 -300 231 -262 261 -262 257 -258 293 -296 239 -278 237 -268 257 -258 257 -294 267 -234 261 -292 231 -298 265 -236 301 -266 233 -300 229 -264 257 -258 291 -228 295 -264 267 -268 265 -268 273 -268 231 -264 263 -262 255 -258 291 -230 295 -262 269 -268 263 -236 297 -266 235 -300 231 -264 261 -262 257 -258 291 -262 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 257 -256 291 -262 267 -268 231 -266 295 -234 299 -232 295 -262 235 -300 229 -296 231 -262 257 -258 291 -262 269 -266 265 -236 299 -264 235 -300 231 -264 255 -292 265 -270 269 -266 235 -302 229 -264 261 -262 257 -290 265 -232 259 -260 293 -262 269 -272 269 -264 235 -300 231 -264 261 -262 257 -258 291 -296 241 -276 239 -266 263 -262 255 -258 291 -264 267 -268 265 -234 299 -232 295 -262 235 -300 231 -264 261 -262 257 -258 291 -264 267 -268 263 -270 271 -268 233 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -260 257 -292 231 -298 231 -298 263 -234 299 -266 235 -300 231 -262 257 -292 231 -298 231 -296 271 -272 235 -266 257 -258 289 -228 295 -264 267 -268 233 -262 295 -234 299 -266 267 -268 231 -264 261 -294 229 -296 229 -264 261 -262 295 -268 271 -270 233 -262 261 -262 263 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 257 -290 231 -300 263 -236 301 -266 235 -300 229 -264 261 -262 257 -290 265 -266 233 -264 261 -294 263 -234 299 -266 235 -300 231 -296 229 -262 263 -262 257 -258 291 -262 235 -300 263 -270 273 -268 233 -262 261 -294 229 -296 229 -296 263 -268 273 -268 231 -264 261 -262 261 -262 257 -292 231 -298 265 -270 273 -268 233 -262 261 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -302 231 -264 257 -288 231 -300 263 -236 301 -266 -RAW_Data: 235 -300 231 -262 261 -262 257 -258 293 -228 295 -264 267 -274 267 -266 235 -300 231 -262 257 -258 291 -262 267 -274 275 -270 233 -262 261 -262 261 -264 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -260 295 -264 267 -268 265 -236 299 -264 235 -300 231 -262 257 -258 261 -294 267 -234 259 -292 231 -300 263 -236 301 -266 233 -300 231 -262 257 -258 291 -264 233 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -262 269 -266 265 -236 299 -264 235 -266 259 -260 291 -230 293 -264 267 -268 231 -264 293 -268 271 -270 233 -264 261 -262 255 -292 231 -266 259 -294 265 -272 267 -266 235 -300 231 -262 257 -290 231 -300 265 -270 271 -270 233 -262 261 -262 257 -290 265 -266 233 -264 257 -290 229 -300 265 -270 271 -270 231 -264 257 -288 231 -268 259 -292 231 -300 271 -238 265 -294 231 -300 231 -264 257 -290 231 -298 265 -236 299 -266 235 -300 231 -296 229 -262 261 -296 229 -296 229 -296 261 -270 271 -268 233 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -268 231 -264 261 -292 229 -296 263 -270 271 -268 233 -262 261 -294 231 -262 257 -292 231 -264 261 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -264 261 -262 261 -264 257 -258 291 -296 239 -278 237 -270 261 -262 255 -258 291 -230 295 -264 233 -300 265 -234 299 -266 235 -300 231 -296 229 -262 263 -262 257 -290 231 -300 263 -270 273 -268 231 -264 261 -296 229 -294 263 -236 299 -266 233 -300 231 -296 229 -264 259 -296 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -268 259 -292 231 -300 263 -236 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 265 -234 301 -266 235 -300 229 -296 229 -264 261 -262 257 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -262 257 -292 231 -266 259 -292 231 -300 265 -234 301 -264 235 -300 231 -296 229 -264 261 -294 263 -268 271 -270 233 -264 259 -294 229 -296 231 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -264 255 -258 291 -262 269 -266 265 -270 271 -268 233 -264 255 -290 231 -298 265 -236 301 -266 235 -300 229 -296 231 -262 257 -290 231 -298 231 -298 229 -264 295 -268 271 -270 231 -264 261 -262 -RAW_Data: 257 -290 265 -272 267 -268 233 -300 231 -264 263 -262 261 -262 257 -290 265 -266 231 -296 263 -270 271 -268 233 -264 261 -262 261 -296 229 -262 261 -296 261 -236 301 -266 233 -300 231 -262 263 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 231 -264 261 -262 261 -262 257 -290 265 -266 265 -270 267 -266 233 -300 231 -264 261 -296 229 -262 257 -290 231 -300 231 -296 263 -234 301 -264 269 -268 231 -262 261 -294 229 -296 229 -264 261 -294 263 -236 299 -266 235 -298 231 -264 257 -290 231 -300 263 -236 301 -266 235 -298 231 -296 229 -296 229 -262 257 -290 265 -266 265 -234 301 -266 235 -300 231 -262 261 -296 229 -264 293 -268 271 -270 233 -264 261 -262 261 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -296 229 -296 263 -268 273 -268 231 -264 257 -290 231 -298 265 -236 299 -266 235 -300 231 -262 257 -290 265 -232 261 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 255 -258 291 -262 269 -234 261 -292 231 -298 265 -234 301 -264 235 -300 231 -296 229 -262 261 -296 229 -296 229 -262 295 -234 301 -264 235 -300 231 -264 261 -294 229 -264 257 -258 291 -264 267 -268 263 -236 299 -264 235 -300 231 -296 229 -294 231 -262 257 -258 291 -262 267 -268 265 -270 271 -270 231 -264 259 -262 261 -296 231 -262 257 -290 231 -300 265 -234 301 -264 269 -266 231 -264 261 -262 261 -296 229 -264 257 -290 231 -300 231 -296 269 -272 235 -268 257 -290 229 -300 231 -264 295 -236 299 -264 235 -300 231 -264 261 -262 257 -258 291 -262 269 -266 265 -270 271 -268 233 -262 261 -262 257 -290 265 -272 269 -266 235 -300 231 -264 263 -262 261 -262 257 -258 291 -262 267 -268 265 -270 271 -270 231 -262 261 -296 229 -296 229 -262 257 -290 265 -266 233 -264 293 -234 301 -266 233 -300 231 -264 255 -258 291 -264 267 -234 261 -292 231 -298 265 -234 299 -266 269 -266 231 -264 257 -256 291 -230 295 -264 267 -266 265 -236 299 -266 235 -300 231 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -268 233 -264 255 -290 231 -298 233 -296 231 -262 293 -236 299 -232 295 -262 267 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -262 255 -292 231 -300 231 -264 263 -262 295 -268 271 -270 231 -264 261 -262 257 -290 265 -272 275 -270 233 -262 261 -262 261 -264 255 -260 291 -262 269 -266 265 -236 299 -264 235 -300 -RAW_Data: 231 -264 255 -258 293 -262 267 -268 263 -270 271 -270 231 -262 257 -258 291 -262 269 -266 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 265 -266 265 -236 299 -266 233 -300 231 -296 229 -264 255 -292 231 -300 231 -296 263 -234 299 -266 267 -268 231 -262 263 -294 229 -264 295 -234 299 -266 233 -300 231 -264 257 -258 291 -262 269 -234 259 -292 231 -298 265 -236 299 -266 233 -300 231 -264 261 -264 261 -262 255 -292 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 263 -266 231 -298 231 -294 269 -270 237 -266 263 -262 261 -262 257 -290 265 -266 265 -236 299 -264 269 -266 231 -264 261 -264 255 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -264 261 -262 257 -258 293 -262 267 -268 231 -264 293 -268 273 -270 231 -264 261 -262 255 -258 291 -296 241 -276 239 -268 263 -262 261 -260 263 -296 229 -262 295 -236 299 -264 269 -266 231 -264 261 -262 257 -258 291 -264 267 -268 263 -236 299 -266 233 -300 231 -264 261 -264 293 -234 301 -266 233 -300 231 -296 229 -264 255 -258 291 -264 267 -234 261 -292 231 -298 265 -268 273 -268 233 -262 261 -262 257 -258 293 -262 267 -268 231 -262 295 -236 299 -266 233 -300 231 -264 261 -294 229 -264 257 -290 265 -266 231 -296 263 -234 299 -266 269 -268 231 -262 257 -256 291 -264 267 -268 231 -264 293 -234 301 -266 233 -302 229 -264 257 -258 289 -264 267 -234 261 -292 231 -298 265 -270 271 -270 231 -264 261 -262 261 -262 257 -290 265 -266 273 -238 265 -294 231 -266 259 -260 259 -294 267 -268 265 -234 299 -266 267 -268 231 -264 261 -262 261 -262 257 -292 231 -298 265 -236 299 -266 235 -300 231 -262 263 -262 295 -234 299 -266 267 -268 231 -264 257 -256 291 -262 269 -268 231 -264 293 -268 271 -270 233 -264 261 -260 257 -290 265 -266 233 -296 269 -272 235 -268 257 -256 291 -228 293 -264 267 -268 233 -262 297 -234 299 -264 235 -300 231 -264 261 -264 259 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -290 231 -266 259 -294 265 -264 265 -236 301 -232 263 -294 267 -266 231 -262 259 -290 265 -272 269 -266 235 -300 231 -262 261 -294 231 -262 257 -292 263 -266 231 -298 263 -234 299 -266 233 -302 229 -296 229 -264 257 -288 265 -234 259 -260 291 -262 267 -274 267 -268 235 -300 229 -264 263 -262 257 -290 231 -300 263 -270 273 -268 231 -264 263 -262 257 -256 291 -262 267 -268 -RAW_Data: 265 -236 301 -264 233 -300 231 -296 229 -262 257 -258 291 -262 267 -236 259 -294 231 -298 271 -272 237 -266 263 -260 261 -262 257 -290 265 -272 269 -234 295 -264 233 -300 231 -262 263 -262 257 -290 231 -300 263 -270 273 -268 233 -264 259 -262 257 -292 265 -272 275 -270 231 -264 261 -262 255 -290 267 -234 259 -292 231 -298 265 -236 299 -266 235 -300 231 -262 257 -290 265 -232 261 -292 265 -272 267 -266 235 -300 231 -264 261 -294 229 -296 229 -264 261 -264 261 -294 261 -236 301 -266 233 -268 259 -258 259 -260 293 -264 269 -268 263 -236 301 -264 233 -300 231 -264 263 -262 257 -290 265 -264 231 -298 231 -262 293 -234 301 -266 235 -300 229 -296 231 -262 257 -256 293 -230 293 -264 267 -274 267 -266 235 -300 231 -262 257 -290 231 -298 265 -270 267 -266 235 -300 229 -264 263 -262 261 -262 257 -258 291 -264 267 -268 265 -268 271 -270 231 -262 261 -262 257 -258 293 -296 239 -276 237 -302 231 -262 255 -256 291 -264 269 -268 231 -262 293 -234 301 -264 269 -268 231 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -264 235 -266 259 -292 231 -300 265 -234 301 -264 235 -300 231 -264 257 -256 291 -230 295 -264 233 -300 231 -264 295 -234 301 -264 235 -300 231 -262 263 -262 261 -262 257 -258 293 -262 235 -300 263 -236 299 -266 233 -302 229 -296 229 -264 257 -290 265 -270 269 -268 235 -300 231 -262 263 -262 261 -262 257 -290 265 -266 233 -264 293 -236 299 -266 235 -300 229 -264 261 -296 229 -262 263 -262 257 -290 265 -266 265 -236 299 -266 235 -300 229 -264 257 -290 233 -266 259 -292 231 -300 269 -274 235 -268 261 -262 255 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 229 -264 261 -294 229 -296 229 -264 295 -268 273 -268 231 -264 287 -266 267 -270 271 -268 231 -262 261 -262 257 -290 233 -298 231 -296 231 -262 295 -270 271 -268 233 -262 261 -296 229 -296 229 -262 261 -294 263 -270 271 -236 261 -260 257 -294 267 -268 231 -264 293 -236 299 -266 235 -300 231 -262 261 -262 257 -258 293 -262 267 -268 265 -234 301 -264 235 -300 231 -294 231 -262 295 -234 299 -266 235 -300 231 -264 261 -262 255 -292 231 -300 231 -296 263 -236 299 -264 269 -266 231 -262 259 -290 229 -266 261 -292 265 -272 275 -236 261 -292 231 -298 231 -296 229 -262 295 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -264 257 -290 231 -300 263 -270 273 -270 231 -264 261 -294 229 -262 257 -258 293 -228 -RAW_Data: 293 -264 267 -268 265 -236 299 -264 269 -266 233 -262 261 -294 231 -262 261 -264 255 -292 265 -266 265 -270 271 -268 231 -264 261 -262 257 -290 265 -234 259 -292 265 -272 267 -266 235 -302 229 -296 229 -264 257 -258 289 -230 295 -264 267 -268 265 -268 271 -270 231 -264 261 -264 255 -290 265 -272 273 -272 231 -264 261 -262 257 -290 231 -266 259 -260 293 -262 235 -300 263 -270 271 -270 231 -264 261 -262 257 -256 293 -230 295 -262 269 -272 275 -236 261 -292 229 -266 259 -292 265 -272 267 -266 235 -300 231 -296 229 -294 231 -294 229 -264 261 -294 263 -234 301 -266 235 -300 229 -296 229 -262 289 -300 241 -272 235 -264 261 -262 261 -294 229 -264 257 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 229 -296 229 -262 261 -294 263 -270 267 -264 235 -300 231 -294 231 -262 261 -264 255 -292 231 -298 231 -298 263 -234 299 -232 295 -230 295 -230 295 -262 235 -300 263 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 229 -296 261 -270 271 -268 233 -264 261 -294 261 -270 271 -270 231 -264 261 -294 229 -264 257 -290 231 -298 233 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -296 229 -296 229 -294 263 -268 273 -234 261 -260 259 -260 293 -264 269 -266 265 -236 299 -264 235 -300 231 -264 261 -264 261 -262 255 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -264 293 -234 301 -266 233 -300 231 -296 229 -264 261 -294 229 -262 259 -290 231 -300 263 -270 267 -266 233 -300 231 -264 261 -262 255 -260 291 -296 241 -276 239 -300 229 -262 261 -262 257 -258 291 -262 267 -268 265 -234 301 -264 235 -300 233 -262 261 -294 229 -264 257 -290 231 -298 265 -270 273 -270 231 -264 255 -258 291 -262 267 -236 259 -292 231 -298 265 -236 301 -264 235 -300 231 -294 231 -262 293 -268 267 -266 235 -300 229 -296 231 -262 257 -290 231 -266 259 -294 231 -300 231 -296 263 -268 271 -268 233 -262 263 -294 229 -262 259 -290 229 -266 261 -292 231 -300 263 -270 273 -268 233 -264 261 -260 257 -258 293 -262 267 -272 275 -270 233 -262 257 -258 289 -230 295 -264 267 -268 265 -234 299 -266 233 -302 231 -264 261 -260 257 -292 231 -298 231 -298 229 -296 263 -268 271 -268 233 -264 257 -256 259 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 261 -294 229 -296 229 -296 263 -234 299 -264 269 -266 -RAW_Data: 233 -262 257 -290 263 -272 269 -266 235 -302 229 -296 231 -262 257 -256 291 -264 267 -234 261 -292 263 -272 269 -266 235 -300 229 -264 257 -290 231 -298 233 -296 231 -262 293 -268 267 -266 235 -300 231 -262 257 -290 231 -298 233 -264 257 -292 263 -272 275 -236 263 -258 259 -292 267 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -262 255 -292 231 -266 259 -294 231 -300 231 -296 263 -234 299 -266 233 -300 231 -264 261 -294 231 -294 231 -262 261 -294 263 -268 273 -268 233 -264 261 -262 255 -292 231 -298 265 -270 273 -270 231 -264 261 -260 257 -258 293 -262 235 -300 265 -270 271 -268 231 -264 261 -294 229 -264 257 -290 231 -300 271 -272 237 -266 263 -260 261 -262 257 -258 293 -262 235 -300 265 -270 271 -268 231 -264 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -296 229 -296 229 -262 257 -292 231 -266 259 -292 265 -272 267 -268 233 -302 231 -262 257 -256 291 -230 295 -304 243 -274 235 -264 257 -256 289 -262 269 -234 261 -292 231 -298 265 -268 267 -266 235 -298 231 -264 261 -262 257 -258 291 -264 267 -268 265 -268 271 -268 233 -264 261 -294 229 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -298 229 -262 261 -294 263 -234 301 -266 235 -300 231 -262 263 -262 255 -292 231 -300 231 -296 231 -262 295 -232 299 -266 269 -266 233 -262 261 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -268 233 -264 261 -262 257 -290 265 -272 267 -268 233 -300 231 -264 257 -258 289 -264 267 -234 261 -292 231 -298 265 -236 299 -266 235 -298 231 -264 257 -258 291 -262 267 -268 265 -268 273 -268 231 -264 257 -256 293 -262 233 -302 263 -236 299 -266 233 -300 231 -298 229 -262 261 -294 229 -296 231 -262 293 -268 267 -266 235 -300 231 -262 263 -294 263 -268 267 -266 233 -300 231 -262 257 -292 231 -298 231 -298 229 -296 261 -268 267 -264 235 -300 231 -264 261 -294 229 -264 257 -290 265 -272 273 -272 233 -262 257 -258 257 -294 267 -268 233 -264 293 -234 299 -266 267 -268 231 -264 257 -256 291 -230 295 -264 267 -274 267 -266 235 -300 231 -294 229 -296 229 -264 255 -258 291 -264 267 -274 267 -234 263 -294 233 -300 231 -298 229 -262 261 -294 263 -268 273 -270 231 -264 261 -294 229 -262 259 -290 231 -298 231 -298 229 -296 261 -270 271 -268 231 -298 229 -262 257 -258 291 -262 267 -266 233 -296 263 -234 299 -266 -RAW_Data: 233 -300 231 -296 231 -262 257 -256 291 -264 233 -300 271 -274 235 -268 261 -262 259 -262 257 -292 231 -266 259 -294 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 231 -298 231 -296 263 -236 299 -264 235 -302 231 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -264 261 -262 255 -292 231 -300 265 -268 267 -266 235 -300 231 -262 257 -258 291 -230 293 -264 235 -300 265 -236 299 -264 233 -302 229 -298 229 -262 261 -262 257 -258 291 -264 233 -300 265 -270 271 -270 231 -264 261 -262 255 -292 231 -300 231 -264 257 -292 231 -298 265 -236 299 -264 269 -266 233 -262 261 -296 229 -262 257 -292 231 -298 231 -298 261 -270 271 -268 233 -264 261 -262 255 -258 293 -262 267 -274 275 -270 231 -264 261 -294 229 -262 257 -258 259 -296 267 -266 267 -234 299 -266 233 -300 231 -264 257 -256 293 -262 267 -268 265 -236 299 -230 295 -230 295 -264 233 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 233 -296 263 -236 299 -264 235 -300 231 -262 257 -258 293 -268 271 -270 237 -302 231 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -262 257 -292 231 -298 265 -236 301 -264 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 265 -272 269 -266 235 -300 231 -294 229 -296 229 -296 229 -296 229 -294 229 -264 293 -268 273 -268 233 -264 261 -262 255 -292 231 -298 265 -270 273 -270 231 -264 261 -260 257 -258 293 -262 235 -300 233 -262 295 -234 299 -266 235 -300 231 -294 229 -296 229 -296 229 -262 257 -292 231 -298 265 -236 299 -266 235 -300 229 -296 231 -262 261 -262 257 -290 231 -300 265 -270 271 -236 261 -292 231 -298 231 -296 231 -262 293 -268 267 -266 235 -300 231 -262 261 -296 229 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -262 255 -292 231 -266 291 -302 239 -274 235 -264 261 -262 255 -292 231 -298 231 -298 229 -296 263 -234 299 -264 235 -300 231 -264 257 -258 291 -262 267 -268 231 -296 263 -234 299 -266 233 -302 231 -264 261 -262 255 -260 291 -262 235 -300 233 -262 295 -234 297 -266 269 -268 231 -264 255 -290 265 -272 267 -268 235 -300 231 -262 261 -296 229 -262 257 -258 261 -294 267 -266 267 -234 299 -266 235 -300 -RAW_Data: 231 -262 263 -262 255 -292 231 -266 259 -292 233 -298 265 -270 271 -270 231 -262 261 -262 257 -292 265 -272 269 -266 235 -300 231 -294 229 -264 257 -258 257 -294 269 -266 233 -264 295 -232 299 -266 235 -300 231 -264 257 -290 231 -266 259 -294 265 -272 273 -238 259 -292 231 -266 259 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 261 -294 231 -262 257 -292 265 -270 269 -266 235 -300 231 -262 259 -290 229 -300 265 -270 271 -270 231 -264 257 -290 231 -266 259 -292 233 -298 231 -296 263 -268 273 -268 231 -296 231 -262 257 -290 231 -298 231 -296 271 -272 235 -266 259 -256 259 -260 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -296 229 -296 263 -268 271 -268 233 -264 261 -262 255 -290 265 -234 261 -292 231 -300 231 -296 263 -268 271 -268 233 -264 261 -294 229 -264 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -260 257 -292 231 -298 233 -296 269 -272 235 -300 229 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -264 255 -290 231 -300 231 -298 263 -234 299 -232 295 -230 295 -262 235 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -258 291 -264 267 -272 269 -266 235 -300 231 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -258 259 -292 269 -234 261 -292 231 -298 265 -270 271 -270 231 -262 261 -296 229 -296 229 -296 229 -294 263 -268 273 -268 231 -264 257 -256 291 -264 233 -268 259 -292 231 -300 263 -270 267 -232 263 -262 293 -230 295 -262 235 -300 263 -236 301 -266 233 -300 229 -296 231 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -232 295 -264 233 -300 231 -296 231 -262 257 -256 291 -264 233 -300 265 -236 301 -266 233 -300 231 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -292 231 -266 259 -294 231 -300 263 -236 299 -266 233 -300 231 -296 229 -264 257 -258 259 -328 239 -276 239 -300 231 -262 255 -258 291 -262 235 -300 265 -236 299 -232 293 -264 233 -302 231 -264 261 -262 257 -258 291 -262 267 -268 265 -268 273 -268 231 -264 257 -290 231 -298 265 -270 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -296 269 -272 235 -268 257 -256 -RAW_Data: 259 -260 293 -264 269 -268 231 -264 293 -268 267 -266 235 -300 229 -264 257 -290 231 -300 231 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -234 301 -264 267 -268 231 -264 255 -290 231 -300 265 -268 267 -266 235 -300 231 -294 231 -262 257 -290 231 -298 231 -298 229 -296 263 -234 299 -266 233 -302 231 -264 259 -296 229 -262 257 -260 257 -294 267 -268 265 -270 271 -268 233 -296 229 -262 261 -296 229 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -266 259 -292 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 255 -290 231 -268 261 -292 231 -298 271 -274 235 -266 259 -288 231 -298 265 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -300 231 -262 257 -292 265 -270 269 -266 235 -300 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -294 231 -262 257 -290 231 -300 263 -270 273 -268 233 -262 261 -296 229 -294 229 -264 257 -258 259 -294 267 -268 265 -270 271 -268 233 -262 257 -258 291 -262 235 -300 231 -296 263 -234 299 -266 233 -300 231 -298 229 -262 261 -294 229 -264 257 -292 229 -300 265 -236 299 -266 235 -300 231 -262 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 235 -266 259 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 229 -264 257 -290 231 -266 259 -292 267 -270 269 -266 235 -300 231 -262 257 -258 259 -260 295 -304 243 -274 235 -266 255 -258 257 -294 267 -234 261 -292 231 -300 263 -236 301 -266 233 -300 229 -264 257 -258 259 -260 295 -262 269 -268 265 -268 273 -236 259 -292 231 -298 231 -296 263 -270 271 -268 231 -296 231 -262 261 -294 229 -296 229 -264 293 -268 267 -266 233 -302 231 -262 261 -296 229 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -266 259 -260 291 -262 267 -268 265 -270 271 -270 231 -264 261 -294 229 -294 231 -294 229 -296 263 -268 271 -270 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 231 -296 263 -234 299 -232 295 -230 295 -264 233 -300 265 -234 299 -266 233 -300 231 -298 229 -262 261 -294 229 -264 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -228 295 -264 267 -268 265 -270 271 -268 231 -264 261 -294 229 -296 229 -264 293 -268 273 -268 233 -264 261 -262 261 -262 257 -292 231 -298 233 -296 263 -234 299 -266 233 -302 231 -262 261 -296 229 -262 -RAW_Data: 257 -292 263 -272 275 -236 263 -292 229 -266 259 -292 265 -272 267 -266 235 -300 231 -264 261 -262 257 -258 291 -230 295 -264 267 -274 267 -266 233 -300 231 -264 257 -290 265 -272 267 -268 235 -300 231 -294 229 -264 257 -290 231 -300 231 -262 259 -290 231 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -258 291 -262 235 -300 265 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -300 263 -270 273 -268 233 -264 261 -262 257 -290 231 -298 233 -296 231 -262 293 -270 265 -266 235 -300 231 -264 261 -294 229 -264 257 -258 257 -294 269 -266 233 -296 263 -234 299 -264 235 -300 231 -264 257 -290 231 -298 233 -296 263 -236 299 -230 295 -264 235 -266 259 -292 231 -298 265 -268 267 -266 235 -300 231 -264 261 -294 229 -262 257 -258 293 -262 267 -272 269 -266 235 -300 231 -264 257 -256 291 -270 271 -270 237 -300 231 -296 229 -296 229 -262 257 -258 259 -294 267 -268 265 -234 301 -264 235 -300 231 -262 257 -258 259 -294 267 -268 233 -296 269 -236 265 -294 231 -266 259 -292 231 -300 265 -268 273 -268 233 -294 229 -294 231 -294 231 -294 229 -296 229 -296 263 -234 299 -264 235 -300 231 -264 257 -258 259 -262 293 -264 267 -274 267 -268 233 -300 231 -296 229 -262 257 -258 291 -230 295 -264 233 -300 265 -234 299 -266 233 -302 231 -264 259 -296 229 -262 257 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -236 301 -266 233 -300 231 -262 257 -258 291 -262 269 -272 275 -270 231 -264 261 -262 255 -260 291 -262 235 -300 231 -296 263 -234 299 -266 233 -300 231 -296 229 -264 257 -258 291 -262 267 -274 269 -266 235 -300 231 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -300 263 -270 267 -266 235 -300 231 -262 261 -294 229 -296 263 -268 271 -270 231 -264 261 -294 229 -264 257 -290 231 -300 231 -298 261 -270 271 -268 231 -264 257 -290 231 -298 231 -298 229 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -264 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -260 259 -294 267 -268 263 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 231 -262 293 -268 267 -266 235 -300 231 -262 263 -294 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 257 -290 -RAW_Data: 231 -300 263 -270 267 -266 233 -300 231 -264 261 -294 229 -296 263 -268 273 -268 231 -264 261 -262 257 -258 291 -262 267 -268 231 -296 263 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -298 229 -264 293 -268 273 -268 233 -262 257 -290 231 -298 265 -270 267 -266 235 -300 229 -264 257 -290 231 -264 261 -292 233 -298 265 -270 271 -270 231 -264 261 -262 255 -258 293 -302 243 -274 235 -266 261 -262 255 -258 291 -262 235 -268 259 -292 231 -298 265 -236 301 -266 233 -300 229 -264 257 -258 259 -294 267 -268 265 -236 299 -264 233 -302 231 -264 261 -262 257 -258 291 -230 293 -264 269 -266 265 -236 299 -232 293 -264 233 -302 231 -296 263 -234 299 -264 233 -302 231 -264 261 -262 257 -290 231 -300 231 -264 257 -292 231 -298 265 -268 267 -266 235 -300 231 -294 231 -262 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -292 231 -298 265 -270 271 -270 231 -264 261 -262 255 -292 231 -266 259 -294 231 -300 231 -296 263 -268 271 -268 233 -262 257 -258 259 -294 267 -268 231 -298 267 -272 235 -268 257 -290 229 -300 231 -296 231 -262 295 -268 271 -270 231 -264 257 -290 231 -264 261 -292 265 -272 267 -268 235 -300 229 -296 229 -296 229 -296 263 -234 299 -266 233 -300 231 -296 229 -296 229 -296 229 -262 257 -290 231 -300 263 -270 273 -270 231 -264 261 -294 229 -262 259 -290 231 -298 271 -274 237 -266 261 -262 261 -294 229 -296 263 -234 299 -266 233 -302 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -294 231 -300 263 -236 301 -232 263 -294 233 -300 231 -296 229 -296 229 -296 229 -262 257 -290 265 -272 267 -268 233 -302 231 -262 261 -262 257 -290 233 -298 231 -298 263 -234 301 -264 233 -300 231 -296 231 -262 257 -290 231 -266 259 -294 265 -272 267 -268 233 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 231 -296 263 -268 271 -270 231 -264 261 -294 229 -264 257 -290 231 -300 271 -274 235 -266 257 -258 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 257 -288 231 -300 231 -296 263 -270 271 -268 233 -264 261 -262 257 -290 265 -272 267 -268 235 -300 231 -294 229 -296 229 -264 255 -292 231 -300 231 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -290 -RAW_Data: 231 -300 231 -296 263 -268 273 -268 231 -264 261 -294 229 -296 229 -296 263 -234 299 -266 267 -268 231 -264 261 -294 229 -262 259 -258 259 -294 267 -268 265 -234 299 -266 233 -300 231 -298 229 -262 261 -294 229 -264 257 -292 263 -272 269 -266 235 -300 231 -264 261 -294 229 -296 263 -234 299 -266 233 -302 231 -264 261 -262 255 -292 231 -300 231 -296 263 -236 297 -266 235 -300 231 -262 259 -290 229 -300 231 -298 269 -272 235 -266 259 -256 257 -294 267 -236 259 -292 231 -300 263 -236 301 -266 233 -300 231 -294 231 -294 229 -296 229 -296 229 -294 263 -268 273 -268 231 -264 263 -294 227 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -300 265 -270 267 -264 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 235 -300 231 -296 229 -262 261 -262 257 -290 233 -298 265 -270 267 -266 233 -300 231 -262 257 -258 291 -262 269 -234 261 -290 231 -300 263 -236 301 -266 233 -300 231 -294 229 -264 257 -256 293 -262 235 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 269 -272 235 -300 229 -262 261 -294 231 -294 231 -294 263 -268 265 -266 235 -300 231 -264 261 -262 255 -260 291 -262 269 -272 269 -266 235 -300 229 -296 229 -264 259 -296 261 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -264 295 -268 271 -270 231 -264 263 -260 257 -258 291 -296 241 -276 237 -302 229 -264 255 -288 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 265 -266 235 -300 231 -294 231 -294 263 -234 299 -266 233 -302 231 -264 261 -262 255 -292 231 -300 231 -264 257 -290 265 -272 273 -272 233 -262 261 -294 229 -296 229 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 231 -298 231 -264 291 -264 275 -272 235 -264 261 -262 261 -294 229 -296 229 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -230 295 -264 233 -300 265 -234 299 -232 295 -264 267 -266 233 -262 261 -262 257 -258 291 -264 267 -268 265 -268 273 -268 231 -264 257 -290 229 -300 265 -270 265 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 265 -272 267 -266 235 -300 231 -296 229 -296 229 -262 289 -298 241 -272 235 -264 263 -260 261 -262 257 -292 231 -298 233 -296 231 -294 263 -268 273 -268 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 273 -236 261 -258 -RAW_Data: 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 255 -292 231 -298 231 -298 261 -236 299 -264 235 -302 231 -264 259 -262 257 -292 231 -298 233 -296 231 -262 293 -268 267 -266 235 -300 229 -296 229 -296 229 -296 229 -294 263 -234 301 -264 235 -300 231 -294 231 -294 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 233 -302 229 -298 229 -262 257 -290 231 -266 261 -260 259 -292 267 -274 269 -266 235 -300 231 -262 257 -290 233 -298 231 -296 269 -272 237 -266 263 -262 259 -296 229 -296 229 -294 231 -294 263 -234 299 -266 235 -300 231 -262 257 -292 229 -300 231 -298 229 -262 295 -268 271 -270 233 -264 257 -288 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -292 231 -266 259 -292 231 -300 265 -268 273 -268 233 -262 261 -262 257 -290 265 -272 269 -266 235 -300 231 -296 229 -264 261 -260 257 -292 231 -298 265 -270 267 -266 235 -300 229 -264 257 -256 293 -262 235 -300 263 -236 301 -266 233 -268 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -292 231 -300 263 -270 267 -266 233 -300 231 -264 261 -262 257 -258 291 -262 267 -272 269 -268 233 -302 229 -296 229 -296 229 -262 257 -258 291 -268 271 -272 237 -302 231 -262 261 -294 229 -264 255 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -264 257 -258 259 -296 267 -234 259 -292 231 -300 263 -236 299 -266 233 -302 231 -264 255 -258 291 -230 293 -264 269 -266 265 -270 271 -236 259 -292 229 -266 261 -292 231 -298 265 -236 301 -266 233 -300 229 -296 229 -264 255 -292 231 -300 231 -296 263 -268 273 -268 231 -296 229 -264 261 -294 263 -234 301 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -298 233 -296 263 -268 271 -270 231 -266 261 -260 257 -290 231 -300 231 -298 269 -272 235 -266 257 -290 231 -266 259 -292 265 -272 267 -268 233 -300 231 -298 229 -262 261 -262 257 -290 231 -300 265 -270 273 -268 231 -264 261 -262 257 -258 291 -264 233 -302 231 -262 295 -234 299 -266 233 -302 231 -264 261 -294 229 -296 229 -264 255 -292 265 -272 267 -266 235 -300 231 -264 261 -262 257 -290 231 -300 231 -264 257 -290 231 -300 265 -236 301 -264 235 -298 231 -298 229 -262 257 -258 291 -230 295 -262 269 -272 267 -266 235 -300 231 -264 261 -294 229 -296 263 -234 299 -266 235 -300 231 -262 257 -290 231 -266 261 -292 231 -300 231 -296 263 -268 271 -270 231 -264 261 -294 229 -296 -RAW_Data: 229 -296 229 -294 263 -268 267 -232 263 -294 233 -300 231 -296 263 -236 299 -232 295 -264 233 -300 231 -296 229 -262 257 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -296 229 -262 257 -258 259 -296 267 -266 265 -236 299 -264 235 -300 231 -264 257 -258 291 -262 235 -300 265 -268 273 -236 259 -292 231 -298 231 -296 229 -296 263 -234 299 -266 235 -300 231 -294 229 -264 257 -290 231 -300 231 -296 263 -234 301 -266 233 -300 231 -262 257 -258 259 -262 293 -264 269 -268 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -294 263 -268 265 -266 235 -300 231 -296 229 -262 261 -262 257 -258 293 -262 235 -300 265 -236 299 -264 235 -300 231 -262 257 -258 291 -262 267 -268 231 -296 269 -272 235 -300 229 -264 255 -258 291 -262 235 -300 265 -234 299 -266 235 -300 231 -264 261 -294 229 -296 229 -296 229 -262 295 -234 301 -264 235 -300 231 -296 229 -262 295 -268 267 -232 261 -296 267 -266 233 -264 255 -290 231 -268 259 -292 231 -300 265 -268 273 -268 233 -262 261 -294 231 -296 229 -294 269 -272 235 -300 229 -262 261 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -264 257 -258 289 -262 269 -268 265 -234 299 -266 233 -300 231 -296 231 -294 263 -268 271 -270 231 -264 261 -294 229 -296 229 -264 257 -290 231 -298 265 -270 271 -270 233 -264 261 -260 257 -258 291 -230 295 -264 267 -272 269 -266 235 -300 229 -296 231 -262 261 -262 257 -258 291 -264 233 -300 265 -270 271 -270 231 -262 257 -258 291 -262 267 -268 233 -264 293 -234 299 -264 269 -268 231 -264 261 -262 257 -290 231 -266 261 -258 261 -294 267 -266 267 -234 299 -266 233 -302 231 -264 255 -258 291 -264 233 -300 231 -296 263 -268 273 -234 261 -292 231 -266 259 -292 263 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -304 241 -276 233 -266 261 -262 255 -290 231 -266 261 -292 231 -300 263 -270 267 -266 235 -300 229 -264 261 -262 257 -258 291 -262 269 -266 265 -270 271 -236 259 -292 231 -298 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -292 231 -298 231 -298 263 -268 271 -268 233 -296 229 -264 259 -296 261 -270 271 -270 231 -264 261 -294 229 -296 229 -264 255 -292 231 -298 233 -296 263 -234 299 -266 -RAW_Data: 233 -300 231 -298 229 -262 261 -262 257 -290 265 -272 269 -266 235 -302 229 -264 261 -294 229 -264 257 -290 231 -300 231 -298 263 -234 299 -230 295 -232 293 -230 295 -264 233 -300 263 -236 301 -266 233 -300 231 -296 229 -294 229 -264 255 -292 231 -298 233 -296 263 -234 299 -266 233 -302 231 -264 261 -262 257 -258 291 -230 293 -264 267 -268 265 -234 299 -266 235 -300 231 -262 257 -290 231 -300 265 -236 301 -264 235 -300 229 -264 257 -290 231 -298 233 -296 231 -262 293 -268 273 -268 233 -264 261 -294 229 -264 257 -290 231 -300 271 -240 265 -294 231 -298 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -298 261 -268 271 -270 231 -264 257 -290 263 -272 269 -266 235 -300 231 -296 231 -262 261 -262 257 -290 231 -266 261 -292 265 -272 267 -268 235 -300 229 -264 257 -258 257 -262 293 -264 269 -266 265 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 265 -272 267 -268 235 -302 229 -264 255 -290 231 -300 233 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -296 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -296 231 -294 263 -268 271 -270 231 -264 257 -258 257 -294 267 -268 265 -236 301 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -268 271 -270 231 -264 261 -296 261 -236 299 -232 295 -264 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -296 261 -268 271 -270 231 -266 261 -260 257 -290 265 -266 233 -296 269 -272 235 -300 229 -262 257 -256 291 -264 233 -300 265 -268 273 -268 233 -264 259 -296 229 -296 229 -262 261 -296 261 -270 265 -266 235 -300 231 -296 229 -264 293 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -264 257 -292 263 -272 269 -266 235 -300 231 -296 229 -264 255 -290 231 -300 265 -236 299 -234 261 -296 233 -298 231 -264 257 -292 231 -266 259 -260 259 -294 267 -274 273 -238 261 -258 259 -294 267 -266 233 -296 261 -236 297 -266 267 -268 231 -264 261 -262 255 -292 231 -300 231 -296 231 -296 261 -236 299 -230 295 -264 233 -300 231 -296 231 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -268 259 -258 257 -294 267 -268 265 -236 301 -264 233 -300 231 -298 229 -262 261 -262 257 -290 231 -300 265 -270 273 -268 231 -264 261 -262 257 -258 291 -296 241 -276 237 -302 229 -264 259 -262 257 -290 231 -298 -RAW_Data: 233 -296 263 -236 299 -230 295 -264 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -270 271 -234 261 -292 231 -298 231 -296 263 -236 299 -264 235 -300 231 -264 257 -258 291 -262 235 -300 231 -296 263 -234 299 -264 235 -300 231 -296 229 -264 255 -290 233 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 265 -270 273 -268 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 229 -298 261 -270 271 -268 233 -264 261 -294 229 -264 255 -292 231 -300 271 -272 237 -266 257 -258 257 -294 267 -268 231 -296 263 -236 299 -264 233 -300 231 -298 229 -294 231 -262 257 -290 231 -300 263 -236 301 -266 233 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -296 229 -296 229 -262 261 -262 257 -292 231 -298 265 -270 273 -268 231 -298 229 -262 261 -294 229 -296 261 -270 271 -270 231 -264 257 -258 259 -260 295 -262 269 -266 265 -236 299 -264 235 -302 231 -262 261 -294 229 -296 231 -294 263 -234 299 -266 235 -300 231 -296 229 -296 261 -268 265 -234 261 -296 267 -266 233 -264 261 -294 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 231 -298 231 -296 231 -294 263 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -262 267 -274 275 -270 231 -264 257 -290 229 -266 261 -260 259 -294 267 -268 265 -234 299 -266 235 -300 231 -296 229 -296 229 -262 261 -262 257 -290 231 -300 265 -236 301 -266 233 -300 231 -296 229 -262 261 -294 229 -296 231 -294 263 -268 271 -236 261 -260 259 -260 293 -264 267 -274 267 -234 263 -294 233 -300 231 -298 229 -262 257 -290 231 -298 231 -298 263 -268 273 -268 231 -296 229 -264 261 -294 263 -268 273 -268 233 -262 261 -262 257 -258 291 -262 267 -234 261 -292 233 -298 265 -234 301 -266 233 -300 231 -262 257 -292 231 -264 261 -292 265 -272 275 -270 231 -264 255 -258 291 -264 267 -272 269 -266 235 -300 231 -294 229 -296 229 -264 255 -290 231 -300 265 -236 301 -266 233 -300 231 -294 229 -296 231 -262 257 -256 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 269 -266 265 -270 271 -268 233 -262 261 -262 257 -290 231 -268 259 -260 259 -294 267 -268 265 -236 299 -264 235 -300 231 -264 261 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 261 -262 257 -258 291 -264 267 -272 -RAW_Data: 275 -270 231 -264 255 -290 231 -266 259 -294 231 -300 231 -296 263 -234 299 -266 235 -300 231 -262 257 -258 259 -294 267 -268 265 -236 299 -232 295 -262 235 -300 231 -296 263 -234 299 -232 295 -264 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -262 289 -266 269 -268 271 -268 231 -264 259 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -262 257 -292 263 -272 269 -266 235 -300 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -266 259 -294 265 -272 267 -268 233 -300 231 -296 229 -294 231 -262 257 -290 231 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -262 257 -258 291 -230 295 -262 235 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -260 257 -292 231 -266 259 -294 231 -300 263 -270 273 -268 231 -264 261 -294 229 -262 257 -292 231 -298 231 -298 269 -272 235 -266 263 -262 255 -290 233 -298 231 -298 229 -264 293 -268 267 -266 233 -300 231 -264 255 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -264 257 -292 263 -272 267 -234 295 -264 267 -266 233 -296 227 -262 257 -258 291 -230 295 -264 235 -300 263 -236 299 -266 233 -300 231 -264 257 -290 231 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -294 229 -264 257 -290 231 -298 233 -296 263 -270 271 -268 231 -296 231 -262 257 -290 231 -266 259 -292 231 -300 231 -296 263 -268 273 -268 231 -296 231 -262 257 -256 291 -262 267 -234 261 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -290 231 -300 233 -296 267 -272 235 -268 257 -290 231 -298 231 -296 231 -296 261 -268 265 -266 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -264 261 -262 257 -292 231 -298 233 -264 257 -290 265 -270 269 -266 235 -300 231 -264 255 -260 259 -260 295 -304 243 -274 235 -264 257 -288 231 -298 231 -298 229 -296 263 -234 299 -266 233 -302 231 -264 259 -262 257 -258 293 -262 267 -266 267 -268 273 -268 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -264 261 -294 229 -296 229 -296 229 -294 263 -234 299 -266 235 -300 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -258 291 -302 243 -274 235 -266 261 -262 261 -262 257 -258 -RAW_Data: 291 -262 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 231 -298 261 -236 297 -266 235 -300 231 -296 229 -262 261 -262 257 -292 231 -298 265 -270 273 -268 233 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -262 261 -296 229 -296 229 -296 261 -270 271 -268 231 -296 231 -262 261 -262 289 -300 239 -274 235 -264 261 -262 261 -262 257 -290 265 -266 233 -296 229 -264 293 -234 299 -266 235 -300 231 -296 229 -264 261 -294 229 -296 263 -268 271 -268 233 -264 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 229 -264 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 235 -300 231 -296 229 -264 255 -292 231 -266 259 -292 231 -300 263 -270 267 -266 235 -300 229 -264 257 -290 265 -272 267 -268 233 -300 231 -264 257 -258 291 -228 295 -264 267 -268 265 -234 299 -266 233 -302 231 -262 261 -262 257 -292 231 -298 231 -264 259 -290 231 -298 265 -270 267 -266 235 -300 229 -264 257 -290 231 -298 231 -298 263 -268 273 -268 231 -264 255 -290 231 -300 231 -264 259 -290 231 -298 265 -270 267 -264 235 -300 231 -262 257 -292 231 -298 231 -296 263 -236 299 -232 295 -230 261 -296 267 -268 231 -296 263 -234 299 -264 235 -300 231 -264 261 -294 229 -264 257 -258 259 -294 267 -274 269 -266 235 -300 229 -264 257 -258 291 -268 271 -270 237 -304 231 -262 261 -294 229 -262 257 -290 231 -300 265 -236 301 -266 233 -300 231 -262 257 -258 291 -230 295 -262 235 -300 265 -268 273 -236 259 -292 231 -298 231 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -264 257 -290 231 -298 233 -296 263 -270 271 -268 231 -296 231 -262 257 -256 291 -262 267 -268 231 -298 261 -270 271 -268 231 -264 261 -262 257 -258 293 -262 267 -274 273 -270 233 -262 261 -262 257 -258 291 -264 233 -300 231 -296 263 -234 299 -266 235 -300 233 -262 261 -262 257 -290 231 -268 259 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 255 -258 291 -262 267 -268 265 -270 271 -236 259 -260 259 -260 293 -264 267 -268 233 -262 295 -268 265 -266 235 -300 231 -264 261 -294 229 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -264 257 -290 231 -298 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -294 231 -294 231 -262 295 -268 271 -270 231 -264 261 -294 229 -296 229 -264 255 -292 231 -300 231 -296 263 -268 -RAW_Data: 271 -270 231 -296 229 -264 259 -296 229 -296 229 -294 263 -270 271 -268 231 -296 231 -262 257 -290 231 -266 259 -292 233 -298 231 -296 263 -236 299 -232 295 -230 293 -262 235 -300 265 -270 271 -268 233 -294 229 -294 229 -264 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 261 -294 229 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -234 301 -266 233 -300 231 -296 229 -264 261 -262 257 -290 265 -272 273 -238 261 -292 231 -298 231 -296 229 -296 261 -268 267 -266 233 -300 231 -264 261 -262 257 -258 291 -230 293 -264 269 -272 269 -266 233 -300 231 -296 229 -264 261 -294 263 -268 273 -268 233 -262 263 -262 255 -258 293 -230 293 -264 233 -302 263 -270 273 -268 231 -264 257 -256 291 -262 267 -268 231 -298 267 -238 265 -294 231 -300 231 -262 259 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 261 -294 231 -294 231 -294 263 -268 273 -268 231 -296 231 -262 257 -288 231 -300 231 -296 231 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -294 263 -268 271 -270 233 -262 261 -262 257 -292 231 -266 259 -260 259 -296 233 -300 265 -234 299 -266 233 -302 231 -264 261 -262 255 -258 293 -262 269 -266 233 -262 295 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 269 -272 235 -300 229 -262 257 -288 231 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -294 231 -294 231 -294 229 -296 263 -234 299 -266 233 -300 231 -264 261 -294 263 -236 299 -232 295 -264 233 -300 231 -296 229 -264 255 -290 231 -268 259 -292 265 -272 269 -266 235 -300 229 -296 229 -296 229 -296 261 -270 271 -268 231 -296 231 -262 261 -294 229 -264 257 -290 231 -298 233 -296 263 -268 273 -268 231 -264 257 -290 231 -298 233 -296 231 -262 293 -268 273 -268 233 -264 261 -262 257 -290 231 -298 233 -296 231 -294 229 -264 293 -268 267 -266 235 -266 259 -260 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 235 -300 231 -264 257 -258 257 -294 267 -274 269 -268 233 -300 231 -296 229 -294 231 -294 229 -264 255 -292 265 -270 269 -266 235 -300 231 -296 229 -262 257 -258 259 -328 241 -276 239 -268 263 -260 261 -262 257 -290 -RAW_Data: 231 -300 265 -268 267 -234 261 -296 231 -300 233 -264 257 -258 257 -262 293 -264 269 -266 265 -270 271 -268 233 -296 229 -262 261 -296 261 -270 271 -270 231 -264 261 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -264 257 -288 231 -300 231 -296 271 -270 237 -298 231 -262 261 -262 255 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -296 229 -262 261 -294 229 -296 263 -268 273 -268 231 -264 263 -294 229 -294 229 -264 257 -290 231 -300 265 -236 299 -266 235 -298 231 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -298 261 -236 299 -264 235 -300 233 -262 261 -262 257 -290 231 -268 259 -292 231 -300 263 -270 273 -268 233 -262 261 -262 257 -292 231 -298 265 -270 271 -270 231 -264 261 -262 257 -290 231 -300 231 -296 231 -294 263 -234 301 -264 233 -300 231 -296 231 -294 229 -264 257 -290 265 -272 267 -268 235 -300 231 -262 257 -290 231 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -258 291 -264 267 -272 269 -266 235 -300 229 -296 229 -296 229 -296 261 -270 271 -268 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -270 231 -264 257 -256 291 -262 267 -268 233 -264 293 -268 271 -236 261 -292 231 -298 231 -298 229 -262 295 -234 299 -264 269 -268 231 -264 261 -262 257 -290 231 -298 233 -296 263 -270 271 -268 231 -296 231 -262 257 -256 291 -230 293 -264 267 -268 265 -236 299 -266 233 -300 231 -296 231 -262 257 -288 265 -272 275 -270 233 -264 261 -294 229 -294 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 233 -300 231 -264 257 -258 259 -296 267 -234 259 -292 231 -300 263 -236 299 -266 235 -300 231 -262 259 -290 229 -266 261 -292 265 -272 275 -268 233 -264 255 -258 291 -264 233 -302 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 265 -272 267 -268 233 -302 229 -296 229 -296 263 -234 299 -264 235 -300 231 -296 229 -264 255 -290 231 -300 231 -264 259 -290 265 -270 269 -266 235 -302 229 -264 257 -290 231 -298 231 -298 269 -272 235 -300 229 -262 261 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -264 257 -290 231 -298 233 -296 263 -270 271 -268 231 -264 261 -294 229 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 231 -298 265 -270 271 -270 233 -262 261 -262 257 -258 291 -264 233 -302 265 -234 -RAW_Data: 299 -266 233 -300 231 -296 229 -296 229 -262 257 -258 259 -296 267 -272 275 -270 231 -264 257 -256 291 -264 233 -300 231 -296 263 -236 299 -264 233 -300 231 -298 229 -262 257 -290 233 -298 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -236 299 -264 235 -266 259 -292 231 -300 231 -296 263 -236 299 -264 233 -302 231 -264 261 -262 257 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -262 257 -258 291 -262 267 -268 233 -296 229 -262 295 -268 271 -270 231 -264 257 -258 259 -292 269 -266 233 -264 295 -266 273 -268 233 -264 261 -262 261 -294 263 -268 267 -266 233 -302 229 -264 257 -290 231 -266 259 -294 265 -272 267 -268 233 -300 231 -296 229 -296 229 -262 289 -298 241 -272 235 -266 261 -262 255 -290 231 -268 259 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 229 -264 293 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -262 267 -268 231 -296 263 -234 299 -266 233 -302 231 -296 229 -262 261 -294 231 -294 231 -294 231 -294 263 -268 271 -270 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 265 -270 271 -268 233 -262 261 -294 231 -294 263 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -296 229 -262 287 -300 241 -274 233 -266 261 -262 255 -258 291 -264 267 -268 265 -234 299 -266 233 -302 231 -264 255 -290 231 -300 231 -298 229 -262 295 -268 271 -270 233 -264 259 -294 229 -296 263 -234 299 -266 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -268 271 -270 233 -264 261 -260 257 -290 231 -300 231 -298 229 -264 293 -268 271 -270 233 -264 261 -262 255 -258 291 -264 267 -274 273 -270 233 -262 261 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -298 229 -294 231 -262 261 -294 263 -234 301 -232 263 -262 293 -230 295 -264 233 -300 265 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 265 -272 267 -268 233 -302 231 -294 229 -264 255 -292 231 -298 265 -270 265 -266 235 -300 231 -294 231 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 -RAW_Data: 261 -262 257 -258 259 -328 239 -278 237 -270 261 -262 261 -294 229 -262 257 -292 231 -300 231 -296 263 -234 299 -266 233 -302 231 -264 255 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -262 257 -292 231 -266 259 -292 233 -298 265 -236 299 -266 235 -300 229 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -264 233 -300 265 -270 265 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 233 -262 261 -262 257 -258 291 -264 267 -274 275 -270 231 -264 261 -292 229 -264 257 -290 231 -300 231 -296 263 -236 299 -264 233 -300 231 -264 257 -258 261 -260 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -296 261 -268 267 -266 233 -300 231 -264 261 -262 257 -292 231 -298 231 -298 231 -262 293 -268 271 -270 233 -264 261 -262 255 -292 265 -272 275 -270 231 -264 261 -262 255 -290 265 -234 261 -292 231 -300 263 -270 271 -268 233 -296 229 -264 261 -294 229 -296 229 -262 295 -268 271 -270 233 -264 257 -256 289 -264 267 -268 265 -236 299 -264 235 -300 231 -264 261 -294 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 231 -294 229 -296 229 -296 261 -236 299 -266 233 -300 231 -296 229 -264 261 -294 263 -268 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -274 269 -266 235 -300 231 -294 229 -296 229 -296 229 -262 257 -292 229 -300 231 -298 263 -234 299 -264 235 -300 233 -262 261 -262 257 -258 291 -264 267 -268 265 -268 271 -236 261 -292 231 -264 261 -290 231 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 229 -296 263 -234 299 -264 235 -300 233 -262 261 -296 229 -262 257 -290 231 -300 231 -298 229 -264 293 -268 273 -268 233 -262 261 -262 257 -258 291 -298 239 -276 237 -302 229 -264 255 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -294 229 -296 229 -296 227 -296 261 -270 273 -268 233 -262 261 -294 231 -294 263 -236 297 -266 235 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 265 -234 301 -264 233 -300 231 -264 257 -258 259 -296 267 -266 233 -296 263 -268 271 -268 231 -296 231 -262 257 -290 231 -266 259 -292 265 -272 275 -270 231 -264 261 -294 229 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -234 299 -266 235 -300 -RAW_Data: 231 -296 229 -262 261 -296 229 -262 257 -292 265 -272 267 -266 235 -302 229 -264 255 -292 231 -298 265 -236 301 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 231 -262 261 -294 263 -268 273 -268 233 -262 261 -294 231 -262 257 -292 231 -298 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 263 -268 271 -268 233 -264 257 -290 229 -300 265 -270 265 -266 235 -300 231 -262 257 -258 291 -230 295 -262 235 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -304 241 -274 235 -266 261 -260 261 -262 257 -292 231 -268 259 -290 233 -298 265 -236 299 -266 235 -300 231 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 261 -236 299 -232 295 -262 235 -300 231 -262 257 -258 259 -296 267 -234 261 -292 229 -300 263 -236 301 -266 233 -300 231 -296 229 -294 229 -296 229 -264 255 -292 263 -272 269 -266 235 -300 231 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 231 -266 259 -260 259 -294 267 -268 265 -268 273 -268 231 -264 257 -258 257 -294 267 -268 233 -296 269 -270 237 -266 257 -258 257 -294 267 -268 231 -298 263 -234 299 -264 267 -268 231 -264 261 -294 229 -296 229 -296 229 -296 263 -268 271 -268 233 -296 229 -262 261 -294 263 -236 299 -264 235 -300 231 -296 229 -296 229 -296 229 -294 231 -262 293 -268 273 -268 233 -264 261 -294 229 -296 229 -296 229 -294 269 -272 235 -268 257 -258 257 -294 267 -268 231 -298 261 -236 297 -266 235 -300 231 -296 229 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -296 261 -236 299 -264 267 -268 231 -264 261 -262 257 -258 291 -264 233 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 271 -274 235 -266 263 -262 259 -296 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -258 259 -296 267 -266 233 -264 293 -268 271 -270 233 -264 255 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -262 261 -294 231 -294 231 -296 261 -270 271 -268 233 -264 261 -262 255 -258 291 -304 243 -274 235 -266 261 -262 259 -294 231 -262 -RAW_Data: 257 -290 265 -272 267 -234 295 -264 233 -300 231 -264 257 -290 231 -266 259 -294 231 -300 263 -236 301 -264 235 -300 231 -296 229 -262 257 -290 231 -266 259 -292 233 -298 265 -236 301 -266 233 -300 231 -296 229 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -260 257 -292 231 -298 233 -296 263 -236 299 -264 235 -266 259 -260 259 -294 267 -268 265 -236 299 -264 233 -300 231 -298 229 -262 257 -290 231 -266 259 -294 231 -298 265 -270 271 -270 231 -264 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 255 -258 291 -262 235 -268 259 -292 231 -300 263 -236 301 -266 233 -300 231 -262 257 -258 291 -264 233 -300 265 -268 273 -236 259 -260 259 -294 267 -266 233 -296 263 -234 299 -264 235 -300 231 -296 229 -262 261 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -290 231 -300 265 -268 267 -266 235 -300 231 -262 261 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -296 229 -296 261 -236 299 -264 267 -268 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -262 259 -258 259 -294 267 -268 231 -296 269 -270 237 -266 263 -260 261 -262 257 -290 231 -302 265 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -270 271 -268 233 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 231 -264 263 -260 257 -290 233 -298 271 -274 237 -266 261 -262 261 -262 255 -292 231 -300 265 -270 267 -266 233 -300 231 -262 261 -262 257 -258 293 -262 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 293 -234 299 -266 235 -300 231 -296 229 -264 261 -262 255 -292 231 -266 259 -294 265 -272 267 -266 235 -300 231 -262 257 -258 259 -262 293 -264 269 -268 263 -236 299 -264 235 -300 231 -264 257 -258 259 -292 269 -234 293 -266 275 -272 233 -266 261 -260 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -292 231 -266 259 -292 231 -300 265 -268 267 -232 263 -294 233 -300 231 -264 257 -258 291 -230 293 -264 267 -268 265 -270 271 -268 231 -264 261 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -262 257 -290 265 -234 259 -294 231 -298 265 -268 273 -268 231 -264 261 -262 257 -258 291 -304 -RAW_Data: 243 -274 235 -264 263 -260 261 -294 229 -264 257 -290 231 -300 265 -236 299 -266 235 -298 231 -296 229 -264 255 -258 293 -262 267 -266 265 -270 273 -234 261 -292 229 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 263 -268 273 -268 231 -296 229 -264 261 -294 269 -272 235 -300 229 -264 259 -294 229 -296 229 -296 263 -234 299 -232 295 -264 267 -266 233 -262 257 -290 231 -300 231 -264 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -268 231 -264 293 -268 267 -232 263 -262 293 -264 267 -268 231 -264 293 -268 271 -270 233 -262 261 -294 229 -296 231 -294 229 -296 263 -234 299 -264 235 -300 233 -262 261 -262 257 -258 293 -262 233 -268 261 -292 231 -298 265 -236 299 -266 233 -300 231 -296 229 -264 261 -294 263 -268 271 -270 233 -264 261 -260 257 -258 291 -264 233 -268 259 -292 231 -300 263 -270 273 -268 231 -296 231 -262 261 -262 257 -290 231 -300 271 -240 265 -294 231 -266 259 -260 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -236 299 -266 235 -298 231 -264 261 -296 261 -236 299 -266 235 -300 231 -294 229 -296 229 -264 259 -262 257 -292 231 -298 265 -270 273 -270 231 -264 261 -294 229 -296 229 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -262 257 -290 231 -300 231 -264 289 -264 269 -270 271 -268 231 -262 257 -290 231 -298 231 -266 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -292 231 -298 231 -296 231 -294 263 -234 299 -266 235 -300 231 -262 259 -256 291 -262 269 -268 231 -264 293 -268 271 -270 233 -264 261 -262 255 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -262 263 -294 261 -268 267 -266 235 -300 231 -262 263 -294 229 -262 259 -290 229 -266 261 -292 231 -300 263 -270 273 -268 233 -262 263 -260 257 -292 231 -298 233 -296 269 -270 237 -298 231 -262 261 -262 257 -290 265 -272 267 -268 235 -300 231 -294 231 -294 229 -264 257 -290 231 -300 263 -236 301 -266 233 -302 229 -264 261 -294 263 -268 267 -264 235 -300 231 -264 261 -262 257 -258 259 -294 267 -268 231 -264 263 -294 263 -234 299 -266 233 -300 231 -296 231 -294 229 -262 257 -290 231 -300 233 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -260 259 -296 267 -266 265 -236 299 -264 -RAW_Data: 235 -300 231 -296 229 -264 261 -294 263 -234 301 -266 233 -300 231 -264 257 -290 231 -264 261 -260 259 -294 267 -268 265 -268 273 -268 231 -264 261 -294 229 -296 229 -296 229 -296 263 -268 271 -236 259 -260 259 -294 267 -266 267 -234 299 -232 263 -294 267 -268 231 -264 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 233 -300 231 -298 229 -262 295 -232 301 -266 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -236 299 -264 233 -300 231 -264 257 -292 231 -264 261 -292 265 -272 273 -238 261 -292 229 -266 259 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -236 299 -264 235 -300 233 -262 257 -256 293 -230 293 -264 233 -300 265 -236 301 -264 233 -300 231 -296 231 -262 257 -290 265 -272 267 -266 235 -302 229 -296 229 -294 229 -264 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -268 271 -268 233 -264 257 -290 231 -266 259 -292 231 -300 271 -274 235 -266 259 -256 257 -262 293 -264 269 -268 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -270 271 -268 231 -298 229 -262 261 -294 263 -234 301 -266 235 -300 229 -296 229 -296 229 -262 257 -258 259 -296 267 -266 265 -270 271 -270 231 -264 261 -294 229 -296 229 -296 267 -272 235 -300 229 -264 259 -262 257 -258 291 -262 269 -266 265 -236 301 -264 233 -300 231 -264 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -296 261 -236 299 -232 295 -262 233 -300 231 -298 229 -264 255 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 259 -262 257 -292 231 -300 231 -296 263 -236 299 -264 233 -302 229 -298 229 -262 257 -292 231 -266 259 -292 265 -272 267 -268 233 -300 231 -262 259 -258 259 -260 295 -262 269 -268 265 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 233 -268 259 -292 231 -300 265 -268 273 -268 233 -262 261 -262 257 -290 233 -298 231 -298 269 -238 265 -260 261 -292 267 -234 261 -292 231 -298 265 -268 267 -266 235 -298 231 -264 261 -262 257 -258 293 -262 267 -266 265 -236 299 -266 233 -302 231 -262 261 -296 261 -236 299 -266 235 -300 231 -262 257 -290 231 -268 259 -292 231 -300 263 -236 301 -266 233 -300 231 -264 261 -262 257 -258 291 -262 267 -268 265 -268 273 -268 233 -262 261 -296 229 -296 229 -262 257 -290 231 -300 263 -270 -RAW_Data: 267 -266 235 -300 229 -264 257 -258 259 -294 267 -234 261 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -270 271 -268 231 -298 229 -262 257 -290 231 -298 265 -236 301 -266 235 -300 229 -296 229 -264 259 -262 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 263 -234 299 -264 269 -266 233 -262 261 -294 231 -262 257 -292 231 -298 231 -296 263 -270 271 -268 233 -264 261 -294 229 -262 257 -258 293 -294 241 -276 239 -268 263 -260 257 -288 231 -300 231 -298 263 -268 271 -270 231 -296 229 -264 255 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 257 -290 263 -272 269 -266 235 -300 231 -296 231 -262 257 -256 291 -262 267 -234 261 -292 265 -272 267 -266 235 -300 231 -264 257 -290 231 -298 231 -298 231 -262 293 -234 299 -266 235 -302 231 -262 257 -256 293 -262 235 -266 259 -292 265 -272 269 -266 233 -268 259 -292 231 -298 265 -270 265 -266 235 -300 231 -294 231 -262 257 -290 231 -266 259 -292 231 -300 265 -236 301 -264 235 -298 231 -298 229 -262 257 -290 231 -266 259 -292 233 -298 265 -236 301 -266 233 -300 229 -264 257 -290 265 -272 275 -270 231 -264 261 -262 255 -258 293 -262 269 -272 269 -266 235 -300 231 -262 261 -262 257 -290 231 -266 261 -292 231 -300 265 -268 273 -268 233 -262 257 -290 231 -298 233 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -264 257 -290 231 -300 265 -268 267 -266 235 -300 231 -262 261 -294 263 -268 267 -266 233 -300 231 -296 229 -264 255 -258 293 -262 267 -266 233 -264 293 -268 271 -270 233 -264 261 -262 255 -258 291 -264 267 -268 271 -274 235 -264 263 -262 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -266 233 -262 261 -294 231 -262 257 -290 265 -272 267 -268 235 -300 231 -262 257 -258 259 -294 267 -236 259 -292 231 -300 263 -236 301 -266 233 -300 231 -262 257 -292 229 -300 231 -296 263 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -230 293 -264 269 -266 265 -236 299 -264 235 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 231 -296 263 -268 271 -270 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -294 231 -294 229 -264 257 -258 259 -294 267 -268 265 -268 273 -268 231 -264 261 -262 257 -290 233 -266 259 -326 237 -276 237 -268 257 -290 231 -264 261 -292 -RAW_Data: 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -262 257 -290 265 -274 267 -268 233 -300 231 -296 229 -264 261 -294 263 -268 267 -266 233 -300 231 -264 261 -294 229 -264 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -294 231 -294 229 -296 261 -268 267 -266 233 -300 231 -298 229 -262 261 -262 257 -290 265 -272 275 -270 233 -264 255 -290 231 -298 233 -296 263 -236 299 -264 233 -300 231 -298 229 -294 229 -262 257 -290 233 -300 265 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -300 231 -264 259 -290 229 -300 265 -270 267 -264 235 -300 231 -294 231 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -292 231 -298 231 -296 263 -270 271 -268 233 -264 261 -262 261 -294 261 -270 273 -268 233 -264 261 -294 229 -262 257 -292 231 -266 259 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -264 261 -262 257 -290 265 -272 275 -270 233 -262 257 -258 257 -294 267 -274 269 -266 235 -300 231 -296 229 -294 229 -264 257 -290 231 -300 263 -236 301 -266 233 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 261 -262 257 -292 231 -298 265 -270 273 -268 233 -262 261 -262 257 -258 293 -262 235 -266 259 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 227 -262 257 -292 231 -300 231 -298 263 -268 271 -268 231 -264 263 -260 257 -290 233 -298 265 -270 273 -268 231 -264 261 -294 229 -296 229 -264 255 -292 231 -298 265 -236 301 -266 233 -300 231 -264 257 -256 293 -262 233 -302 263 -236 299 -234 261 -262 293 -264 233 -302 231 -264 293 -234 301 -266 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 291 -270 271 -270 235 -302 231 -264 261 -262 255 -258 291 -264 269 -266 265 -236 299 -264 235 -300 231 -294 231 -262 257 -258 259 -294 267 -274 269 -266 235 -300 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -262 257 -292 231 -266 259 -292 265 -272 269 -266 235 -300 229 -296 229 -296 229 -296 227 -296 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 265 -270 273 -268 233 -262 261 -294 229 -264 257 -292 231 -298 231 -296 263 -236 299 -264 235 -300 233 -262 257 -258 259 -294 267 -266 233 -264 295 -232 299 -266 235 -300 231 -298 229 -262 261 -294 229 -264 257 -292 229 -300 263 -270 -RAW_Data: 273 -236 261 -292 231 -298 231 -296 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 265 -266 235 -300 231 -262 263 -294 261 -236 299 -264 235 -302 231 -264 261 -294 229 -262 259 -256 291 -262 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 257 -258 257 -328 239 -278 239 -268 257 -258 257 -294 267 -268 231 -264 295 -234 297 -266 235 -302 231 -262 257 -292 229 -300 231 -298 263 -234 299 -264 235 -300 233 -262 257 -290 265 -272 267 -234 263 -296 233 -298 231 -298 229 -262 257 -258 291 -264 233 -300 231 -296 263 -268 273 -268 231 -264 257 -290 231 -298 231 -298 229 -264 293 -268 267 -266 233 -300 231 -264 261 -262 257 -292 231 -298 233 -296 263 -268 271 -268 233 -296 229 -264 255 -290 231 -300 231 -296 265 -234 299 -266 233 -300 231 -264 257 -290 231 -298 231 -264 259 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 227 -296 261 -270 273 -268 233 -262 261 -262 257 -258 291 -264 233 -268 259 -292 265 -272 273 -272 231 -264 261 -294 229 -296 229 -296 261 -268 271 -270 231 -266 255 -290 231 -266 259 -260 261 -294 267 -266 265 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 265 -272 267 -268 235 -268 259 -290 231 -300 231 -296 263 -268 271 -268 233 -296 229 -264 259 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -262 257 -292 231 -298 265 -270 271 -270 233 -262 261 -294 229 -264 257 -258 259 -294 267 -266 233 -296 263 -268 271 -270 231 -264 257 -258 257 -294 267 -268 233 -296 263 -268 271 -268 231 -298 229 -262 261 -294 231 -294 231 -294 261 -268 267 -232 263 -294 267 -236 259 -292 231 -266 259 -292 265 -270 269 -266 235 -302 229 -296 229 -296 229 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 233 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 233 -262 261 -294 229 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 267 -268 233 -262 257 -290 231 -264 261 -292 265 -272 275 -236 261 -292 231 -264 259 -292 233 -298 265 -236 301 -264 233 -300 231 -296 229 -264 255 -292 231 -266 259 -292 233 -298 265 -236 299 -266 235 -300 229 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -270 271 -268 -RAW_Data: 231 -264 261 -296 229 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -296 229 -296 229 -262 289 -298 241 -274 235 -264 261 -262 261 -262 255 -292 231 -300 263 -270 267 -266 235 -300 231 -294 229 -264 257 -258 259 -260 295 -262 269 -268 265 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -236 261 -292 231 -264 261 -290 231 -300 265 -234 301 -266 235 -298 231 -296 229 -296 229 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 287 -266 269 -268 271 -268 231 -264 259 -262 257 -258 291 -264 233 -302 231 -264 293 -234 301 -266 235 -300 229 -296 229 -264 255 -292 231 -300 229 -298 269 -238 265 -294 231 -300 231 -294 229 -296 263 -268 271 -268 233 -264 261 -262 255 -292 231 -266 259 -294 265 -272 267 -268 233 -300 231 -296 229 -296 261 -268 267 -266 233 -300 231 -264 261 -262 257 -258 291 -264 233 -268 259 -292 231 -300 263 -270 273 -268 231 -264 257 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -298 231 -264 261 -296 229 -262 257 -290 233 -298 265 -270 265 -234 261 -262 293 -232 293 -264 235 -300 263 -236 299 -266 235 -300 229 -296 229 -264 255 -258 291 -230 295 -264 267 -274 267 -266 235 -300 231 -294 229 -296 229 -262 257 -292 231 -266 259 -292 231 -300 265 -236 299 -266 235 -298 231 -264 257 -290 231 -300 265 -270 271 -268 233 -262 261 -262 257 -290 233 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 229 -262 257 -258 259 -328 241 -276 239 -268 263 -262 255 -256 291 -264 233 -302 265 -234 299 -232 295 -264 233 -300 231 -296 229 -264 255 -258 291 -264 233 -302 265 -268 271 -270 231 -264 257 -288 231 -300 263 -270 273 -270 231 -264 261 -262 255 -292 263 -268 231 -298 263 -234 299 -264 235 -300 231 -264 257 -290 231 -298 231 -298 269 -272 235 -266 259 -256 257 -262 293 -264 269 -266 233 -264 293 -234 299 -266 235 -300 231 -296 229 -264 255 -258 291 -230 295 -262 269 -272 269 -232 263 -294 233 -302 231 -296 229 -264 259 -262 257 -290 233 -298 231 -298 263 -234 301 -264 235 -300 231 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 261 -260 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 293 -268 267 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -260 -RAW_Data: 261 -294 267 -272 269 -266 235 -300 229 -264 257 -290 231 -298 233 -296 269 -272 235 -300 229 -264 259 -262 257 -290 231 -300 265 -270 267 -264 235 -300 231 -296 229 -262 257 -258 259 -294 267 -274 267 -268 235 -300 229 -296 229 -296 261 -236 299 -232 295 -264 233 -300 231 -296 229 -264 261 -260 257 -258 293 -262 235 -300 263 -270 273 -268 233 -262 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 235 -300 231 -262 261 -296 229 -294 231 -294 231 -262 295 -268 271 -270 231 -264 263 -260 261 -294 229 -296 231 -294 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -264 255 -258 291 -264 233 -302 265 -234 299 -266 233 -268 259 -292 231 -298 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -258 259 -260 295 -264 267 -274 269 -266 235 -300 231 -262 257 -258 291 -230 295 -304 241 -274 235 -264 257 -256 259 -294 267 -234 261 -292 231 -298 265 -236 299 -266 235 -300 229 -264 257 -258 259 -262 293 -262 269 -268 265 -268 273 -234 261 -292 231 -298 231 -296 263 -268 271 -268 233 -264 261 -294 229 -296 229 -296 229 -296 261 -236 299 -264 267 -268 231 -264 261 -294 229 -296 263 -268 273 -268 231 -264 261 -262 257 -292 231 -266 259 -292 231 -300 231 -296 263 -234 299 -266 233 -302 231 -264 255 -258 291 -264 267 -268 231 -264 293 -268 267 -266 233 -300 231 -296 229 -264 255 -258 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -296 229 -264 255 -290 231 -300 231 -296 231 -294 263 -268 273 -268 231 -296 231 -262 257 -256 291 -230 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -270 271 -270 231 -262 261 -296 229 -296 261 -236 299 -266 233 -302 231 -264 261 -262 255 -258 293 -228 293 -264 269 -272 269 -266 235 -300 231 -294 231 -262 261 -294 263 -268 273 -268 233 -296 227 -262 257 -258 293 -262 235 -300 265 -236 299 -264 235 -300 231 -262 257 -258 261 -294 267 -266 233 -264 293 -268 271 -270 233 -264 261 -294 229 -294 263 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -230 295 -264 233 -300 265 -268 273 -268 233 -262 261 -262 257 -258 291 -262 269 -266 233 -296 263 -268 271 -268 233 -264 261 -262 255 -292 231 -266 291 -302 239 -274 235 -264 261 -262 261 -294 229 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 229 -264 -RAW_Data: 255 -258 259 -294 267 -268 231 -298 261 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -298 263 -234 299 -232 295 -264 233 -268 259 -292 229 -300 263 -270 267 -266 235 -300 229 -296 229 -262 257 -258 259 -260 295 -264 267 -274 269 -266 235 -300 231 -262 257 -258 291 -268 273 -270 237 -300 231 -294 229 -296 229 -264 255 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -268 231 -296 269 -238 263 -296 231 -266 259 -292 231 -298 265 -270 273 -268 231 -264 261 -294 229 -296 229 -296 229 -296 229 -296 261 -270 271 -268 231 -296 231 -262 289 -264 267 -270 271 -268 231 -264 261 -292 229 -264 257 -258 291 -264 233 -300 231 -296 263 -268 273 -268 231 -264 261 -296 229 -294 231 -294 229 -296 263 -234 299 -264 235 -300 233 -262 261 -262 257 -292 231 -298 231 -298 229 -296 261 -270 271 -236 259 -260 259 -292 267 -268 265 -236 301 -230 295 -264 233 -300 231 -262 259 -290 231 -266 259 -260 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 261 -294 229 -296 229 -264 293 -268 267 -266 233 -300 231 -264 257 -290 231 -300 263 -270 273 -268 233 -294 229 -262 257 -258 291 -264 233 -268 261 -292 263 -272 267 -268 235 -300 229 -296 229 -296 229 -296 229 -294 269 -272 235 -266 259 -290 231 -264 261 -258 261 -294 267 -266 265 -236 299 -264 235 -300 233 -262 257 -258 291 -262 235 -300 233 -262 295 -268 271 -270 231 -264 261 -294 229 -296 261 -270 271 -270 231 -264 261 -294 229 -264 255 -292 231 -300 231 -296 263 -268 271 -270 231 -264 257 -258 257 -262 293 -296 241 -276 239 -302 231 -262 261 -260 257 -258 291 -264 233 -300 265 -234 301 -232 263 -296 231 -300 231 -264 257 -290 233 -298 231 -296 231 -294 263 -236 299 -264 235 -300 231 -262 257 -260 259 -294 267 -234 259 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 233 -300 265 -236 299 -234 261 -296 233 -298 231 -264 257 -258 293 -228 295 -262 269 -272 269 -266 235 -300 231 -294 231 -294 263 -234 299 -266 233 -302 231 -264 261 -262 255 -258 293 -262 235 -268 259 -292 231 -298 265 -268 273 -268 233 -264 255 -258 291 -230 293 -296 241 -276 239 -268 263 -262 255 -290 231 -298 231 -298 263 -234 301 -264 233 -302 231 -264 261 -294 229 -296 229 -296 229 -294 261 -270 273 -268 233 -262 261 -296 -RAW_Data: 229 -296 261 -236 299 -266 233 -300 231 -264 261 -262 257 -290 231 -268 259 -292 231 -300 265 -268 273 -268 233 -262 261 -296 229 -296 229 -294 231 -262 293 -268 267 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -290 265 -272 275 -270 233 -264 255 -290 229 -268 259 -292 231 -300 265 -268 267 -266 235 -300 231 -262 261 -294 229 -264 257 -290 231 -300 231 -296 263 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 229 -296 263 -234 299 -264 235 -300 233 -262 257 -290 231 -300 263 -236 301 -266 235 -298 231 -296 229 -264 261 -262 255 -292 231 -300 263 -270 267 -266 235 -300 231 -294 229 -296 229 -296 261 -270 271 -268 231 -298 229 -262 257 -258 291 -262 235 -300 231 -264 295 -232 301 -266 233 -300 231 -264 257 -290 231 -298 233 -296 231 -262 293 -268 273 -270 231 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -262 257 -292 229 -266 261 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 255 -258 291 -264 267 -234 261 -290 231 -300 263 -236 301 -266 233 -300 231 -262 257 -258 259 -262 293 -264 269 -266 265 -270 271 -268 233 -262 257 -290 231 -300 231 -264 257 -292 229 -300 265 -270 265 -234 261 -296 233 -300 231 -264 295 -234 297 -266 269 -268 231 -262 261 -262 257 -292 231 -298 233 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 265 -266 233 -296 263 -234 299 -264 235 -300 233 -262 257 -290 231 -300 265 -234 301 -266 235 -300 229 -296 229 -262 257 -290 231 -268 259 -292 231 -300 265 -268 273 -268 233 -262 257 -258 291 -262 267 -266 233 -296 269 -272 235 -266 257 -292 229 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -298 229 -264 293 -268 273 -268 233 -264 261 -262 261 -294 263 -268 267 -266 235 -300 229 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -264 261 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -296 227 -262 257 -258 293 -262 269 -272 269 -266 233 -300 231 -296 229 -262 259 -256 293 -262 267 -266 265 -236 299 -232 295 -264 233 -300 231 -264 257 -290 231 -266 259 -260 259 -296 267 -266 265 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -290 265 -272 267 -268 235 -266 259 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 265 -272 -RAW_Data: 267 -268 235 -300 231 -294 229 -296 263 -234 299 -266 267 -268 231 -262 261 -262 257 -292 231 -266 259 -260 259 -296 267 -266 265 -270 271 -270 231 -264 259 -296 229 -262 257 -292 231 -298 271 -274 235 -268 257 -258 257 -294 267 -266 267 -236 299 -264 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -298 229 -262 255 -290 265 -272 269 -266 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 231 -296 231 -262 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -298 229 -262 295 -234 299 -266 235 -300 231 -294 231 -294 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 229 -264 255 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 261 -294 229 -264 257 -290 265 -272 267 -268 233 -300 231 -264 257 -258 259 -292 269 -268 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 229 -298 263 -268 271 -270 231 -264 261 -296 229 -262 289 -266 273 -274 233 -266 261 -262 255 -290 231 -300 231 -264 257 -292 231 -298 265 -270 273 -268 233 -262 261 -294 229 -264 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 229 -264 257 -256 293 -228 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -296 229 -262 261 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -258 259 -260 295 -262 269 -268 265 -234 299 -232 295 -262 235 -300 231 -296 229 -264 255 -258 291 -264 267 -268 265 -268 271 -268 233 -262 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -294 231 -262 261 -262 289 -300 241 -272 235 -264 261 -262 261 -262 257 -258 261 -294 267 -266 265 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 271 -236 261 -260 259 -294 265 -268 265 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -296 231 -262 255 -292 265 -270 275 -270 233 -264 261 -294 229 -262 257 -258 291 -262 267 -268 233 -264 293 -234 299 -266 235 -300 231 -264 257 -290 231 -266 259 -292 265 -272 267 -268 235 -300 231 -294 229 -296 229 -296 229 -296 229 -294 231 -294 263 -268 271 -236 261 -292 231 -298 265 -268 273 -268 233 -262 257 -290 231 -266 259 -292 233 -298 231 -296 263 -234 301 -264 235 -300 231 -296 229 -296 229 -262 257 -290 -RAW_Data: 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 257 -258 257 -260 295 -264 267 -268 265 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -266 257 -290 231 -298 265 -270 273 -268 233 -262 261 -296 229 -296 229 -294 231 -294 269 -238 263 -296 231 -266 259 -260 259 -292 267 -268 265 -236 301 -264 233 -300 231 -296 231 -262 261 -294 229 -296 231 -294 263 -268 271 -270 231 -296 229 -264 261 -294 263 -234 301 -264 235 -300 231 -294 231 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -264 261 -262 257 -292 231 -266 259 -292 231 -300 265 -268 273 -268 233 -262 263 -260 257 -258 291 -264 267 -268 231 -264 295 -234 297 -266 269 -268 231 -262 257 -290 231 -266 259 -294 231 -298 231 -296 263 -236 299 -266 235 -300 229 -264 257 -258 291 -262 235 -266 259 -292 231 -300 263 -270 267 -266 235 -300 229 -264 257 -258 259 -262 293 -264 267 -268 265 -268 273 -268 231 -264 261 -262 255 -292 231 -300 263 -270 267 -266 235 -300 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -296 229 -262 295 -268 267 -264 235 -300 231 -296 229 -262 257 -258 259 -294 267 -234 261 -292 231 -298 265 -270 271 -270 231 -264 261 -262 255 -292 231 -300 231 -296 271 -270 235 -266 259 -290 231 -264 261 -292 265 -272 267 -266 235 -300 231 -296 229 -264 261 -260 257 -292 231 -298 265 -236 301 -266 235 -300 229 -296 229 -294 263 -268 267 -266 235 -300 229 -296 229 -264 255 -292 231 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -274 267 -268 233 -300 231 -296 229 -294 229 -296 229 -262 257 -290 231 -300 265 -268 273 -270 231 -264 255 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -264 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -260 291 -262 267 -268 231 -296 263 -234 299 -266 233 -302 229 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -296 229 -264 255 -290 233 -298 231 -296 263 -270 271 -268 233 -296 229 -262 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 257 -256 291 -264 233 -300 231 -298 261 -236 299 -264 235 -300 231 -264 257 -258 289 -264 267 -268 265 -236 299 -264 233 -268 259 -292 231 -300 263 -270 267 -266 233 -300 231 -264 255 -290 233 -266 259 -292 233 -298 265 -270 271 -270 231 -262 261 -262 257 -292 231 -298 233 -264 257 -290 231 -298 265 -270 267 -266 -RAW_Data: 233 -300 231 -264 261 -262 257 -258 291 -264 267 -274 267 -266 235 -300 229 -296 229 -264 257 -290 231 -298 233 -296 231 -262 293 -236 299 -266 235 -300 229 -296 229 -296 229 -262 257 -258 291 -264 233 -300 265 -236 299 -266 235 -300 229 -264 257 -258 259 -294 267 -268 231 -296 269 -272 235 -298 231 -262 257 -256 291 -262 269 -268 265 -234 299 -266 233 -300 231 -264 261 -296 229 -296 229 -296 229 -262 293 -268 273 -270 231 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -258 291 -264 267 -268 265 -268 271 -270 231 -264 261 -294 229 -296 229 -296 267 -272 237 -298 231 -262 261 -262 257 -258 259 -294 267 -268 231 -264 295 -234 299 -264 269 -268 231 -264 255 -290 231 -298 233 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -266 233 -300 231 -264 257 -258 257 -296 267 -266 265 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -296 229 -262 261 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -302 231 -264 261 -294 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 257 -290 231 -298 231 -298 229 -264 293 -268 273 -236 261 -258 259 -260 295 -262 235 -300 263 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -256 293 -262 235 -300 265 -270 271 -268 231 -296 231 -262 255 -290 265 -272 269 -266 235 -300 231 -264 257 -290 231 -266 259 -292 231 -300 265 -268 273 -268 233 -262 261 -262 257 -258 293 -228 295 -296 241 -276 239 -268 263 -260 261 -262 255 -292 265 -266 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 265 -236 301 -264 233 -300 231 -296 231 -262 257 -256 293 -262 233 -302 263 -270 273 -268 231 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 229 -264 257 -258 259 -294 267 -268 231 -296 263 -268 273 -268 231 -264 261 -262 255 -292 231 -266 259 -326 239 -276 237 -300 231 -262 255 -290 231 -298 231 -298 263 -268 273 -268 231 -296 231 -262 261 -294 229 -296 229 -296 261 -268 267 -266 233 -300 231 -264 -RAW_Data: 261 -296 261 -236 299 -266 235 -300 231 -294 229 -264 257 -256 293 -262 233 -268 259 -292 265 -272 267 -268 235 -300 231 -294 229 -296 229 -264 259 -296 261 -270 267 -232 263 -294 233 -300 231 -264 257 -290 231 -300 231 -264 259 -290 263 -272 273 -238 261 -260 259 -292 267 -268 231 -264 295 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 229 -264 293 -268 273 -268 233 -264 257 -256 291 -262 269 -272 269 -266 235 -300 231 -262 261 -294 231 -294 231 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 257 -290 231 -266 293 -300 241 -272 235 -264 261 -262 261 -262 255 -260 291 -264 267 -268 265 -234 299 -266 233 -300 231 -264 257 -290 231 -300 231 -298 263 -234 299 -264 233 -302 231 -264 255 -290 231 -300 265 -270 271 -270 231 -264 261 -262 257 -290 231 -268 259 -292 265 -272 267 -268 233 -300 231 -264 257 -258 291 -228 295 -304 243 -274 235 -264 263 -260 257 -256 291 -264 233 -302 231 -264 293 -234 301 -266 233 -300 231 -296 231 -262 261 -294 229 -296 229 -296 263 -234 299 -264 235 -300 233 -262 261 -296 229 -296 229 -262 257 -290 231 -300 263 -270 267 -232 263 -294 233 -300 231 -264 257 -292 231 -298 233 -296 231 -262 293 -268 267 -266 233 -300 231 -264 257 -258 291 -262 267 -266 233 -296 263 -234 299 -264 235 -300 231 -296 229 -264 257 -256 293 -262 233 -302 263 -270 267 -266 233 -300 231 -294 229 -296 263 -236 299 -264 233 -300 231 -298 229 -262 257 -290 231 -266 261 -292 231 -300 263 -270 271 -270 231 -264 261 -294 229 -264 257 -258 291 -302 243 -274 235 -266 255 -290 231 -264 261 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 261 -294 229 -296 263 -234 299 -264 235 -300 231 -296 229 -296 263 -234 299 -232 295 -264 267 -266 231 -264 261 -296 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -296 229 -296 229 -262 261 -296 261 -270 265 -266 235 -300 231 -294 231 -294 231 -262 257 -288 265 -272 275 -270 233 -264 257 -290 229 -266 261 -260 259 -294 233 -300 265 -236 299 -266 235 -300 231 -262 263 -260 257 -292 231 -266 259 -294 231 -298 265 -268 267 -266 235 -300 231 -294 229 -296 229 -294 229 -296 229 -296 263 -268 271 -236 261 -260 257 -262 293 -262 269 -274 267 -232 295 -264 233 -300 231 -296 229 -264 255 -292 231 -266 259 -292 -RAW_Data: 233 -298 265 -236 301 -264 233 -300 231 -296 229 -296 229 -296 261 -270 271 -268 233 -264 261 -294 229 -262 257 -258 259 -296 267 -266 265 -236 299 -266 233 -300 231 -264 257 -290 231 -298 231 -266 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -274 269 -266 235 -300 231 -294 231 -262 257 -258 259 -294 267 -268 231 -296 263 -234 299 -266 233 -302 231 -264 261 -294 229 -262 257 -292 231 -298 231 -298 261 -236 299 -266 235 -300 231 -294 231 -294 229 -264 255 -290 231 -300 265 -270 271 -236 261 -292 231 -298 231 -296 231 -294 263 -234 299 -266 233 -302 231 -264 261 -294 229 -262 259 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -262 295 -268 265 -266 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -234 301 -266 233 -300 231 -296 229 -262 257 -258 291 -296 241 -276 237 -268 259 -256 257 -294 267 -268 231 -298 263 -268 271 -268 231 -264 263 -260 257 -290 231 -268 259 -292 265 -272 269 -266 235 -300 231 -262 257 -290 265 -272 267 -234 263 -296 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -234 299 -266 267 -268 231 -262 257 -290 231 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -264 261 -262 257 -290 231 -266 261 -292 265 -272 273 -270 233 -264 261 -294 229 -294 229 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -230 295 -264 235 -300 263 -236 299 -266 235 -300 229 -296 229 -264 261 -262 257 -290 231 -300 265 -270 265 -266 235 -300 231 -262 257 -290 233 -298 265 -270 271 -270 231 -296 227 -262 257 -292 231 -300 231 -264 257 -290 265 -272 273 -272 233 -262 261 -294 229 -296 229 -296 261 -268 273 -268 233 -264 257 -290 231 -264 261 -258 261 -294 267 -268 265 -234 301 -264 235 -300 231 -296 229 -262 257 -258 259 -294 267 -268 265 -268 273 -268 231 -264 261 -294 229 -296 263 -236 299 -230 295 -264 235 -300 229 -264 257 -290 233 -266 259 -292 265 -270 269 -266 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 263 -234 299 -264 235 -300 231 -296 229 -264 257 -290 231 -298 231 -298 263 -268 271 -268 233 -264 261 -262 255 -292 231 -266 259 -294 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -296 229 -296 261 -270 271 -268 231 -264 257 -258 257 -294 269 -234 261 -292 -RAW_Data: 231 -298 263 -236 301 -266 233 -300 231 -296 231 -262 259 -296 229 -296 263 -234 299 -266 233 -268 259 -292 231 -298 231 -298 261 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 265 -272 269 -266 235 -300 231 -264 255 -290 265 -272 267 -266 235 -302 231 -264 261 -262 255 -258 293 -262 267 -234 261 -292 231 -298 265 -268 273 -270 231 -264 255 -290 231 -266 259 -294 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 231 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -262 257 -258 291 -262 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 255 -258 291 -230 295 -264 233 -300 265 -234 301 -266 233 -300 231 -264 257 -258 257 -296 267 -272 269 -266 235 -300 231 -264 261 -294 229 -296 229 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -294 229 -264 257 -258 291 -294 241 -276 239 -268 263 -262 261 -294 229 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 233 -298 231 -298 263 -234 299 -264 269 -266 233 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -294 229 -262 257 -292 231 -266 259 -292 265 -272 267 -268 235 -300 231 -262 257 -290 231 -266 261 -292 231 -298 265 -270 271 -270 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -268 273 -268 231 -296 231 -262 257 -256 291 -264 267 -274 267 -266 235 -300 231 -294 231 -294 229 -296 229 -296 229 -296 261 -236 299 -264 233 -300 231 -264 257 -292 231 -298 265 -236 301 -266 233 -300 231 -264 255 -290 231 -266 259 -294 231 -300 263 -270 273 -268 233 -262 261 -262 257 -290 233 -298 231 -264 259 -290 231 -300 263 -270 273 -268 231 -264 261 -262 257 -290 231 -300 231 -296 263 -270 271 -268 233 -264 255 -290 231 -298 233 -264 257 -292 231 -298 265 -236 301 -264 235 -300 229 -296 229 -264 257 -290 231 -298 265 -270 267 -232 263 -262 293 -264 233 -300 265 -234 301 -266 233 -300 231 -296 229 -264 255 -290 231 -266 261 -292 265 -272 275 -268 233 -264 261 -260 257 -258 293 -228 295 -304 243 -274 235 -264 263 -260 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -290 233 -298 265 -270 273 -268 231 -264 261 -294 229 -296 229 -264 293 -268 267 -266 233 -300 231 -264 261 -262 257 -292 231 -266 259 -292 267 -270 269 -266 235 -300 231 -294 231 -262 257 -290 231 -266 259 -294 -RAW_Data: 231 -300 263 -270 273 -268 231 -264 261 -294 229 -296 261 -236 299 -266 233 -302 231 -264 255 -290 231 -298 231 -298 231 -294 263 -234 299 -266 267 -268 231 -264 261 -260 257 -292 231 -266 259 -260 261 -294 267 -266 265 -270 271 -270 231 -264 261 -260 257 -258 293 -262 235 -300 263 -270 273 -236 259 -292 231 -266 259 -292 231 -298 265 -270 267 -266 233 -300 229 -296 231 -262 257 -290 231 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 295 -234 299 -266 267 -268 233 -262 261 -262 257 -290 231 -268 259 -292 231 -300 263 -270 267 -266 233 -300 231 -264 255 -290 233 -298 231 -298 269 -272 235 -266 263 -262 259 -262 257 -290 233 -300 265 -236 299 -266 233 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -266 235 -298 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 231 -296 263 -234 299 -232 295 -232 261 -294 235 -300 263 -270 267 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -298 231 -298 263 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -230 293 -264 235 -300 265 -270 271 -268 231 -264 261 -294 229 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -264 257 -290 231 -298 233 -296 263 -234 299 -266 233 -300 231 -298 229 -262 261 -262 257 -290 265 -272 275 -238 261 -292 229 -298 231 -298 229 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -230 295 -264 267 -272 267 -268 233 -300 231 -296 229 -296 229 -294 263 -268 273 -268 231 -296 229 -264 257 -256 291 -230 293 -264 267 -268 265 -270 271 -268 233 -262 257 -258 291 -262 235 -300 233 -294 269 -238 263 -294 233 -300 229 -264 257 -290 233 -298 265 -270 265 -266 235 -298 231 -296 229 -296 229 -264 259 -296 229 -296 263 -268 271 -270 231 -264 261 -262 257 -258 291 -262 267 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -264 259 -290 229 -300 263 -270 267 -266 235 -300 231 -296 229 -262 257 -258 291 -262 267 -268 231 -296 263 -268 271 -268 233 -264 261 -262 257 -290 231 -266 261 -324 239 -276 237 -300 231 -262 255 -258 291 -262 267 -266 233 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 263 -236 299 -264 267 -268 231 -264 261 -294 229 -296 263 -234 299 -266 233 -302 231 -296 229 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -300 231 -296 229 -264 -RAW_Data: 255 -290 265 -272 275 -270 233 -262 261 -294 231 -294 229 -264 257 -290 231 -300 263 -270 267 -266 233 -302 229 -264 255 -292 231 -298 231 -298 229 -264 293 -268 271 -270 233 -264 261 -262 255 -290 265 -234 261 -292 231 -298 231 -296 263 -234 299 -266 235 -268 259 -258 259 -294 267 -268 265 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -260 261 -294 267 -268 265 -234 301 -264 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -234 301 -264 233 -302 231 -264 255 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -292 231 -300 231 -264 257 -290 265 -272 267 -268 235 -302 229 -264 261 -262 257 -290 231 -300 271 -272 237 -298 231 -262 261 -262 255 -258 291 -264 267 -274 269 -232 263 -294 233 -300 231 -264 259 -258 257 -260 295 -264 267 -268 265 -270 271 -268 231 -296 231 -262 261 -294 263 -268 273 -268 233 -262 261 -262 257 -258 293 -262 233 -302 231 -264 293 -268 271 -270 233 -264 257 -290 229 -266 261 -324 239 -276 237 -300 231 -262 261 -262 255 -290 233 -298 231 -298 263 -234 299 -232 295 -264 233 -300 231 -296 229 -264 261 -294 229 -296 263 -268 271 -270 231 -264 263 -260 257 -290 231 -268 259 -292 231 -300 263 -270 267 -266 233 -300 231 -264 257 -290 229 -300 265 -270 267 -264 235 -300 231 -294 231 -294 229 -264 257 -290 231 -298 231 -298 263 -236 299 -264 233 -300 231 -296 231 -262 257 -290 231 -266 261 -292 231 -298 265 -270 271 -270 231 -264 261 -294 229 -264 257 -290 265 -272 273 -272 231 -264 261 -262 255 -292 231 -300 231 -298 229 -262 293 -268 267 -266 235 -300 231 -296 229 -262 261 -262 257 -292 263 -272 269 -266 235 -300 231 -264 255 -290 233 -298 265 -236 301 -266 233 -300 231 -296 229 -264 261 -262 255 -260 259 -294 267 -272 269 -266 235 -302 229 -296 229 -296 227 -296 261 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -262 261 -262 257 -290 231 -266 261 -292 265 -272 267 -268 235 -300 231 -262 257 -290 231 -266 261 -292 231 -298 265 -270 271 -270 233 -262 261 -294 229 -264 257 -290 231 -298 265 -270 267 -232 263 -294 233 -302 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 227 -294 231 -262 257 -292 231 -298 -RAW_Data: 265 -236 301 -266 233 -300 231 -262 257 -290 233 -298 265 -270 271 -270 231 -264 261 -262 257 -258 291 -264 233 -302 231 -264 293 -234 301 -266 233 -300 231 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -268 233 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -264 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -262 255 -260 291 -302 243 -274 235 -266 261 -262 255 -290 231 -300 231 -264 257 -290 231 -300 263 -270 273 -270 231 -264 261 -262 255 -260 291 -262 235 -300 265 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -228 295 -264 267 -268 265 -236 299 -232 293 -264 233 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -264 257 -292 231 -298 265 -236 301 -264 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -234 301 -264 233 -300 231 -264 257 -292 231 -298 265 -270 271 -270 231 -296 229 -262 261 -296 229 -262 257 -292 231 -298 231 -298 261 -270 271 -268 231 -264 257 -258 259 -294 267 -268 231 -298 267 -272 235 -268 257 -290 229 -300 231 -298 263 -234 299 -230 295 -264 235 -300 231 -262 257 -258 291 -262 269 -268 231 -262 295 -268 271 -270 233 -262 261 -294 229 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -264 257 -290 265 -272 267 -268 235 -300 231 -262 257 -258 259 -260 295 -262 269 -268 265 -268 273 -268 231 -264 257 -258 257 -294 267 -268 265 -236 301 -264 233 -300 231 -296 229 -296 229 -264 255 -292 231 -298 265 -236 301 -266 233 -300 231 -294 229 -296 231 -262 257 -290 231 -300 265 -236 299 -264 233 -302 231 -264 261 -294 229 -296 263 -268 271 -270 231 -296 229 -264 261 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 233 -300 231 -296 231 -294 229 -264 257 -290 231 -298 231 -298 263 -234 301 -264 233 -300 231 -296 231 -262 257 -256 291 -298 239 -278 237 -300 231 -262 255 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -294 231 -262 257 -256 293 -262 267 -266 267 -270 271 -268 231 -264 261 -294 229 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -264 261 -294 229 -264 289 -298 241 -274 235 -264 261 -262 255 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 229 -296 229 -296 229 -296 229 -262 293 -268 273 -270 231 -264 261 -262 261 -294 263 -268 267 -234 261 -296 265 -268 231 -296 -RAW_Data: 231 -262 257 -290 231 -298 231 -298 263 -268 271 -270 231 -264 261 -294 229 -296 229 -296 229 -264 261 -294 261 -270 271 -270 231 -264 261 -294 229 -264 257 -290 265 -272 267 -266 235 -302 231 -264 261 -262 255 -260 291 -262 235 -300 263 -236 301 -264 235 -300 231 -296 229 -264 259 -296 229 -262 257 -292 231 -298 231 -298 263 -234 299 -264 269 -266 233 -262 261 -294 231 -294 231 -294 263 -268 265 -234 295 -228 295 -230 293 -264 235 -300 265 -236 299 -264 233 -302 231 -296 229 -262 257 -290 231 -266 259 -260 261 -294 267 -266 265 -236 299 -266 233 -300 231 -264 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -262 257 -258 259 -296 267 -266 265 -270 271 -270 231 -264 261 -260 257 -292 231 -266 259 -294 231 -298 265 -236 301 -264 235 -298 231 -296 231 -262 257 -290 231 -266 259 -292 267 -270 269 -266 235 -300 231 -262 257 -258 291 -262 269 -234 261 -290 231 -300 263 -236 301 -266 233 -300 231 -294 231 -262 257 -258 259 -294 267 -268 231 -296 263 -234 299 -266 233 -302 231 -264 261 -260 257 -292 231 -298 265 -270 273 -268 233 -262 257 -290 231 -298 231 -298 229 -296 263 -234 299 -266 233 -300 231 -298 229 -262 257 -258 291 -262 269 -272 269 -266 233 -300 231 -296 229 -296 261 -236 299 -232 295 -262 267 -268 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 231 -264 263 -260 257 -258 291 -296 241 -276 237 -302 229 -264 255 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 261 -294 229 -296 229 -264 255 -292 265 -272 267 -234 263 -294 233 -300 231 -264 257 -258 291 -264 233 -300 265 -270 271 -270 231 -264 261 -294 229 -262 257 -292 231 -298 265 -270 267 -264 235 -300 231 -296 229 -262 257 -258 291 -230 295 -264 233 -300 265 -234 299 -232 295 -264 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -262 261 -296 229 -296 229 -294 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 261 -270 271 -270 233 -262 261 -294 229 -264 257 -290 231 -300 231 -296 263 -234 301 -264 235 -300 233 -262 261 -294 229 -296 229 -296 229 -296 261 -270 271 -268 231 -264 257 -290 -RAW_Data: 231 -300 263 -270 267 -266 235 -300 229 -264 257 -258 259 -262 293 -264 267 -268 265 -268 273 -268 231 -264 261 -262 255 -292 265 -272 269 -266 235 -300 231 -262 257 -290 231 -266 259 -260 261 -294 267 -266 267 -234 299 -266 233 -300 231 -296 231 -294 229 -264 255 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -264 255 -290 233 -298 231 -298 263 -268 271 -268 233 -296 229 -262 261 -262 257 -258 291 -262 269 -266 265 -270 273 -268 231 -264 261 -294 229 -296 229 -264 261 -294 229 -296 263 -234 299 -266 233 -302 231 -264 255 -258 291 -264 233 -302 231 -264 293 -268 271 -270 231 -264 257 -290 231 -298 265 -270 267 -266 235 -300 229 -264 261 -294 229 -296 229 -264 255 -290 233 -298 265 -270 273 -268 231 -264 261 -262 257 -258 293 -302 243 -272 235 -266 261 -262 261 -294 229 -262 259 -290 231 -300 265 -236 299 -266 235 -298 231 -296 229 -262 257 -290 231 -300 265 -270 271 -270 231 -264 257 -290 231 -298 231 -298 263 -234 299 -264 269 -266 233 -262 261 -296 229 -296 229 -294 231 -294 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 235 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -270 271 -270 237 -300 231 -296 229 -262 257 -258 291 -264 233 -300 231 -296 263 -234 301 -264 233 -300 231 -264 257 -258 259 -296 267 -234 259 -292 231 -300 263 -236 299 -266 233 -300 231 -296 231 -262 257 -258 259 -292 269 -268 265 -270 271 -236 259 -292 231 -298 231 -296 263 -236 299 -264 233 -302 229 -298 229 -262 261 -294 231 -294 231 -294 229 -296 263 -268 271 -270 231 -264 261 -294 263 -234 299 -266 269 -266 233 -262 261 -262 257 -290 233 -298 231 -264 259 -290 231 -300 263 -270 273 -268 231 -264 261 -294 229 -296 229 -296 229 -296 267 -272 235 -268 257 -290 231 -266 259 -292 265 -272 269 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 263 -270 273 -268 231 -296 229 -262 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -266 233 -262 261 -294 231 -294 231 -262 257 -290 231 -300 265 -236 299 -232 263 -294 233 -300 231 -298 229 -264 259 -296 261 -270 271 -270 231 -264 261 -294 229 -296 229 -264 255 -292 231 -298 233 -296 263 -268 271 -268 233 -264 257 -290 231 -298 231 -296 -RAW_Data: 231 -294 263 -234 301 -230 297 -262 235 -298 231 -298 261 -236 299 -232 295 -262 235 -298 231 -298 229 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -264 257 -290 263 -272 269 -266 235 -302 229 -296 229 -264 255 -258 259 -294 267 -268 265 -236 299 -264 235 -300 233 -262 257 -258 291 -262 267 -266 267 -270 271 -236 261 -290 231 -298 231 -296 231 -294 263 -234 299 -266 233 -302 231 -264 261 -294 229 -296 229 -296 229 -296 261 -236 299 -264 233 -300 231 -264 257 -258 259 -294 267 -268 233 -296 263 -234 299 -264 235 -300 231 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -264 257 -256 291 -264 233 -268 259 -292 231 -298 265 -236 301 -266 233 -300 231 -294 231 -294 229 -264 257 -290 231 -298 231 -298 263 -234 299 -266 235 -300 231 -264 257 -256 259 -294 267 -268 231 -298 267 -272 235 -268 261 -262 257 -256 291 -264 267 -268 265 -236 299 -264 235 -300 231 -296 229 -262 261 -296 229 -296 229 -262 295 -268 271 -270 231 -264 263 -294 229 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 265 -270 271 -268 233 -264 261 -294 229 -262 257 -292 231 -298 271 -274 237 -266 261 -262 255 -290 231 -300 233 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 233 -302 263 -270 267 -266 233 -300 231 -296 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 261 -260 257 -292 231 -298 265 -270 273 -268 233 -262 261 -262 257 -290 233 -266 259 -292 265 -272 269 -266 235 -300 231 -294 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 233 -300 231 -298 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 233 -302 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -232 261 -296 267 -266 231 -264 257 -258 259 -260 293 -264 269 -268 265 -268 273 -234 261 -292 231 -266 259 -292 263 -272 269 -266 235 -300 231 -296 227 -262 257 -292 231 -266 261 -292 231 -298 265 -270 271 -270 233 -262 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 261 -294 229 -264 257 -290 231 -300 263 -236 301 -266 233 -300 231 -296 229 -264 255 -258 291 -264 233 -300 265 -268 273 -236 259 -292 231 -300 229 -298 261 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 231 -298 263 -268 271 -270 231 -264 261 -294 229 -296 263 -268 273 -268 231 -296 229 -264 261 -294 229 -262 257 -292 231 -298 231 -298 261 -236 299 -266 235 -300 -RAW_Data: 231 -294 229 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -234 299 -232 297 -230 261 -262 293 -264 233 -302 265 -236 299 -264 233 -300 231 -298 229 -294 229 -262 257 -290 233 -300 231 -298 261 -236 297 -266 233 -300 231 -296 231 -294 229 -264 257 -258 259 -294 267 -272 269 -266 235 -300 231 -296 229 -294 231 -294 263 -234 299 -232 295 -264 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -262 261 -262 257 -292 231 -298 265 -270 273 -268 233 -262 257 -290 231 -266 259 -260 261 -294 267 -268 265 -234 299 -266 233 -300 231 -264 257 -258 291 -262 267 -266 267 -236 299 -264 235 -266 259 -292 231 -298 265 -270 273 -268 233 -262 261 -294 229 -296 231 -294 229 -294 263 -270 271 -270 231 -296 229 -262 257 -290 231 -298 233 -296 269 -272 235 -268 261 -262 261 -262 257 -290 231 -300 233 -296 263 -234 299 -264 235 -300 233 -262 261 -296 229 -296 229 -294 263 -236 299 -264 233 -302 231 -264 257 -288 231 -300 231 -264 257 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 265 -270 269 -266 235 -300 231 -264 261 -294 229 -264 257 -258 259 -294 267 -268 231 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -264 259 -290 263 -272 267 -268 235 -300 231 -262 257 -290 233 -298 231 -298 263 -268 271 -268 233 -262 257 -258 291 -262 267 -268 231 -264 295 -268 265 -266 235 -300 231 -296 229 -262 261 -296 229 -296 229 -296 261 -270 271 -268 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -264 255 -290 233 -266 259 -292 231 -300 263 -270 273 -268 231 -264 261 -296 229 -294 263 -234 299 -266 235 -300 233 -262 257 -290 231 -264 261 -292 231 -300 265 -268 267 -266 235 -300 229 -264 257 -258 257 -294 269 -266 267 -236 299 -264 235 -266 259 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -298 229 -262 261 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -262 257 -258 259 -260 295 -264 267 -268 265 -236 299 -264 235 -300 231 -296 229 -296 229 -262 289 -266 267 -270 235 -302 231 -264 257 -290 229 -300 231 -264 257 -292 231 -298 265 -270 271 -270 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -258 291 -262 269 -266 233 -262 295 -268 271 -270 233 -264 255 -258 259 -292 269 -268 263 -236 299 -266 -RAW_Data: 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -296 229 -264 293 -234 301 -266 233 -300 231 -296 229 -262 257 -258 291 -264 233 -300 265 -270 265 -266 235 -300 229 -264 257 -258 259 -260 295 -262 269 -274 267 -268 233 -300 231 -264 255 -290 233 -298 265 -270 265 -234 261 -296 265 -268 231 -296 231 -262 257 -256 291 -264 233 -300 265 -236 299 -266 233 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -264 261 -262 257 -290 231 -266 261 -258 261 -294 267 -272 269 -266 235 -300 231 -294 229 -264 257 -258 259 -294 267 -272 269 -266 235 -300 231 -296 229 -264 255 -290 231 -266 261 -258 261 -294 267 -272 275 -238 261 -258 259 -260 293 -264 267 -268 265 -236 299 -264 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -262 295 -268 265 -234 295 -262 235 -300 229 -264 257 -258 291 -262 235 -300 265 -236 299 -264 235 -300 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -264 257 -256 291 -264 267 -268 265 -268 271 -270 231 -296 229 -264 255 -258 291 -296 239 -276 239 -302 231 -262 261 -260 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -262 235 -300 265 -268 267 -266 233 -300 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -264 257 -258 291 -230 293 -264 233 -300 265 -236 301 -264 235 -298 231 -296 229 -296 229 -262 257 -292 263 -272 267 -268 235 -300 231 -296 229 -262 261 -262 257 -292 231 -300 231 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -296 229 -264 255 -292 231 -298 233 -296 263 -268 271 -268 233 -264 257 -290 229 -266 261 -292 231 -300 271 -272 235 -268 257 -258 257 -260 295 -262 269 -268 265 -234 299 -266 235 -300 231 -296 229 -262 261 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -296 229 -296 261 -236 299 -232 293 -264 233 -300 231 -298 229 -262 257 -290 231 -266 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 289 -264 267 -270 271 -270 231 -262 261 -294 229 -296 229 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -296 229 -262 257 -290 231 -298 233 -296 263 -270 271 -268 231 -296 231 -262 255 -258 291 -230 295 -264 233 -300 265 -234 301 -232 263 -294 233 -300 231 -298 229 -264 293 -234 299 -266 235 -300 231 -294 231 -294 229 -264 257 -290 -RAW_Data: 231 -298 233 -296 263 -236 299 -264 233 -300 231 -298 229 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 231 -264 261 -294 229 -296 263 -234 299 -266 233 -302 231 -264 261 -294 229 -262 257 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 269 -272 235 -266 257 -290 231 -298 231 -298 229 -296 261 -268 265 -266 235 -300 231 -296 229 -264 255 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -264 257 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -294 229 -264 257 -256 293 -294 241 -276 239 -268 263 -262 259 -294 231 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -262 261 -262 257 -258 291 -264 267 -268 265 -268 273 -268 231 -264 257 -256 293 -262 267 -234 259 -292 231 -300 263 -270 267 -266 233 -300 231 -264 261 -294 263 -234 301 -266 233 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 265 -234 301 -232 295 -264 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 261 -262 257 -258 291 -262 269 -272 275 -270 231 -264 257 -290 229 -266 261 -292 231 -300 231 -296 263 -234 299 -266 267 -268 231 -264 255 -258 291 -262 235 -300 265 -236 299 -232 293 -232 293 -264 233 -300 231 -296 263 -236 297 -266 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -264 255 -292 265 -272 267 -268 233 -300 231 -264 261 -294 229 -264 257 -258 259 -294 267 -268 265 -236 299 -266 233 -300 231 -264 261 -262 257 -290 231 -300 265 -236 301 -266 233 -300 231 -294 231 -262 257 -258 259 -294 267 -268 231 -264 295 -234 299 -266 235 -266 259 -292 231 -298 231 -296 231 -294 263 -234 301 -264 235 -300 231 -296 229 -264 255 -290 233 -266 259 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 231 -294 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -262 255 -292 231 -266 259 -294 231 -300 263 -270 273 -268 231 -296 229 -294 229 -296 229 -296 267 -274 235 -300 229 -262 261 -294 229 -264 255 -292 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -262 261 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -290 231 -300 265 -268 273 -268 233 -262 261 -262 257 -258 293 -228 295 -264 267 -274 267 -266 235 -300 231 -262 257 -258 -RAW_Data: 259 -262 293 -304 243 -274 235 -266 261 -262 259 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -260 295 -264 267 -268 265 -268 273 -268 231 -296 231 -262 259 -262 257 -292 231 -298 233 -296 263 -236 299 -264 233 -302 231 -264 261 -294 229 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -300 263 -236 301 -266 233 -300 231 -296 229 -296 229 -262 261 -294 229 -296 229 -296 263 -268 271 -270 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 231 -298 231 -296 231 -294 269 -272 235 -268 257 -256 259 -294 267 -268 231 -298 261 -236 297 -266 233 -300 231 -296 231 -294 229 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -262 257 -292 229 -300 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 255 -290 231 -266 259 -294 231 -300 265 -234 301 -232 295 -262 267 -266 233 -262 263 -262 255 -258 293 -262 235 -300 265 -270 271 -268 231 -264 261 -294 229 -296 231 -294 263 -268 271 -270 231 -264 261 -294 229 -296 229 -264 257 -290 231 -298 265 -270 273 -268 231 -264 261 -262 257 -290 231 -266 261 -324 239 -276 237 -302 229 -262 255 -290 231 -300 233 -296 263 -234 299 -264 235 -300 231 -296 229 -296 227 -296 229 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -290 265 -272 267 -268 235 -300 231 -262 261 -262 257 -290 233 -266 259 -292 265 -272 269 -266 235 -300 231 -264 261 -294 229 -296 229 -296 267 -272 235 -268 257 -258 257 -294 267 -268 231 -298 229 -262 295 -234 299 -266 235 -300 231 -262 257 -258 291 -262 267 -268 231 -296 263 -270 271 -268 231 -296 231 -262 261 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 263 -268 271 -270 233 -264 261 -260 257 -258 291 -264 233 -268 259 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -262 257 -290 265 -268 231 -296 263 -236 297 -266 235 -300 231 -296 229 -262 261 -262 289 -266 267 -270 271 -270 231 -262 255 -258 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -264 255 -290 231 -300 231 -296 263 -236 299 -232 295 -230 293 -264 267 -268 231 -264 293 -268 265 -266 235 -300 231 -296 229 -296 229 -262 261 -262 -RAW_Data: 257 -290 265 -274 267 -268 235 -300 231 -262 257 -256 291 -270 271 -272 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 257 -258 259 -294 267 -274 267 -268 233 -268 259 -290 231 -300 231 -296 263 -234 301 -264 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 229 -296 263 -268 271 -270 231 -264 261 -294 231 -262 257 -290 231 -300 231 -296 263 -268 271 -270 231 -264 261 -262 257 -258 293 -262 267 -274 273 -270 231 -264 263 -260 257 -258 291 -262 235 -300 233 -262 295 -268 265 -266 235 -300 231 -296 229 -264 261 -294 229 -262 257 -292 231 -298 265 -236 301 -264 235 -300 231 -296 229 -264 255 -258 291 -264 233 -300 265 -268 273 -236 259 -292 231 -298 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 261 -236 299 -264 235 -300 233 -262 261 -294 229 -296 231 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -296 241 -276 237 -268 259 -290 229 -266 259 -294 231 -298 265 -236 299 -266 233 -300 231 -264 257 -258 291 -230 295 -262 269 -272 267 -266 235 -300 231 -262 257 -292 265 -270 269 -234 263 -294 233 -300 233 -296 229 -264 255 -258 291 -262 267 -268 231 -264 295 -268 271 -268 233 -264 257 -290 231 -266 259 -292 233 -298 265 -234 301 -266 233 -300 231 -296 229 -262 257 -258 291 -262 267 -268 265 -270 271 -270 231 -264 259 -262 257 -292 231 -298 233 -296 263 -236 299 -264 233 -302 231 -264 257 -290 229 -300 231 -264 257 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -264 233 -300 265 -234 301 -266 233 -300 231 -296 229 -296 229 -294 263 -268 273 -268 231 -296 229 -264 255 -258 291 -264 233 -268 259 -292 231 -298 265 -270 267 -266 233 -300 231 -262 261 -262 257 -290 233 -298 265 -270 273 -268 233 -264 261 -262 255 -258 291 -264 267 -268 231 -264 293 -268 273 -270 231 -264 255 -258 291 -264 233 -300 231 -296 269 -238 263 -262 261 -294 233 -266 261 -292 265 -270 269 -234 263 -294 233 -300 231 -264 257 -290 231 -300 231 -296 231 -294 263 -236 299 -264 233 -300 231 -296 231 -294 229 -264 257 -290 231 -300 231 -296 263 -234 299 -266 233 -302 231 -296 229 -262 257 -258 291 -230 295 -262 235 -300 263 -236 299 -266 233 -300 231 -296 231 -294 231 -262 261 -294 229 -296 263 -234 299 -266 -RAW_Data: 235 -300 231 -264 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 233 -302 229 -264 257 -258 291 -268 271 -270 237 -304 231 -262 261 -292 229 -264 257 -290 231 -298 233 -296 263 -236 299 -264 235 -300 231 -264 257 -258 289 -262 269 -268 231 -296 269 -238 263 -296 231 -298 231 -296 231 -262 293 -268 273 -268 233 -264 261 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -300 231 -296 229 -262 295 -268 267 -266 233 -300 231 -296 229 -262 261 -262 257 -292 231 -266 259 -292 233 -298 265 -270 271 -270 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -264 261 -294 229 -296 231 -262 257 -290 231 -300 263 -270 273 -236 259 -260 259 -292 267 -268 265 -236 299 -264 235 -300 231 -264 257 -258 291 -230 293 -230 295 -264 267 -274 267 -232 295 -262 267 -268 231 -296 231 -262 255 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -264 255 -290 233 -298 265 -270 273 -268 231 -264 261 -262 257 -290 231 -266 261 -260 259 -294 267 -274 267 -268 233 -300 231 -296 229 -262 257 -258 291 -296 239 -276 239 -268 263 -262 255 -258 291 -264 233 -300 265 -236 299 -232 263 -294 267 -268 231 -264 257 -256 291 -230 295 -264 233 -300 265 -268 273 -268 233 -264 255 -290 231 -298 265 -270 273 -270 231 -264 261 -260 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -292 231 -298 231 -296 269 -274 235 -266 259 -288 231 -266 259 -292 231 -300 231 -296 263 -234 299 -266 233 -302 231 -296 229 -262 257 -258 291 -262 235 -300 233 -262 295 -268 265 -266 235 -300 231 -264 261 -262 257 -258 291 -230 295 -262 235 -300 263 -236 299 -266 235 -300 231 -262 257 -258 259 -294 267 -268 233 -264 293 -234 299 -266 233 -302 231 -296 229 -262 257 -258 291 -262 235 -300 265 -236 299 -264 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 257 -288 231 -268 259 -260 259 -294 267 -274 267 -268 235 -300 229 -264 255 -292 231 -298 233 -296 269 -272 235 -300 229 -262 261 -262 257 -290 231 -300 265 -236 299 -266 235 -300 231 -294 229 -262 257 -290 233 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -294 261 -236 299 -266 235 -300 231 -294 231 -294 229 -264 257 -258 259 -294 267 -268 265 -268 273 -268 231 -264 261 -262 257 -258 291 -264 267 -268 263 -236 299 -266 -RAW_Data: 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -268 273 -270 231 -264 255 -258 291 -230 295 -264 233 -300 265 -234 299 -266 235 -300 231 -296 229 -264 261 -294 229 -262 259 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -268 259 -290 231 -300 231 -296 263 -234 301 -266 233 -300 231 -296 229 -262 257 -258 291 -230 293 -264 269 -272 269 -266 233 -302 229 -264 257 -258 259 -294 267 -272 275 -270 233 -262 257 -258 291 -262 235 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -258 257 -262 293 -264 269 -266 265 -270 271 -236 261 -290 231 -300 231 -296 263 -268 271 -268 233 -296 229 -262 261 -296 229 -262 257 -292 263 -272 267 -268 235 -300 231 -296 229 -294 231 -262 287 -300 241 -272 235 -266 261 -262 255 -290 231 -266 259 -260 261 -294 267 -266 265 -236 301 -264 235 -300 231 -296 229 -294 229 -264 255 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -296 263 -234 299 -266 233 -302 229 -298 229 -262 261 -294 231 -294 231 -294 229 -296 263 -268 271 -270 231 -296 229 -264 255 -258 291 -230 293 -264 269 -266 265 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -262 235 -300 265 -270 271 -268 233 -262 261 -294 229 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 263 -270 267 -266 235 -300 231 -294 229 -296 229 -262 289 -300 241 -272 235 -264 261 -262 257 -256 291 -264 267 -268 265 -234 299 -266 235 -300 231 -262 259 -290 229 -300 231 -298 229 -262 295 -268 271 -268 233 -264 261 -294 229 -296 263 -234 301 -264 235 -300 231 -296 229 -262 257 -258 291 -262 269 -266 233 -262 295 -268 271 -270 233 -262 261 -262 257 -290 231 -300 231 -298 229 -264 293 -268 271 -270 233 -264 261 -262 255 -258 291 -264 267 -274 273 -270 233 -262 261 -262 257 -258 291 -264 233 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 233 -298 231 -264 257 -292 231 -298 265 -236 301 -266 233 -300 231 -296 229 -262 257 -290 231 -298 233 -296 263 -270 271 -234 261 -292 231 -266 259 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -274 267 -268 235 -300 229 -296 229 -264 259 -296 261 -236 299 -266 235 -300 231 -294 229 -264 257 -290 231 -298 233 -296 231 -294 261 -268 273 -270 231 -264 261 -262 -RAW_Data: 257 -258 291 -296 241 -276 237 -302 229 -262 261 -262 255 -258 293 -262 269 -268 231 -262 295 -234 299 -266 235 -300 229 -264 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 233 -262 257 -290 231 -266 259 -292 233 -298 265 -268 267 -234 261 -296 233 -298 231 -296 231 -296 261 -236 299 -264 235 -300 233 -262 261 -294 229 -296 229 -264 257 -290 265 -270 269 -234 295 -262 233 -300 231 -266 257 -290 231 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 265 -272 269 -266 235 -300 231 -262 257 -258 291 -262 267 -268 233 -264 293 -234 299 -266 235 -300 231 -264 257 -258 257 -260 295 -264 267 -268 265 -270 271 -270 231 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -264 255 -292 231 -298 231 -298 229 -296 261 -268 271 -270 233 -264 261 -262 255 -290 265 -272 275 -270 233 -264 261 -262 255 -292 231 -300 231 -296 229 -296 263 -268 271 -268 233 -296 229 -264 259 -296 229 -296 229 -296 261 -270 271 -268 231 -264 257 -258 291 -262 235 -300 263 -236 301 -266 233 -300 229 -296 231 -294 229 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -264 261 -294 229 -264 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -294 263 -236 299 -264 235 -300 231 -264 257 -290 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 233 -262 261 -294 229 -264 257 -290 231 -300 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -296 227 -262 257 -292 263 -274 273 -272 231 -264 257 -288 231 -300 231 -296 231 -294 263 -236 299 -264 233 -300 231 -298 229 -262 257 -258 259 -294 267 -274 269 -266 233 -300 231 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 233 -298 231 -296 263 -268 273 -268 231 -296 229 -264 257 -256 291 -296 241 -276 239 -300 231 -262 255 -290 229 -300 231 -298 263 -234 299 -266 235 -300 231 -296 229 -262 261 -294 231 -296 229 -294 263 -268 273 -268 231 -264 263 -292 229 -296 263 -234 301 -266 233 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 233 -262 261 -262 257 -258 291 -264 233 -300 265 -270 271 -270 231 -264 261 -294 229 -262 259 -290 231 -300 231 -296 263 -268 271 -268 233 -296 229 -262 261 -262 257 -292 231 -298 265 -270 267 -266 233 -302 229 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 233 -300 231 -298 -RAW_Data: 229 -294 231 -294 229 -296 229 -262 295 -268 267 -266 233 -300 231 -264 257 -290 231 -298 265 -236 301 -266 233 -302 229 -296 229 -262 261 -262 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -294 263 -268 273 -268 231 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 257 -290 231 -298 233 -296 231 -262 295 -266 273 -268 233 -264 257 -290 229 -300 265 -236 301 -264 235 -300 231 -262 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -296 229 -262 255 -260 291 -304 243 -274 235 -264 261 -262 257 -256 291 -264 233 -268 259 -292 231 -298 265 -270 267 -266 233 -300 231 -264 255 -258 291 -262 267 -268 265 -236 299 -264 235 -300 233 -262 257 -256 293 -262 267 -234 261 -292 231 -298 265 -236 299 -264 235 -300 231 -296 229 -264 293 -234 301 -232 295 -264 233 -300 231 -264 257 -258 257 -294 267 -234 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -296 229 -294 261 -270 267 -266 233 -300 231 -264 255 -292 231 -298 265 -270 273 -268 231 -264 261 -262 257 -290 231 -266 261 -260 259 -294 267 -268 265 -268 273 -268 231 -264 257 -256 291 -264 233 -302 231 -296 269 -270 237 -266 257 -258 257 -294 267 -268 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 259 -296 229 -296 229 -296 261 -270 271 -268 233 -264 261 -294 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -264 257 -290 231 -300 271 -272 237 -266 257 -290 229 -266 261 -292 231 -300 263 -236 301 -232 263 -294 233 -300 231 -264 257 -290 231 -268 259 -292 231 -300 263 -270 273 -268 231 -264 261 -262 257 -290 231 -266 261 -260 259 -294 267 -268 265 -234 299 -266 267 -268 231 -264 259 -262 257 -292 231 -266 259 -294 265 -272 267 -266 235 -300 231 -264 261 -262 255 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -292 231 -266 259 -292 265 -272 269 -266 235 -300 229 -296 229 -296 263 -234 299 -264 235 -300 231 -296 229 -264 255 -292 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 231 -296 231 -262 261 -262 257 -290 231 -300 271 -274 235 -268 257 -256 257 -294 267 -268 265 -236 301 -264 233 -302 231 -296 229 -262 257 -290 231 -298 231 -298 263 -268 271 -270 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -296 229 -262 261 -262 -RAW_Data: 257 -258 291 -264 269 -266 265 -270 271 -268 233 -262 261 -262 257 -258 291 -262 267 -268 231 -298 263 -234 299 -264 233 -302 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 271 -238 265 -262 259 -294 267 -234 261 -290 265 -272 267 -268 233 -300 231 -296 229 -296 229 -262 257 -258 291 -262 267 -266 267 -236 299 -264 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 265 -272 267 -266 235 -302 229 -296 229 -264 261 -294 261 -270 265 -266 235 -300 231 -296 229 -262 257 -258 291 -262 267 -268 231 -298 263 -268 271 -268 231 -264 257 -290 231 -298 231 -298 269 -272 235 -266 259 -258 257 -294 267 -234 259 -292 231 -300 263 -270 267 -266 233 -302 229 -296 229 -296 229 -262 261 -294 229 -296 263 -270 271 -270 231 -264 255 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -262 255 -292 231 -300 271 -272 237 -266 263 -262 259 -262 257 -258 293 -262 269 -266 265 -270 271 -268 231 -264 261 -262 257 -258 291 -264 233 -300 265 -236 299 -266 233 -300 231 -298 229 -262 257 -258 291 -262 235 -300 263 -236 299 -266 233 -302 229 -298 229 -262 257 -258 291 -262 269 -272 269 -266 233 -300 231 -296 229 -262 257 -292 231 -266 259 -292 231 -300 265 -234 301 -266 233 -300 231 -264 261 -294 229 -264 257 -290 231 -266 261 -292 265 -272 267 -266 235 -300 231 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -292 269 -268 231 -296 263 -268 271 -270 231 -264 257 -258 257 -294 267 -268 233 -296 263 -234 299 -232 295 -264 233 -300 231 -296 263 -234 299 -266 233 -300 231 -296 229 -264 255 -292 231 -298 233 -296 231 -262 293 -268 271 -270 233 -264 261 -262 255 -290 265 -272 275 -270 233 -264 261 -262 255 -290 233 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -262 257 -290 231 -266 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -264 255 -292 231 -298 231 -298 263 -268 271 -270 231 -296 229 -264 255 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -230 295 -264 233 -300 265 -234 299 -232 297 -262 235 -298 231 -298 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 233 -300 231 -298 229 -294 231 -294 263 -234 299 -266 267 -268 231 -264 255 -290 231 -266 261 -292 265 -272 267 -268 233 -300 231 -296 229 -296 229 -262 289 -266 275 -272 -RAW_Data: 235 -264 261 -262 261 -262 257 -258 291 -264 233 -300 265 -236 299 -266 235 -298 231 -296 229 -296 229 -264 255 -290 231 -300 265 -270 271 -236 261 -290 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 263 -268 273 -268 231 -264 261 -294 229 -296 263 -268 273 -268 231 -296 231 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -264 233 -300 231 -264 257 -292 231 -266 259 -292 265 -272 267 -268 235 -300 229 -296 229 -296 229 -296 229 -294 229 -296 229 -296 263 -234 299 -264 235 -268 259 -260 257 -294 267 -268 265 -234 299 -266 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 231 -296 263 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -258 259 -294 267 -272 269 -266 235 -302 229 -296 229 -296 229 -262 295 -268 265 -266 235 -300 231 -264 257 -256 291 -262 267 -234 261 -292 231 -300 263 -270 273 -268 231 -264 257 -290 231 -266 259 -294 231 -298 271 -240 263 -296 231 -266 259 -258 259 -294 267 -266 267 -236 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -258 291 -262 267 -268 231 -264 295 -268 271 -270 231 -264 261 -262 257 -290 231 -300 233 -296 229 -264 293 -268 271 -270 231 -264 263 -260 261 -262 257 -292 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -264 259 -290 231 -298 265 -270 267 -266 233 -300 231 -262 257 -258 293 -262 267 -234 259 -292 231 -300 263 -236 301 -266 233 -300 231 -262 257 -292 231 -266 259 -292 231 -300 263 -270 273 -268 233 -294 229 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -262 261 -294 263 -268 267 -266 235 -300 231 -262 263 -262 255 -258 293 -262 267 -234 261 -292 231 -298 265 -268 273 -268 233 -262 263 -262 255 -292 231 -300 231 -296 269 -272 235 -268 261 -262 261 -262 255 -292 265 -272 269 -266 235 -300 231 -262 261 -296 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -262 235 -300 231 -296 263 -234 301 -266 233 -300 231 -262 257 -258 -RAW_Data: 291 -230 293 -264 267 -268 265 -236 299 -264 235 -300 231 -264 257 -288 231 -268 259 -260 259 -294 267 -268 265 -268 273 -268 231 -296 231 -262 261 -294 229 -296 229 -296 263 -268 271 -236 259 -260 259 -292 267 -268 265 -236 299 -232 295 -264 233 -300 231 -296 229 -264 255 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 229 -264 293 -268 267 -266 233 -302 231 -262 261 -262 257 -292 231 -298 233 -296 263 -234 299 -266 267 -268 231 -264 255 -258 291 -230 295 -264 267 -272 273 -238 261 -292 231 -298 231 -296 229 -264 293 -234 299 -266 235 -300 231 -296 229 -264 255 -290 231 -300 231 -298 229 -262 295 -268 271 -270 233 -264 259 -294 229 -264 257 -258 291 -230 295 -264 233 -300 265 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 231 -300 263 -270 273 -268 231 -296 229 -262 257 -290 231 -268 259 -292 265 -272 267 -268 233 -300 231 -264 261 -262 257 -258 259 -296 267 -234 259 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -262 257 -292 229 -266 261 -292 231 -300 263 -236 301 -264 235 -300 231 -296 229 -264 255 -258 291 -296 241 -276 237 -300 231 -262 261 -262 255 -258 293 -262 235 -300 265 -236 299 -264 233 -300 231 -298 229 -262 261 -262 257 -290 233 -298 265 -270 273 -268 233 -262 257 -258 291 -262 267 -234 261 -292 231 -298 265 -268 267 -266 235 -300 229 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 231 -296 231 -262 261 -262 255 -292 231 -266 261 -292 265 -272 267 -268 235 -300 229 -296 229 -264 255 -258 291 -264 267 -274 275 -270 231 -264 261 -260 257 -258 291 -262 269 -266 233 -264 293 -234 299 -264 269 -268 231 -264 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -294 229 -296 263 -234 299 -264 235 -300 231 -296 229 -264 257 -258 259 -294 267 -268 231 -296 263 -268 271 -270 231 -264 259 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -268 231 -264 295 -234 299 -264 235 -302 231 -264 255 -290 231 -300 231 -296 263 -236 299 -264 235 -300 233 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -262 293 -264 269 -272 269 -266 235 -300 229 -264 257 -290 231 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -296 229 -264 261 -262 289 -264 269 -270 -RAW_Data: 271 -268 231 -262 261 -294 229 -296 231 -262 257 -290 231 -300 265 -234 301 -266 235 -298 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -266 233 -300 231 -264 257 -290 231 -298 233 -296 231 -262 293 -268 273 -268 233 -264 255 -258 291 -264 233 -302 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -264 261 -294 263 -268 267 -232 263 -294 267 -268 231 -264 261 -262 257 -290 231 -266 261 -292 265 -270 269 -266 235 -300 231 -262 259 -258 257 -262 293 -306 243 -274 235 -264 261 -262 257 -256 291 -262 267 -268 231 -298 263 -234 299 -264 233 -302 229 -298 229 -262 257 -258 291 -264 233 -300 265 -268 273 -270 231 -262 261 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -266 233 -300 231 -296 231 -262 261 -294 263 -234 299 -266 269 -266 233 -262 261 -262 257 -258 291 -262 267 -268 233 -264 293 -234 299 -266 267 -268 233 -262 261 -294 229 -264 257 -290 231 -298 233 -296 263 -270 271 -268 231 -264 263 -260 257 -290 265 -266 267 -234 301 -264 235 -300 231 -296 229 -262 257 -258 259 -294 267 -234 261 -292 231 -298 265 -270 267 -264 235 -300 231 -262 261 -262 257 -258 291 -264 267 -274 273 -270 233 -264 259 -296 229 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -264 257 -258 259 -292 269 -266 233 -264 295 -266 273 -268 233 -264 261 -262 255 -292 231 -266 259 -292 233 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -264 293 -268 267 -266 233 -300 231 -264 261 -262 257 -292 231 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 231 -262 261 -294 263 -268 273 -268 233 -264 261 -262 261 -294 261 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -258 291 -262 267 -268 265 -268 273 -270 231 -264 261 -262 255 -292 231 -300 231 -296 269 -272 235 -266 259 -256 259 -294 267 -266 233 -296 263 -234 299 -266 233 -300 231 -298 229 -262 261 -294 229 -296 231 -294 263 -234 299 -266 233 -302 231 -264 255 -290 265 -272 269 -266 235 -300 231 -264 261 -294 229 -296 229 -264 257 -288 265 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -300 271 -274 235 -266 259 -256 257 -294 269 -234 261 -292 231 -298 263 -236 301 -266 233 -300 231 -264 257 -258 289 -230 295 -264 267 -268 263 -270 271 -268 233 -262 263 -262 255 -258 293 -262 235 -300 231 -296 -RAW_Data: 263 -234 299 -266 233 -300 231 -296 229 -296 229 -296 261 -270 271 -268 233 -264 261 -262 255 -258 293 -262 267 -234 261 -292 231 -298 265 -236 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -296 231 -262 257 -256 291 -264 267 -274 275 -270 231 -264 261 -292 229 -264 257 -258 291 -264 233 -300 265 -234 301 -266 233 -300 231 -264 257 -258 289 -264 267 -268 265 -234 301 -230 295 -230 295 -262 235 -300 231 -296 263 -234 299 -266 233 -302 229 -264 261 -296 229 -262 257 -292 229 -300 265 -270 265 -266 235 -300 231 -262 257 -258 291 -270 269 -272 237 -302 231 -262 261 -262 257 -290 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -296 227 -262 257 -290 265 -274 267 -268 235 -300 231 -262 257 -290 231 -300 265 -268 267 -266 233 -300 231 -264 261 -262 257 -290 231 -266 261 -292 265 -272 267 -268 233 -302 229 -264 261 -296 229 -296 229 -262 261 -294 231 -296 261 -270 271 -268 233 -264 261 -262 257 -290 231 -300 263 -270 273 -268 233 -264 259 -262 257 -258 293 -262 267 -266 233 -264 295 -232 301 -266 233 -300 231 -296 231 -262 257 -290 229 -300 231 -264 257 -292 231 -300 263 -270 273 -268 233 -262 261 -296 229 -262 257 -290 231 -300 231 -296 271 -270 237 -266 263 -260 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -302 231 -264 261 -294 229 -296 229 -296 261 -236 299 -264 235 -300 231 -262 257 -292 231 -300 263 -270 273 -268 233 -262 261 -262 257 -292 231 -266 259 -292 265 -272 267 -268 235 -300 231 -262 261 -262 257 -258 293 -296 239 -276 239 -266 259 -258 257 -294 267 -268 231 -296 263 -234 299 -266 233 -300 231 -264 257 -292 231 -266 259 -292 265 -272 267 -266 235 -302 231 -262 257 -290 265 -272 267 -234 295 -262 233 -302 231 -264 261 -262 257 -258 291 -264 233 -302 231 -262 295 -268 271 -270 233 -264 255 -290 231 -300 231 -296 229 -296 263 -234 299 -266 233 -302 231 -264 259 -262 257 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -230 295 -264 233 -300 265 -236 299 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 263 -270 267 -266 235 -266 259 -292 231 -298 265 -236 301 -264 233 -300 231 -296 231 -262 261 -294 229 -264 257 -290 233 -298 231 -296 263 -234 301 -264 235 -300 231 -296 229 -264 255 -290 -RAW_Data: 231 -300 265 -270 273 -268 231 -264 257 -288 231 -266 259 -294 231 -300 231 -296 263 -268 271 -270 233 -262 257 -256 293 -262 267 -266 233 -296 269 -270 237 -266 257 -258 257 -262 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -262 257 -292 231 -298 231 -296 263 -270 271 -268 233 -264 261 -262 255 -290 233 -266 259 -292 233 -298 265 -270 265 -266 235 -300 229 -296 231 -294 229 -264 293 -268 273 -268 233 -264 261 -260 257 -290 233 -266 259 -292 233 -298 265 -236 299 -266 235 -300 229 -296 231 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 233 -296 229 -262 257 -288 231 -300 231 -298 269 -272 235 -268 261 -262 261 -262 255 -292 231 -300 233 -264 293 -234 299 -266 235 -300 231 -264 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -294 263 -234 299 -266 233 -300 231 -296 231 -262 257 -258 259 -292 267 -234 261 -294 265 -270 269 -266 235 -300 229 -264 257 -258 259 -294 267 -274 275 -270 231 -264 257 -288 231 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -296 229 -264 255 -258 291 -264 233 -300 265 -268 273 -268 233 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 233 -266 259 -292 231 -300 263 -270 273 -268 233 -262 263 -294 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 261 -262 287 -300 241 -274 235 -264 261 -262 257 -288 231 -268 261 -292 231 -298 265 -268 267 -266 235 -298 231 -264 261 -262 257 -258 293 -262 267 -266 233 -296 263 -234 299 -264 235 -300 231 -296 229 -262 257 -258 291 -264 233 -300 265 -236 301 -264 235 -300 229 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 255 -292 231 -298 231 -264 257 -292 265 -270 269 -234 295 -264 233 -300 231 -264 257 -290 231 -298 265 -270 271 -270 231 -264 257 -256 291 -262 269 -234 261 -292 231 -298 231 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -266 259 -260 261 -294 267 -268 265 -234 299 -264 235 -300 233 -262 261 -262 257 -292 263 -274 273 -270 233 -296 227 -296 229 -296 229 -262 257 -292 231 -298 265 -236 301 -232 261 -294 267 -268 231 -264 257 -290 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 233 -262 261 -262 257 -258 291 -230 295 -264 235 -300 269 -274 235 -266 257 -290 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 229 -264 255 -292 231 -298 231 -298 -RAW_Data: 263 -234 299 -266 233 -300 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -260 259 -296 267 -272 269 -266 235 -300 231 -262 257 -290 231 -268 259 -258 293 -302 243 -274 235 -264 261 -262 255 -290 233 -298 231 -298 263 -234 301 -264 233 -300 231 -264 257 -292 229 -300 231 -298 229 -262 295 -268 271 -270 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -296 229 -296 229 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 257 -258 259 -294 267 -268 231 -296 263 -268 271 -270 231 -262 261 -296 229 -296 229 -296 261 -270 271 -268 231 -296 231 -262 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 231 -294 229 -264 261 -294 261 -270 271 -236 261 -260 259 -292 267 -268 231 -264 295 -234 299 -264 235 -302 231 -264 261 -262 255 -260 291 -230 295 -264 267 -272 267 -266 235 -300 231 -296 229 -294 231 -294 263 -268 271 -270 231 -296 229 -264 255 -258 291 -262 267 -268 231 -298 261 -236 297 -266 233 -300 231 -296 231 -294 229 -264 257 -258 259 -294 267 -268 265 -234 299 -266 233 -302 231 -296 229 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -262 255 -292 231 -300 231 -296 231 -262 295 -234 299 -264 235 -300 231 -298 229 -262 261 -294 229 -264 257 -292 231 -298 231 -296 263 -236 299 -264 235 -300 233 -262 257 -290 231 -298 231 -298 263 -234 299 -232 295 -264 233 -268 259 -292 231 -298 265 -268 267 -266 235 -300 231 -294 229 -264 255 -258 293 -228 295 -262 269 -274 267 -266 235 -300 231 -262 263 -294 229 -296 263 -234 299 -264 235 -300 231 -296 229 -264 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 255 -258 291 -230 295 -264 233 -300 271 -238 265 -294 233 -264 261 -290 231 -300 263 -270 273 -268 233 -262 261 -296 229 -262 257 -290 231 -300 231 -298 261 -270 271 -268 231 -298 229 -262 257 -288 231 -268 259 -292 231 -300 265 -268 267 -266 235 -300 231 -294 229 -296 229 -264 255 -290 233 -298 265 -236 301 -266 233 -300 231 -294 229 -296 229 -264 255 -292 231 -298 231 -296 263 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -262 257 -258 291 -262 267 -272 275 -272 233 -262 261 -262 255 -258 291 -264 267 -268 231 -264 -RAW_Data: 293 -268 267 -266 233 -302 231 -262 263 -260 257 -258 293 -262 267 -266 267 -268 271 -270 231 -264 257 -290 229 -300 265 -268 267 -266 235 -300 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 293 -268 273 -270 231 -264 261 -294 229 -296 263 -268 271 -270 231 -264 263 -260 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 233 -264 255 -290 231 -298 231 -298 229 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -292 231 -298 231 -298 263 -234 299 -264 235 -300 231 -296 229 -296 229 -296 229 -262 257 -290 231 -300 265 -236 299 -266 235 -300 229 -296 229 -264 255 -292 231 -266 259 -292 233 -298 265 -270 265 -266 233 -300 231 -296 229 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -294 229 -296 263 -268 271 -268 233 -264 261 -262 255 -260 291 -262 235 -300 231 -296 263 -234 299 -266 233 -300 231 -298 229 -262 257 -290 231 -300 231 -296 231 -262 293 -268 273 -270 231 -264 261 -262 257 -290 231 -300 263 -270 267 -266 235 -300 231 -294 229 -264 257 -258 259 -294 267 -268 265 -234 301 -264 233 -300 231 -298 229 -262 257 -258 291 -262 267 -268 265 -236 299 -264 233 -302 231 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -268 231 -264 295 -234 299 -266 233 -302 229 -296 229 -296 229 -264 255 -258 291 -264 233 -300 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 293 -296 239 -276 237 -302 231 -262 255 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -262 257 -258 291 -230 295 -262 235 -300 263 -270 273 -268 231 -264 257 -290 229 -300 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 257 -290 231 -298 231 -298 263 -268 273 -268 231 -264 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 261 -262 257 -290 231 -300 263 -270 267 -232 263 -294 267 -268 231 -264 261 -294 229 -296 229 -296 263 -268 271 -268 233 -296 229 -262 261 -296 229 -296 261 -236 299 -264 235 -300 233 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -270 231 -296 229 -262 261 -294 231 -294 231 -294 231 -294 263 -268 271 -270 231 -264 261 -294 229 -264 255 -292 265 -272 275 -270 231 -264 261 -262 255 -292 231 -300 231 -296 263 -270 271 -268 231 -264 261 -294 229 -264 257 -258 -RAW_Data: 291 -262 269 -272 269 -232 263 -296 233 -300 231 -296 229 -262 257 -292 231 -266 259 -292 265 -272 267 -268 235 -300 229 -296 231 -262 257 -290 231 -300 231 -296 263 -234 299 -266 233 -302 231 -264 261 -262 255 -292 231 -266 259 -294 265 -272 267 -266 235 -300 231 -296 229 -294 263 -268 265 -266 235 -300 231 -296 229 -264 255 -290 231 -266 261 -260 259 -294 267 -268 265 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -268 265 -270 271 -268 233 -262 263 -260 257 -292 231 -298 233 -296 263 -234 299 -232 295 -264 233 -300 231 -264 257 -258 257 -294 269 -266 267 -236 299 -264 235 -300 229 -264 257 -258 291 -264 233 -300 231 -298 261 -236 299 -232 293 -264 233 -302 231 -264 257 -288 265 -272 269 -266 235 -300 231 -262 257 -258 291 -230 295 -264 233 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -260 257 -258 293 -262 269 -266 265 -236 299 -264 235 -300 231 -264 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -294 229 -264 257 -258 259 -294 267 -268 231 -296 263 -268 271 -268 233 -262 257 -258 291 -264 233 -302 231 -262 295 -234 301 -232 261 -296 267 -268 231 -262 295 -268 267 -264 235 -300 231 -294 231 -262 257 -290 231 -300 231 -296 231 -294 263 -268 273 -268 231 -296 231 -262 257 -288 265 -272 275 -270 233 -264 261 -260 257 -290 231 -266 261 -292 231 -300 263 -270 273 -270 231 -264 261 -260 257 -258 293 -262 267 -266 267 -268 271 -270 231 -264 257 -256 291 -262 267 -268 265 -236 301 -264 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 231 -294 263 -234 299 -266 235 -300 231 -296 229 -296 229 -262 295 -234 299 -266 235 -300 231 -264 255 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 257 -258 291 -262 235 -300 263 -270 273 -268 233 -262 257 -258 259 -262 293 -262 269 -268 265 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -296 229 -262 293 -270 265 -266 235 -300 229 -296 231 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -264 261 -262 257 -258 291 -296 241 -276 237 -302 229 -264 255 -290 229 -300 231 -298 263 -234 299 -264 235 -300 233 -262 261 -294 231 -262 257 -290 231 -300 265 -268 267 -266 235 -300 -RAW_Data: 229 -296 231 -262 293 -236 299 -266 235 -300 229 -296 229 -264 255 -260 291 -262 267 -234 261 -292 231 -298 265 -268 273 -268 233 -262 261 -262 257 -290 233 -266 259 -292 233 -298 265 -270 265 -266 235 -300 229 -296 231 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 233 -262 257 -258 291 -262 267 -266 233 -296 263 -234 299 -264 269 -266 233 -262 257 -258 291 -262 267 -234 261 -292 231 -298 265 -270 271 -270 231 -264 255 -290 231 -266 259 -294 231 -300 271 -238 265 -294 233 -266 259 -258 259 -294 267 -266 265 -236 301 -264 233 -302 229 -298 229 -262 261 -262 257 -290 233 -298 265 -270 267 -266 233 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -258 257 -294 267 -268 233 -296 263 -268 271 -268 231 -298 229 -262 257 -290 231 -298 231 -298 269 -272 235 -266 259 -258 257 -260 295 -262 269 -268 231 -264 293 -268 267 -266 233 -300 231 -264 257 -290 231 -298 231 -298 263 -234 299 -266 235 -300 231 -262 259 -290 229 -266 261 -292 231 -300 263 -270 267 -232 263 -294 267 -268 231 -262 257 -290 265 -272 269 -266 235 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -294 229 -264 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 229 -296 229 -264 255 -258 291 -304 243 -274 235 -264 261 -262 257 -290 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 231 -264 263 -294 229 -294 231 -294 229 -296 263 -268 271 -236 261 -292 231 -266 259 -290 231 -300 263 -270 267 -266 233 -302 229 -296 229 -296 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -294 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -292 233 -298 265 -236 301 -266 233 -300 231 -294 229 -264 257 -258 259 -294 267 -272 269 -266 235 -300 231 -296 229 -294 231 -294 229 -264 257 -290 231 -300 231 -296 269 -272 235 -268 257 -288 231 -298 231 -298 263 -234 301 -264 233 -300 231 -296 231 -262 257 -258 291 -262 267 -268 231 -296 263 -236 297 -266 233 -300 231 -296 231 -262 257 -258 291 -228 295 -264 267 -274 267 -268 235 -300 229 -264 255 -290 231 -300 265 -270 267 -266 233 -300 231 -294 231 -262 261 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -296 229 -264 255 -258 291 -296 241 -276 237 -300 231 -262 255 -258 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -294 231 -262 -RAW_Data: 257 -258 291 -264 233 -300 265 -234 299 -266 235 -266 259 -292 231 -300 263 -270 267 -266 235 -300 229 -264 255 -292 231 -298 231 -298 229 -296 263 -268 271 -268 233 -296 229 -262 261 -294 231 -294 263 -270 271 -268 231 -264 257 -258 291 -262 267 -234 261 -292 231 -298 265 -236 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 263 -236 299 -264 267 -268 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -268 259 -258 257 -294 269 -274 267 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -264 257 -292 231 -298 265 -268 267 -266 235 -300 231 -294 229 -264 255 -258 293 -262 233 -302 263 -270 267 -266 233 -300 231 -296 229 -262 261 -262 257 -290 265 -272 275 -270 233 -264 261 -294 229 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -296 229 -262 261 -294 229 -296 231 -294 263 -268 273 -236 259 -260 257 -294 267 -268 231 -264 295 -234 299 -266 235 -300 231 -262 263 -262 255 -260 291 -262 235 -300 263 -236 301 -264 233 -300 231 -296 231 -294 263 -234 301 -264 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -236 299 -266 233 -300 231 -264 257 -258 257 -262 293 -264 269 -272 275 -236 261 -292 231 -298 231 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 231 -296 263 -234 299 -266 233 -300 231 -264 257 -258 259 -260 295 -264 267 -268 265 -236 299 -264 235 -300 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -294 229 -296 231 -262 261 -294 229 -296 229 -296 263 -234 299 -266 233 -300 231 -298 229 -262 261 -262 257 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -264 257 -258 259 -262 293 -264 267 -268 271 -274 235 -266 261 -262 255 -258 291 -264 267 -268 265 -234 299 -266 233 -302 231 -264 261 -294 229 -296 229 -296 229 -262 295 -268 271 -270 231 -264 263 -260 261 -294 263 -270 265 -266 235 -300 231 -294 231 -294 231 -262 257 -256 293 -262 267 -268 265 -270 271 -268 231 -264 261 -294 229 -296 229 -296 267 -272 235 -300 231 -262 261 -262 257 -258 291 -262 269 -272 269 -266 235 -300 229 -296 231 -262 257 -256 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 231 -262 293 -268 267 -232 295 -264 233 -300 231 -296 229 -264 255 -290 231 -266 261 -292 265 -272 267 -268 235 -300 231 -262 257 -258 291 -230 295 -262 235 -300 263 -236 301 -264 233 -300 231 -296 231 -262 257 -290 231 -266 -RAW_Data: 291 -300 243 -272 235 -264 257 -256 259 -292 269 -268 231 -296 231 -262 293 -268 273 -270 231 -264 261 -262 255 -292 231 -266 259 -260 261 -294 267 -266 265 -236 299 -264 235 -300 231 -264 257 -258 259 -294 267 -268 231 -296 263 -268 273 -234 261 -292 229 -266 259 -292 231 -300 265 -234 301 -234 261 -296 231 -300 231 -296 231 -262 257 -258 259 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -264 257 -290 231 -300 265 -268 273 -268 233 -264 261 -262 257 -258 291 -228 295 -296 241 -276 239 -268 263 -260 261 -262 257 -258 291 -262 267 -268 265 -236 301 -264 233 -300 231 -296 229 -264 261 -262 255 -292 265 -272 269 -266 235 -300 231 -262 257 -258 291 -262 269 -234 261 -290 231 -300 263 -236 299 -266 233 -300 231 -264 257 -292 263 -272 267 -268 235 -300 231 -294 229 -296 229 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -268 233 -262 261 -262 257 -258 293 -262 267 -234 261 -292 231 -298 265 -270 271 -270 231 -264 261 -262 257 -290 231 -300 271 -274 235 -266 263 -260 261 -262 257 -290 233 -298 231 -298 263 -234 299 -232 295 -264 233 -300 231 -296 229 -264 255 -292 231 -298 265 -270 271 -270 231 -264 261 -294 229 -296 263 -236 299 -264 235 -300 231 -294 231 -262 257 -258 291 -230 293 -264 267 -268 265 -268 273 -268 231 -264 261 -294 229 -296 263 -234 301 -264 235 -300 231 -264 255 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -266 233 -262 257 -290 231 -298 231 -298 263 -234 299 -266 233 -302 229 -264 261 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -230 295 -264 267 -274 267 -266 235 -300 231 -262 257 -258 259 -294 267 -268 231 -296 263 -236 299 -264 235 -300 231 -296 229 -262 261 -294 263 -270 265 -266 235 -300 231 -262 257 -292 229 -300 231 -264 257 -292 231 -298 265 -236 301 -264 235 -300 229 -296 231 -262 257 -256 293 -262 269 -266 233 -262 295 -234 299 -264 235 -300 231 -264 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 261 -262 255 -292 265 -266 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -268 265 -268 273 -268 231 -296 229 -264 261 -294 263 -234 299 -266 267 -268 231 -264 261 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -296 229 -264 255 -290 231 -266 261 -324 239 -276 237 -268 257 -290 231 -266 -RAW_Data: 259 -292 231 -300 263 -236 299 -266 235 -300 231 -296 229 -262 261 -296 229 -296 229 -296 261 -236 299 -264 235 -300 233 -262 257 -290 231 -298 231 -266 257 -290 231 -300 263 -236 301 -266 233 -300 231 -264 257 -288 231 -300 231 -298 263 -268 271 -270 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -296 229 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -264 255 -290 265 -272 267 -268 235 -300 231 -264 255 -258 289 -264 267 -236 259 -292 231 -298 265 -236 299 -266 235 -300 231 -262 257 -256 293 -262 267 -268 265 -270 271 -268 231 -264 257 -290 231 -298 231 -298 263 -234 299 -266 233 -302 231 -296 229 -262 257 -290 231 -298 233 -296 263 -236 299 -264 235 -300 231 -264 261 -264 293 -234 299 -266 235 -300 231 -264 257 -290 231 -298 231 -298 229 -264 293 -234 301 -266 233 -300 231 -264 261 -294 229 -264 257 -290 265 -272 275 -270 233 -262 261 -262 257 -290 231 -300 265 -236 299 -234 261 -296 265 -268 231 -264 257 -258 257 -262 293 -264 269 -274 267 -268 233 -300 231 -262 257 -290 231 -266 261 -260 259 -294 267 -274 267 -266 235 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -296 263 -234 301 -264 235 -300 231 -296 229 -264 255 -258 291 -262 269 -266 233 -264 293 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -230 293 -264 267 -268 271 -274 235 -266 261 -262 255 -258 291 -264 267 -268 265 -234 301 -264 233 -300 231 -296 231 -262 257 -258 291 -262 267 -268 265 -236 299 -264 235 -300 231 -264 261 -294 263 -234 299 -266 235 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 231 -264 261 -294 229 -264 257 -258 291 -304 241 -274 235 -266 261 -262 259 -296 229 -262 257 -292 231 -300 263 -236 301 -266 233 -300 231 -262 261 -296 229 -262 257 -292 265 -272 269 -266 235 -300 229 -264 261 -264 261 -260 257 -258 293 -262 269 -268 265 -234 299 -266 233 -268 259 -292 229 -300 231 -296 229 -296 263 -234 299 -266 235 -300 231 -262 263 -294 229 -296 229 -264 255 -290 233 -298 265 -268 267 -266 235 -300 231 -296 229 -262 257 -290 231 -298 233 -296 231 -262 293 -268 273 -270 231 -264 261 -262 261 -294 263 -268 267 -266 235 -300 231 -262 261 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 -RAW_Data: 231 -296 269 -272 235 -268 257 -258 289 -262 269 -268 231 -264 293 -234 299 -266 235 -300 231 -296 229 -264 259 -296 229 -296 229 -262 295 -236 299 -264 267 -268 231 -264 261 -294 263 -234 299 -266 269 -268 231 -262 261 -294 229 -264 257 -258 289 -264 267 -274 269 -266 235 -300 231 -262 261 -262 257 -258 291 -298 239 -276 237 -302 229 -264 255 -290 231 -266 259 -292 233 -298 265 -236 299 -264 235 -300 231 -264 257 -256 293 -228 295 -264 235 -300 263 -270 273 -268 231 -264 261 -262 257 -258 291 -262 267 -268 231 -264 295 -234 299 -264 269 -268 231 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -262 257 -258 291 -264 267 -268 265 -234 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 265 -234 301 -232 263 -294 233 -300 231 -264 257 -258 291 -230 293 -264 269 -272 275 -270 231 -264 261 -294 229 -262 257 -292 231 -298 231 -296 263 -236 299 -266 233 -300 231 -264 257 -258 291 -264 233 -300 265 -234 299 -232 295 -264 235 -300 229 -296 231 -262 293 -234 301 -266 235 -300 229 -296 231 -262 257 -290 231 -300 231 -296 263 -234 299 -266 235 -300 231 -264 261 -262 289 -266 267 -270 271 -268 231 -264 255 -290 231 -266 259 -292 231 -300 263 -236 301 -266 235 -300 229 -264 261 -264 259 -262 257 -292 265 -272 267 -268 235 -300 231 -262 257 -290 231 -300 265 -236 299 -266 235 -300 231 -294 231 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -300 231 -294 231 -262 261 -264 261 -294 229 -296 263 -234 299 -266 267 -268 231 -264 261 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -258 293 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -294 229 -296 229 -264 255 -258 293 -262 267 -266 265 -236 299 -266 233 -302 231 -294 229 -296 229 -262 257 -292 231 -298 265 -270 271 -236 261 -292 231 -298 231 -296 229 -264 295 -234 299 -266 233 -302 231 -262 263 -262 261 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -294 231 -294 263 -234 299 -266 235 -300 231 -296 229 -264 259 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -298 229 -262 257 -258 291 -296 241 -276 237 -268 259 -256 259 -294 267 -266 233 -296 263 -268 271 -268 231 -264 263 -262 255 -292 231 -266 259 -292 265 -272 269 -266 235 -300 231 -262 257 -290 265 -272 267 -234 297 -262 267 -266 231 -264 261 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 -RAW_Data: 231 -264 257 -290 231 -300 231 -264 257 -290 265 -272 267 -268 235 -302 231 -262 261 -294 229 -264 255 -292 231 -298 233 -296 269 -272 235 -266 263 -262 255 -258 291 -264 267 -274 267 -234 295 -262 267 -266 231 -264 263 -262 261 -262 257 -258 291 -264 267 -268 265 -234 299 -266 233 -300 231 -264 263 -262 261 -294 229 -296 229 -264 295 -234 299 -266 235 -300 231 -262 257 -292 231 -298 265 -268 273 -270 231 -264 261 -262 255 -292 265 -266 231 -264 263 -262 295 -268 271 -270 231 -264 261 -262 257 -258 291 -262 269 -272 269 -266 235 -300 231 -262 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -268 231 -262 261 -264 257 -288 265 -268 231 -296 269 -272 235 -266 257 -258 259 -294 267 -266 233 -296 263 -234 299 -266 233 -300 231 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -296 229 -262 261 -294 231 -262 257 -290 265 -268 265 -236 299 -264 235 -300 231 -294 231 -262 261 -264 293 -268 273 -268 233 -264 261 -262 257 -290 231 -266 259 -294 231 -298 231 -298 261 -270 271 -268 233 -264 261 -262 255 -258 293 -262 269 -234 259 -292 231 -298 265 -270 271 -270 231 -264 261 -260 257 -292 265 -264 233 -296 269 -272 235 -266 263 -264 259 -296 229 -294 229 -264 261 -294 263 -268 273 -270 231 -264 261 -262 255 -260 291 -262 269 -266 265 -236 299 -264 269 -232 259 -292 231 -300 231 -264 295 -234 299 -266 235 -300 231 -262 263 -260 257 -258 293 -230 293 -264 267 -274 267 -268 235 -300 229 -264 255 -258 291 -264 267 -274 273 -270 233 -262 257 -290 263 -266 231 -296 231 -262 295 -236 299 -264 269 -268 231 -262 261 -262 257 -258 293 -262 267 -268 263 -270 271 -268 233 -264 255 -290 265 -266 265 -234 301 -266 235 -300 231 -262 261 -264 261 -262 263 -262 257 -290 265 -272 269 -266 235 -300 231 -262 261 -264 261 -264 255 -290 265 -266 265 -270 273 -268 233 -262 257 -258 289 -264 267 -234 261 -292 263 -272 275 -270 233 -262 261 -262 257 -290 265 -232 259 -292 233 -298 265 -236 301 -264 235 -300 231 -262 261 -262 257 -258 293 -262 269 -266 233 -262 295 -234 299 -264 269 -268 231 -262 261 -262 257 -258 293 -262 269 -266 265 -236 299 -264 267 -234 259 -292 231 -298 231 -298 263 -234 299 -266 267 -266 231 -264 263 -262 261 -294 229 -296 231 -262 293 -268 267 -266 235 -300 231 -262 263 -262 289 -264 269 -270 271 -268 231 -262 261 -262 257 -290 265 -266 233 -264 -RAW_Data: 261 -262 295 -234 301 -264 269 -266 231 -262 261 -296 229 -296 229 -262 297 -268 271 -268 233 -262 257 -290 265 -266 231 -296 263 -270 271 -268 233 -262 261 -262 257 -290 265 -234 261 -292 265 -270 267 -268 235 -300 231 -262 257 -290 265 -272 269 -266 235 -300 231 -262 261 -262 257 -292 265 -264 233 -264 257 -290 265 -266 263 -270 273 -268 233 -262 257 -290 231 -268 259 -292 231 -300 263 -270 273 -268 231 -264 261 -294 229 -296 229 -264 257 -258 291 -262 269 -266 265 -270 271 -268 231 -264 261 -294 229 -296 263 -234 299 -266 269 -266 231 -262 257 -258 291 -230 295 -264 233 -300 265 -234 301 -264 235 -300 231 -296 229 -264 259 -296 229 -262 257 -292 231 -300 231 -296 263 -268 271 -270 231 -264 257 -256 291 -262 269 -274 267 -268 233 -300 231 -264 261 -294 229 -296 229 -264 255 -290 233 -298 265 -236 299 -266 235 -300 231 -262 263 -262 257 -256 293 -296 241 -276 237 -300 231 -262 255 -290 265 -264 233 -296 263 -236 299 -232 295 -262 267 -266 233 -264 261 -294 229 -262 257 -292 231 -298 265 -270 273 -268 231 -264 257 -290 263 -266 265 -270 273 -268 233 -262 261 -262 257 -290 231 -266 261 -292 265 -272 269 -266 235 -300 229 -264 255 -292 265 -266 231 -296 269 -272 235 -300 229 -264 255 -256 291 -264 267 -268 265 -234 301 -264 267 -268 231 -264 261 -262 255 -258 293 -262 269 -272 269 -266 233 -300 231 -296 229 -264 261 -262 261 -262 257 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 261 -296 229 -296 263 -234 299 -266 233 -300 231 -264 263 -262 257 -290 231 -298 233 -296 231 -262 293 -236 299 -266 235 -300 229 -296 231 -262 261 -264 255 -290 231 -300 231 -296 263 -236 299 -264 235 -302 229 -296 229 -296 229 -264 287 -266 267 -270 271 -268 231 -262 257 -290 231 -266 259 -292 233 -298 265 -236 299 -266 235 -300 229 -264 257 -290 231 -298 231 -298 263 -234 299 -232 295 -232 293 -264 233 -300 231 -296 263 -234 299 -266 235 -300 231 -262 263 -262 261 -262 257 -258 293 -262 267 -274 269 -266 233 -300 231 -262 257 -292 265 -270 269 -266 235 -300 231 -296 229 -262 261 -262 257 -292 231 -300 263 -270 267 -266 235 -300 229 -264 257 -258 289 -230 295 -264 267 -268 265 -268 271 -236 261 -290 231 -300 231 -296 229 -262 295 -268 271 -270 233 -264 261 -262 261 -294 229 -296 229 -296 229 -264 293 -268 273 -268 233 -264 261 -262 255 -258 293 -262 269 -266 233 -262 295 -268 -RAW_Data: 271 -270 231 -264 261 -262 255 -260 291 -262 269 -272 275 -270 231 -264 261 -262 255 -258 291 -264 269 -266 233 -262 295 -234 299 -264 235 -300 231 -264 263 -262 257 -290 231 -266 259 -294 231 -298 265 -236 299 -266 233 -302 231 -264 261 -262 255 -258 293 -262 267 -268 265 -270 271 -234 261 -292 231 -266 259 -292 231 -300 263 -236 299 -266 233 -302 229 -264 261 -296 229 -296 229 -262 261 -296 261 -270 267 -266 233 -300 231 -296 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 259 -296 229 -262 257 -292 231 -298 265 -270 265 -266 235 -300 231 -294 229 -296 231 -262 261 -294 269 -272 237 -266 257 -290 231 -264 261 -292 231 -300 265 -234 301 -264 235 -300 231 -296 229 -264 255 -290 265 -266 233 -264 261 -294 263 -234 301 -266 233 -300 231 -296 229 -294 229 -296 229 -264 255 -290 233 -298 231 -298 263 -234 299 -232 295 -230 295 -262 267 -268 265 -234 299 -266 235 -300 231 -262 263 -294 229 -296 229 -262 257 -290 231 -300 265 -236 299 -266 235 -300 231 -296 229 -262 261 -294 229 -296 231 -294 229 -264 295 -234 299 -266 233 -300 231 -298 229 -262 257 -290 265 -270 269 -266 235 -302 229 -264 261 -264 257 -288 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -290 265 -272 275 -236 263 -290 231 -298 231 -296 229 -296 263 -234 299 -266 233 -302 229 -298 229 -262 257 -258 291 -230 295 -262 269 -272 269 -266 233 -300 231 -296 229 -296 229 -262 295 -268 271 -270 231 -266 261 -260 257 -258 291 -230 295 -264 233 -300 265 -268 273 -268 231 -264 257 -258 291 -262 267 -268 231 -296 269 -238 265 -294 231 -298 231 -264 257 -292 229 -300 265 -236 301 -264 235 -300 229 -296 229 -296 229 -296 229 -262 261 -294 263 -270 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 295 -234 299 -266 267 -268 231 -264 261 -294 229 -296 263 -268 271 -268 233 -296 229 -264 255 -290 231 -266 261 -258 261 -294 267 -268 265 -234 299 -266 233 -300 231 -264 261 -262 257 -292 231 -298 233 -296 231 -262 293 -268 273 -268 233 -264 261 -262 255 -292 231 -298 233 -296 269 -272 235 -300 229 -264 255 -256 291 -264 267 -268 231 -264 295 -234 297 -266 269 -268 231 -264 259 -294 229 -296 231 -294 263 -236 299 -264 235 -300 231 -264 261 -294 263 -234 301 -232 263 -294 267 -266 233 -296 229 -264 255 -290 231 -266 259 -292 267 -270 269 -266 235 -300 231 -264 261 -294 229 -296 -RAW_Data: 263 -268 273 -268 231 -264 261 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -264 257 -256 291 -264 267 -268 265 -236 299 -264 233 -300 231 -264 257 -292 231 -298 265 -236 299 -266 233 -302 231 -264 261 -294 229 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -234 261 -292 231 -298 265 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -262 255 -292 231 -300 231 -264 257 -290 265 -272 267 -268 235 -300 231 -296 229 -262 257 -258 291 -296 241 -276 237 -302 229 -262 261 -262 255 -292 231 -298 265 -270 267 -232 263 -294 233 -300 231 -264 257 -258 291 -228 295 -264 267 -268 265 -270 271 -268 231 -264 261 -294 231 -294 263 -268 271 -270 233 -264 261 -262 255 -258 291 -264 267 -268 231 -264 293 -268 271 -270 233 -264 257 -290 229 -300 231 -296 269 -272 235 -300 229 -264 259 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -264 261 -294 231 -294 231 -294 231 -294 263 -268 271 -270 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 261 -296 261 -270 265 -266 235 -300 231 -294 231 -262 257 -258 291 -262 235 -300 233 -262 295 -234 299 -266 235 -300 231 -262 263 -262 255 -292 231 -266 259 -294 231 -300 263 -270 273 -268 231 -264 261 -294 229 -296 229 -296 263 -268 271 -268 233 -262 263 -262 255 -292 231 -300 231 -296 231 -262 295 -234 299 -266 235 -300 231 -294 229 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -264 257 -256 293 -262 269 -272 267 -268 233 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -260 259 -294 267 -268 265 -268 271 -270 231 -264 261 -296 229 -294 229 -264 257 -288 265 -272 269 -268 235 -300 229 -264 257 -258 291 -262 267 -234 261 -292 231 -298 265 -270 271 -270 231 -262 257 -290 231 -266 259 -292 233 -298 265 -270 265 -266 235 -300 231 -296 229 -262 261 -294 231 -296 229 -294 231 -294 263 -234 299 -266 233 -302 231 -264 261 -260 257 -292 231 -300 231 -296 263 -236 297 -266 235 -266 259 -260 259 -294 267 -268 265 -234 299 -266 233 -300 231 -298 229 -262 261 -262 257 -290 233 -298 265 -236 -RAW_Data: 301 -266 233 -300 231 -262 257 -258 259 -294 267 -274 275 -270 233 -262 261 -262 257 -290 231 -300 231 -296 231 -262 295 -268 265 -266 235 -300 231 -264 257 -258 257 -294 267 -268 231 -266 293 -268 273 -268 233 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -264 257 -290 231 -266 259 -292 231 -300 265 -268 273 -270 231 -264 261 -262 257 -258 291 -302 243 -274 235 -266 261 -262 255 -290 231 -300 231 -264 257 -290 231 -300 265 -268 273 -270 231 -264 261 -294 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -230 293 -264 267 -268 265 -234 299 -232 295 -264 233 -300 231 -298 261 -236 297 -266 235 -300 231 -264 261 -262 257 -290 231 -266 261 -292 231 -300 263 -270 267 -232 263 -294 233 -300 231 -296 229 -264 255 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -262 257 -292 229 -300 265 -270 271 -270 231 -264 261 -294 229 -296 229 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 269 -272 235 -268 257 -290 231 -298 231 -296 263 -234 299 -232 297 -262 235 -300 231 -262 257 -258 259 -296 267 -266 265 -236 299 -266 233 -300 231 -296 231 -262 261 -294 263 -234 301 -266 233 -300 231 -296 229 -294 231 -262 257 -258 291 -262 267 -268 265 -268 273 -268 231 -296 229 -264 255 -258 291 -264 233 -302 265 -268 271 -270 231 -264 259 -262 257 -292 231 -266 259 -294 231 -298 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 267 -268 265 -236 299 -264 233 -300 231 -298 229 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -264 235 -300 231 -296 229 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -236 261 -258 259 -294 267 -268 231 -264 295 -234 297 -266 269 -268 231 -264 255 -290 231 -266 261 -292 265 -270 269 -266 235 -300 231 -262 257 -292 265 -270 269 -266 235 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -298 231 -298 263 -268 273 -268 231 -264 255 -290 231 -268 261 -292 231 -298 271 -274 235 -266 259 -256 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 229 -264 255 -290 231 -300 231 -298 263 -268 271 -270 231 -264 261 -262 257 -290 265 -272 273 -272 231 -264 261 -294 229 -296 229 -264 255 -290 231 -300 231 -298 263 -268 271 -268 233 -264 261 -262 257 -290 231 -300 231 -296 263 -234 301 -264 235 -300 231 -296 229 -262 257 -258 291 -262 269 -266 233 -262 -RAW_Data: 295 -268 271 -270 233 -264 261 -262 259 -294 231 -294 263 -236 299 -264 235 -302 229 -298 229 -262 261 -294 231 -262 257 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 261 -294 229 -296 229 -296 263 -234 299 -266 233 -302 229 -298 229 -262 261 -294 263 -268 267 -266 235 -300 229 -296 229 -264 257 -290 231 -266 259 -292 265 -272 269 -266 235 -300 231 -264 257 -288 231 -300 231 -296 271 -270 235 -268 257 -290 231 -298 231 -264 257 -292 231 -298 265 -270 267 -266 233 -300 231 -262 257 -292 229 -300 231 -298 229 -262 295 -268 271 -270 233 -264 257 -288 231 -298 231 -298 263 -268 273 -268 231 -264 261 -262 257 -258 291 -262 267 -268 231 -298 263 -268 271 -268 231 -264 263 -294 229 -262 257 -258 259 -294 267 -268 265 -270 271 -270 231 -264 259 -262 257 -258 293 -262 235 -300 263 -236 299 -266 235 -300 231 -262 257 -258 259 -294 267 -234 261 -292 231 -300 263 -236 299 -266 233 -300 231 -298 229 -262 257 -290 231 -298 233 -296 231 -262 293 -268 267 -266 235 -300 231 -262 263 -294 229 -296 229 -264 293 -268 271 -270 233 -264 255 -290 231 -298 233 -296 231 -294 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 267 -274 267 -268 233 -302 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -296 231 -294 263 -268 271 -270 231 -296 229 -264 255 -258 291 -296 241 -276 239 -300 231 -262 255 -288 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -296 229 -296 229 -262 261 -294 263 -270 271 -270 231 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 231 -298 229 -262 295 -266 273 -268 233 -264 261 -262 261 -294 231 -294 231 -296 261 -236 299 -232 293 -264 233 -302 229 -264 257 -290 231 -266 261 -258 293 -302 243 -274 235 -264 261 -262 261 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -296 263 -234 299 -232 295 -264 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -270 271 -270 231 -264 257 -290 229 -300 265 -270 265 -266 235 -300 231 -294 231 -294 229 -296 229 -296 229 -294 263 -236 299 -264 233 -300 231 -298 229 -294 231 -294 263 -268 271 -270 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -236 297 -266 235 -300 233 -262 261 -294 229 -296 231 -262 261 -294 263 -268 273 -268 233 -262 257 -290 231 -298 -RAW_Data: 233 -296 263 -236 299 -264 235 -300 231 -264 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -296 229 -262 257 -290 231 -300 265 -236 299 -266 235 -298 231 -296 229 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -264 255 -290 233 -298 265 -270 267 -266 233 -300 231 -296 229 -262 261 -294 231 -262 257 -292 231 -298 265 -270 267 -264 235 -300 231 -262 261 -294 231 -294 231 -296 269 -272 235 -298 231 -262 261 -294 229 -296 229 -294 229 -296 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -268 231 -296 263 -270 271 -268 231 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -294 231 -294 229 -296 229 -262 261 -296 261 -270 273 -268 231 -264 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -264 255 -290 231 -300 265 -270 273 -268 231 -264 257 -290 229 -300 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 231 -262 293 -268 273 -268 233 -264 261 -294 229 -296 229 -264 255 -290 231 -300 265 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -268 271 -272 237 -302 231 -262 261 -262 255 -258 291 -264 267 -268 231 -264 295 -234 299 -266 235 -300 231 -262 257 -290 231 -300 231 -264 257 -292 229 -300 265 -270 265 -266 235 -300 231 -294 229 -264 257 -258 291 -262 269 -272 267 -266 235 -268 259 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -294 231 -294 229 -264 261 -294 229 -296 229 -296 263 -268 271 -270 231 -264 261 -294 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -264 257 -290 231 -300 265 -268 273 -270 231 -264 263 -260 261 -262 257 -292 231 -298 273 -272 237 -266 259 -256 257 -294 267 -268 231 -298 263 -234 299 -264 235 -300 233 -262 261 -262 257 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 231 -262 261 -262 257 -290 231 -300 265 -268 267 -266 235 -300 229 -296 229 -296 229 -264 261 -294 229 -296 263 -234 299 -266 233 -302 229 -298 229 -262 261 -262 257 -258 291 -264 267 -274 267 -234 263 -294 233 -300 231 -296 229 -264 257 -290 231 -300 231 -296 263 -236 299 -264 233 -300 231 -298 229 -294 231 -262 257 -290 263 -272 277 -270 233 -262 261 -262 261 -262 257 -290 233 -298 231 -298 263 -268 271 -270 231 -264 255 -290 231 -300 231 -298 229 -264 -RAW_Data: 293 -234 301 -232 263 -294 267 -268 231 -264 293 -234 299 -266 269 -266 233 -262 261 -294 229 -296 229 -264 255 -292 231 -298 265 -270 271 -270 233 -264 261 -262 255 -290 265 -272 275 -270 233 -264 261 -294 227 -264 257 -258 259 -296 267 -266 265 -236 299 -266 233 -300 231 -264 257 -258 259 -292 269 -268 265 -270 271 -236 259 -260 257 -294 267 -266 233 -264 295 -234 299 -264 235 -302 231 -264 261 -294 229 -296 229 -262 261 -294 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -234 301 -264 235 -300 231 -296 229 -264 255 -290 265 -272 269 -266 235 -300 231 -262 257 -258 259 -296 267 -234 259 -292 231 -298 265 -236 301 -266 233 -300 231 -262 261 -296 229 -262 257 -292 231 -298 233 -296 263 -234 299 -266 233 -302 231 -262 257 -258 291 -262 235 -300 231 -296 269 -272 235 -300 231 -262 259 -262 257 -292 231 -298 265 -236 299 -266 233 -302 231 -264 261 -294 229 -296 229 -264 261 -294 261 -270 271 -270 233 -264 261 -262 259 -294 263 -270 267 -266 233 -300 231 -296 229 -264 259 -262 257 -292 231 -298 233 -296 263 -236 299 -264 233 -302 231 -264 257 -290 231 -298 265 -236 299 -266 233 -300 231 -264 257 -258 291 -262 269 -234 259 -292 231 -300 263 -236 299 -266 233 -300 231 -298 229 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -264 255 -258 291 -262 269 -266 233 -264 293 -234 299 -266 267 -268 231 -264 257 -290 231 -298 265 -236 299 -266 235 -300 231 -296 229 -262 257 -258 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -294 229 -296 231 -262 257 -256 291 -264 267 -274 267 -266 235 -300 231 -264 261 -294 229 -296 263 -236 299 -264 233 -302 231 -264 255 -258 291 -262 267 -234 261 -292 231 -298 265 -270 271 -270 231 -264 261 -262 255 -260 291 -262 269 -266 273 -272 237 -264 263 -262 255 -258 291 -262 235 -300 263 -236 301 -266 233 -300 231 -296 229 -262 257 -258 291 -262 269 -266 265 -236 299 -266 233 -300 231 -296 229 -296 261 -236 299 -266 233 -300 231 -262 259 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -264 261 -262 255 -292 231 -298 233 -296 269 -272 235 -300 229 -264 259 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -296 263 -236 299 -264 235 -300 231 -264 257 -290 231 -298 231 -264 259 -290 231 -298 265 -270 267 -266 233 -300 231 -262 257 -290 265 -272 269 -266 -RAW_Data: 235 -300 231 -296 229 -262 261 -262 257 -258 293 -262 267 -268 231 -264 295 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -264 257 -292 263 -272 269 -266 235 -300 231 -262 257 -290 231 -300 231 -298 263 -268 271 -268 233 -296 229 -262 257 -290 231 -298 231 -298 263 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -296 229 -262 295 -268 271 -270 233 -264 255 -290 231 -300 263 -236 301 -266 233 -300 231 -296 229 -264 255 -290 231 -300 231 -264 259 -290 231 -298 265 -270 271 -270 231 -264 261 -294 229 -296 229 -264 293 -268 273 -268 233 -264 261 -294 229 -264 257 -258 289 -262 269 -268 265 -234 301 -264 233 -300 231 -296 231 -262 257 -256 291 -264 267 -274 267 -268 235 -300 229 -296 229 -262 257 -290 233 -266 259 -260 259 -294 267 -274 275 -270 231 -264 255 -290 231 -298 265 -270 267 -266 235 -300 229 -296 229 -264 255 -258 291 -230 295 -264 233 -300 265 -236 299 -266 233 -300 231 -296 229 -296 229 -262 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 233 -262 261 -294 229 -296 263 -234 299 -266 235 -300 231 -264 261 -294 229 -264 257 -258 291 -262 235 -300 265 -268 273 -268 233 -262 261 -262 257 -258 291 -296 239 -278 237 -270 261 -262 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 233 -300 231 -298 229 -262 257 -258 291 -262 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 293 -234 301 -266 233 -300 231 -264 257 -258 259 -292 269 -234 261 -292 263 -272 275 -270 233 -262 261 -294 229 -296 229 -264 287 -300 241 -272 235 -264 261 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 233 -300 231 -296 229 -296 229 -264 255 -290 265 -274 267 -268 233 -300 231 -296 229 -264 261 -262 255 -260 291 -262 235 -300 265 -236 299 -264 235 -266 259 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 255 -258 291 -264 233 -268 261 -290 231 -300 263 -236 301 -266 233 -300 231 -294 229 -296 229 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 261 -294 263 -268 267 -266 235 -300 229 -264 257 -292 229 -266 261 -260 259 -294 267 -268 265 -268 271 -270 231 -296 229 -264 261 -262 255 -292 231 -300 263 -270 273 -236 261 -258 259 -294 267 -266 265 -236 299 -232 295 -262 267 -268 231 -264 261 -262 257 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -296 229 -264 261 -294 263 -234 301 -232 263 -294 267 -266 233 -264 261 -294 -RAW_Data: 229 -262 257 -292 231 -300 263 -236 301 -266 233 -300 231 -264 257 -256 291 -262 267 -268 233 -264 293 -234 299 -266 235 -300 231 -296 229 -262 257 -258 291 -264 267 -272 275 -270 233 -262 261 -294 229 -264 255 -292 231 -300 231 -296 263 -268 271 -270 231 -264 257 -258 257 -294 267 -236 259 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 265 -268 273 -236 259 -292 231 -266 259 -292 265 -272 267 -266 235 -300 231 -296 229 -262 257 -258 259 -294 267 -268 231 -298 261 -270 271 -268 231 -264 261 -262 257 -290 265 -272 275 -272 231 -264 261 -262 255 -290 231 -300 231 -264 257 -292 231 -298 265 -270 273 -268 233 -262 261 -262 257 -290 233 -266 259 -292 265 -272 275 -270 231 -264 261 -262 257 -290 265 -272 267 -268 235 -300 231 -294 229 -296 229 -264 255 -292 231 -298 265 -270 265 -266 235 -300 231 -264 259 -262 289 -266 269 -270 237 -300 231 -264 261 -294 229 -296 229 -264 255 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -264 261 -262 257 -258 291 -230 295 -264 233 -300 265 -234 301 -230 297 -262 235 -300 231 -264 261 -294 263 -234 301 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 263 -260 261 -294 263 -270 265 -266 235 -300 231 -296 229 -262 257 -258 291 -262 269 -266 233 -262 295 -268 271 -270 231 -264 263 -260 257 -290 231 -300 231 -298 269 -238 263 -262 261 -294 233 -298 231 -296 231 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 233 -302 263 -236 299 -264 235 -300 231 -296 229 -296 263 -234 299 -266 233 -300 231 -264 257 -290 231 -266 259 -294 231 -298 265 -236 299 -266 233 -300 231 -296 231 -262 257 -258 291 -294 241 -276 239 -268 263 -262 259 -296 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 255 -290 231 -300 265 -270 267 -264 235 -300 231 -294 231 -294 229 -296 229 -294 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 231 -262 257 -258 259 -262 293 -262 269 -268 231 -264 293 -234 301 -266 233 -268 259 -292 229 -266 261 -292 231 -300 263 -236 301 -264 233 -302 231 -264 261 -294 229 -264 257 -290 231 -298 265 -236 301 -266 233 -300 231 -296 229 -296 261 -236 299 -264 -RAW_Data: 235 -300 231 -296 229 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 255 -258 291 -296 241 -276 237 -302 229 -264 255 -290 229 -300 231 -298 263 -268 271 -268 233 -296 229 -262 257 -290 231 -300 231 -296 263 -236 297 -266 235 -300 231 -262 257 -292 263 -272 267 -268 235 -300 231 -296 229 -296 229 -262 257 -290 231 -266 259 -292 265 -272 269 -266 235 -300 231 -264 257 -288 231 -300 231 -296 231 -296 261 -236 299 -264 235 -300 231 -262 257 -258 259 -296 267 -234 261 -292 263 -272 275 -236 261 -292 229 -300 231 -296 229 -264 293 -268 267 -266 233 -300 231 -296 229 -262 257 -290 231 -268 259 -292 231 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -294 229 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -262 257 -292 231 -298 265 -270 271 -270 231 -264 261 -262 257 -290 231 -266 261 -292 231 -298 265 -270 273 -268 233 -262 261 -294 231 -294 231 -294 269 -272 235 -300 229 -262 261 -262 257 -258 291 -262 235 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -294 231 -296 261 -236 299 -264 235 -300 231 -264 257 -290 231 -300 265 -268 273 -268 233 -262 261 -262 257 -292 231 -266 259 -292 267 -270 269 -266 235 -300 231 -262 257 -258 291 -230 295 -304 243 -272 235 -266 261 -260 261 -262 257 -290 233 -298 231 -296 265 -234 299 -266 233 -302 231 -262 259 -256 259 -260 295 -262 269 -268 265 -268 273 -268 233 -262 257 -256 293 -262 235 -300 231 -296 263 -268 271 -268 233 -264 261 -262 255 -292 231 -298 233 -296 263 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -262 269 -266 233 -262 295 -234 299 -264 269 -268 231 -264 261 -262 257 -290 231 -266 261 -292 231 -298 231 -298 263 -234 299 -264 235 -300 231 -264 257 -290 265 -272 267 -268 235 -300 231 -262 261 -294 231 -294 231 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 257 -258 291 -230 293 -296 241 -276 239 -268 259 -288 231 -264 261 -292 231 -300 231 -296 263 -234 299 -264 269 -266 233 -262 261 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -262 257 -290 233 -298 265 -270 267 -266 233 -300 231 -296 229 -294 229 -296 229 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -260 291 -304 243 -274 235 -264 261 -262 259 -262 257 -258 293 -262 235 -300 -RAW_Data: 263 -236 301 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 273 -268 233 -262 257 -290 231 -298 231 -298 263 -234 299 -266 233 -302 231 -262 261 -262 257 -292 231 -298 233 -296 263 -268 273 -268 231 -264 263 -294 229 -296 261 -270 271 -268 231 -266 261 -294 229 -262 257 -292 231 -298 231 -298 229 -264 293 -234 301 -266 233 -300 231 -296 229 -294 231 -262 257 -290 265 -272 275 -270 231 -264 257 -290 229 -300 265 -270 265 -266 235 -300 231 -294 231 -294 229 -264 255 -258 293 -262 267 -272 269 -266 235 -300 231 -262 257 -258 291 -270 271 -270 237 -300 233 -262 261 -262 255 -292 265 -232 261 -260 259 -294 267 -266 267 -268 271 -270 231 -264 257 -256 291 -264 233 -300 231 -296 263 -270 271 -268 231 -296 231 -262 255 -292 231 -266 259 -292 265 -272 267 -268 235 -268 257 -292 231 -298 231 -296 231 -294 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -236 299 -266 235 -300 231 -296 229 -262 261 -296 229 -262 257 -292 231 -300 263 -270 273 -268 231 -264 261 -294 231 -294 263 -236 299 -264 235 -300 231 -296 229 -264 261 -260 257 -258 291 -264 267 -268 265 -268 273 -268 231 -264 261 -262 257 -290 231 -300 231 -298 269 -272 235 -266 257 -258 257 -294 267 -268 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -236 299 -266 233 -300 231 -264 257 -290 265 -272 267 -268 233 -302 231 -262 261 -296 229 -294 231 -262 257 -290 265 -272 267 -268 235 -300 231 -294 231 -262 257 -290 231 -300 271 -272 237 -266 257 -258 257 -294 267 -234 261 -292 231 -298 265 -236 301 -266 233 -300 231 -262 257 -258 291 -262 267 -268 231 -296 263 -268 273 -268 231 -296 231 -262 261 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 261 -270 271 -270 233 -264 261 -262 255 -258 291 -264 233 -268 259 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -294 229 -264 255 -292 231 -298 233 -296 263 -268 271 -270 231 -264 263 -260 257 -290 231 -300 265 -270 271 -270 231 -264 257 -290 229 -266 261 -258 261 -294 267 -268 265 -234 299 -266 233 -300 231 -264 257 -258 259 -296 267 -266 265 -236 299 -232 295 -230 293 -264 233 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 -RAW_Data: 261 -236 299 -264 235 -300 231 -264 257 -258 289 -268 271 -272 237 -304 231 -262 261 -294 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 257 -258 259 -294 267 -268 231 -264 293 -268 273 -268 233 -264 261 -262 255 -292 231 -266 259 -292 233 -298 265 -270 267 -232 263 -260 293 -264 267 -268 265 -268 273 -268 231 -264 261 -294 229 -264 257 -290 231 -300 231 -296 231 -294 263 -234 299 -266 233 -302 231 -264 261 -294 229 -262 259 -258 259 -294 267 -268 265 -268 271 -268 233 -296 229 -262 257 -290 231 -300 231 -296 229 -296 263 -268 271 -270 231 -264 261 -262 257 -258 291 -262 267 -236 259 -292 265 -272 273 -270 233 -262 261 -294 231 -262 257 -290 265 -272 275 -270 233 -264 255 -290 231 -298 233 -264 257 -290 231 -298 265 -270 267 -266 233 -300 231 -264 261 -294 229 -264 257 -290 265 -272 267 -268 235 -266 259 -292 231 -298 231 -296 263 -270 271 -268 233 -264 261 -262 261 -260 259 -290 233 -298 265 -270 267 -264 235 -300 231 -262 261 -294 231 -294 263 -270 271 -268 233 -264 261 -294 229 -262 257 -258 293 -262 235 -300 231 -296 263 -268 271 -268 233 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 231 -264 261 -262 257 -292 231 -298 231 -298 229 -296 263 -234 299 -232 293 -264 235 -268 259 -292 229 -300 263 -236 301 -266 233 -300 231 -296 229 -262 261 -262 257 -292 231 -300 231 -296 231 -294 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -298 233 -296 263 -270 271 -268 231 -264 261 -262 257 -290 231 -300 265 -236 299 -266 235 -300 231 -296 229 -262 261 -262 257 -258 291 -262 267 -268 265 -236 301 -264 233 -300 231 -298 229 -262 257 -258 291 -296 239 -278 237 -300 231 -260 257 -290 231 -266 261 -292 231 -298 265 -236 301 -264 235 -300 229 -264 257 -258 291 -230 293 -262 269 -268 265 -268 273 -268 231 -264 261 -294 229 -264 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -264 267 -268 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 267 -268 231 -264 293 -268 267 -266 233 -300 231 -296 229 -264 255 -290 231 -266 261 -292 231 -300 263 -270 273 -268 233 -262 261 -262 257 -292 265 -270 269 -266 235 -302 229 -296 229 -262 257 -290 231 -268 259 -260 259 -294 267 -274 267 -268 235 -300 229 -264 261 -262 255 -260 291 -296 241 -276 237 -302 229 -262 257 -256 291 -262 267 -268 265 -236 -RAW_Data: 299 -232 295 -262 235 -300 231 -296 229 -264 255 -258 291 -262 267 -268 265 -270 271 -268 231 -264 257 -290 231 -300 263 -270 273 -270 231 -264 261 -260 257 -290 265 -266 233 -298 263 -234 299 -264 235 -300 231 -264 255 -290 231 -300 231 -298 269 -272 235 -266 257 -290 231 -264 261 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -294 231 -296 229 -262 295 -268 271 -270 231 -264 263 -294 227 -296 231 -262 257 -290 231 -300 265 -236 299 -266 233 -300 231 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 295 -268 267 -264 235 -300 231 -294 231 -294 229 -296 229 -264 255 -290 233 -298 265 -270 273 -268 233 -262 261 -294 231 -294 229 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 235 -300 231 -296 229 -262 257 -290 231 -300 231 -296 263 -270 271 -268 231 -264 257 -290 231 -298 231 -298 263 -234 299 -232 295 -264 267 -266 233 -264 255 -258 291 -262 269 -266 265 -236 299 -264 235 -300 231 -296 229 -264 259 -296 261 -270 265 -266 235 -300 231 -296 229 -262 261 -262 257 -258 291 -264 267 -268 265 -268 273 -268 231 -264 261 -294 229 -264 257 -258 291 -262 267 -268 231 -264 295 -268 271 -270 231 -264 261 -262 257 -258 291 -262 267 -234 261 -292 231 -298 265 -236 301 -266 233 -300 231 -262 261 -262 257 -292 231 -300 231 -296 263 -268 273 -268 231 -264 261 -294 229 -264 257 -258 259 -294 267 -268 231 -264 295 -268 271 -270 231 -264 257 -290 231 -266 259 -292 233 -298 271 -272 237 -266 257 -290 231 -266 259 -292 231 -300 265 -234 301 -264 235 -300 231 -296 229 -262 257 -258 291 -264 267 -272 269 -266 235 -300 231 -294 229 -296 229 -296 261 -270 271 -268 233 -264 261 -262 255 -292 231 -266 259 -294 231 -298 265 -270 271 -270 231 -264 255 -290 231 -266 261 -292 231 -300 271 -238 265 -294 233 -298 231 -262 259 -290 231 -298 265 -270 265 -266 235 -300 231 -264 261 -294 229 -296 229 -264 261 -294 263 -268 273 -268 233 -262 263 -260 257 -258 291 -264 267 -268 231 -264 293 -234 299 -266 269 -268 231 -264 259 -296 229 -294 263 -268 273 -268 231 -296 231 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -264 259 -262 257 -292 231 -298 233 -296 263 -236 299 -264 233 -302 229 -298 229 -262 257 -258 291 -296 239 -278 237 -300 -RAW_Data: 231 -262 257 -256 291 -230 293 -264 269 -266 265 -236 299 -264 235 -300 231 -296 229 -264 259 -262 257 -292 231 -298 265 -270 273 -268 233 -262 263 -294 227 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 267 -268 265 -234 299 -232 295 -264 233 -300 231 -296 229 -262 261 -296 261 -270 267 -266 233 -300 231 -264 257 -256 291 -262 267 -234 261 -292 233 -298 265 -268 273 -268 233 -262 261 -262 257 -290 233 -298 231 -296 263 -236 299 -266 233 -300 231 -264 261 -262 257 -258 291 -264 267 -268 231 -264 293 -268 273 -236 261 -292 229 -266 259 -292 233 -298 265 -234 301 -266 233 -300 231 -296 229 -262 257 -290 231 -266 261 -292 231 -300 265 -234 299 -266 235 -300 231 -264 261 -294 229 -296 229 -296 229 -264 293 -268 267 -266 233 -300 231 -296 231 -262 261 -294 263 -268 273 -268 233 -262 261 -262 257 -258 291 -264 233 -268 259 -292 265 -272 267 -268 233 -300 231 -296 229 -264 255 -290 231 -300 265 -270 265 -266 235 -300 231 -262 257 -290 233 -266 259 -292 231 -300 231 -296 263 -268 271 -270 231 -264 261 -294 229 -296 229 -296 229 -262 295 -268 267 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -260 257 -292 231 -298 265 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -290 265 -266 233 -264 293 -234 299 -266 235 -302 231 -264 259 -294 229 -296 231 -262 257 -290 265 -272 269 -266 235 -300 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -264 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -264 293 -268 273 -268 233 -262 261 -294 231 -262 257 -292 231 -298 231 -298 261 -236 299 -264 235 -302 231 -264 259 -296 229 -296 229 -262 261 -296 261 -270 271 -270 231 -264 255 -292 231 -298 265 -270 267 -264 235 -300 231 -262 257 -290 231 -266 261 -292 231 -300 263 -270 273 -268 233 -262 261 -262 257 -258 291 -304 243 -274 235 -264 261 -262 261 -262 257 -290 231 -266 261 -292 231 -300 263 -236 301 -266 233 -300 231 -296 229 -262 261 -262 257 -292 231 -298 265 -236 301 -266 233 -300 231 -264 261 -294 229 -264 257 -258 291 -262 267 -268 231 -264 295 -268 271 -270 231 -264 261 -262 261 -262 257 -260 291 -262 267 -268 265 -270 271 -268 233 -262 261 -294 231 -294 229 -264 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -266 -RAW_Data: 261 -292 231 -300 231 -296 269 -272 235 -268 257 -290 229 -266 259 -292 267 -272 267 -266 235 -300 231 -296 229 -262 257 -258 291 -264 233 -300 265 -268 273 -268 233 -262 261 -262 257 -290 265 -274 267 -268 233 -300 231 -296 229 -264 261 -294 229 -264 257 -290 231 -300 263 -270 273 -268 233 -264 259 -296 229 -262 257 -292 231 -298 265 -270 265 -234 261 -296 233 -298 233 -296 231 -262 261 -262 257 -258 291 -262 267 -268 271 -240 265 -260 259 -294 267 -266 233 -264 295 -232 299 -266 269 -268 231 -262 261 -262 257 -258 291 -264 267 -268 265 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 295 -234 299 -266 233 -302 231 -296 229 -262 257 -290 231 -300 263 -236 301 -266 235 -298 231 -296 229 -264 255 -258 291 -264 267 -268 231 -264 293 -268 271 -270 233 -264 261 -262 259 -264 257 -258 291 -270 271 -270 237 -302 231 -262 261 -294 229 -264 257 -290 231 -300 263 -270 267 -266 235 -300 229 -296 229 -262 257 -258 291 -264 233 -300 265 -270 271 -236 261 -258 259 -294 267 -266 233 -296 263 -234 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -236 299 -264 235 -302 231 -264 259 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -264 257 -290 265 -272 267 -268 235 -300 229 -264 257 -258 291 -262 267 -234 261 -292 231 -298 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 257 -258 259 -294 267 -268 231 -296 269 -272 235 -266 263 -262 259 -262 257 -292 231 -300 263 -270 267 -266 233 -300 231 -296 229 -264 261 -294 229 -296 229 -296 263 -234 299 -266 233 -268 259 -292 231 -298 231 -298 261 -270 271 -268 231 -298 229 -262 261 -294 229 -264 257 -292 229 -300 263 -270 267 -266 235 -300 229 -296 231 -262 261 -294 263 -268 273 -268 233 -264 261 -294 229 -296 229 -262 257 -290 233 -298 231 -296 263 -236 299 -264 235 -300 233 -262 261 -294 231 -262 257 -290 265 -272 267 -268 235 -300 231 -294 231 -294 229 -296 229 -296 229 -262 261 -294 263 -236 299 -266 235 -266 259 -260 257 -294 267 -268 265 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 233 -300 265 -236 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 233 -298 265 -236 301 -264 235 -300 231 -262 261 -262 257 -290 231 -300 265 -236 301 -266 233 -300 231 -296 229 -262 261 -262 257 -292 -RAW_Data: 231 -300 263 -270 273 -268 233 -262 261 -294 231 -262 257 -290 231 -300 271 -240 265 -294 233 -266 257 -260 257 -294 269 -266 265 -236 299 -266 233 -302 231 -264 261 -260 257 -258 293 -262 267 -266 265 -236 299 -266 235 -300 229 -296 231 -294 263 -234 299 -266 235 -300 231 -262 259 -256 259 -294 267 -268 231 -298 261 -270 271 -268 233 -262 261 -262 257 -290 231 -300 231 -298 269 -272 235 -266 259 -290 229 -266 259 -292 231 -300 231 -296 263 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -290 231 -300 231 -264 257 -292 229 -300 265 -270 265 -266 235 -300 231 -262 257 -290 265 -272 269 -266 235 -300 229 -296 229 -296 229 -262 257 -292 231 -298 231 -298 229 -296 263 -268 271 -268 233 -262 263 -260 257 -290 231 -300 231 -264 259 -290 265 -272 267 -266 235 -302 229 -296 229 -296 229 -262 257 -258 293 -302 243 -272 235 -266 261 -262 255 -290 231 -300 231 -264 257 -292 231 -298 265 -270 273 -268 231 -264 261 -294 229 -264 257 -290 231 -300 263 -270 273 -236 261 -290 231 -266 259 -292 233 -298 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -296 261 -236 299 -264 235 -302 231 -262 261 -262 257 -292 231 -266 259 -260 291 -262 267 -274 267 -268 233 -300 231 -296 229 -264 255 -258 291 -264 267 -274 267 -266 235 -300 231 -294 229 -296 229 -296 229 -262 257 -290 231 -300 265 -270 271 -236 261 -258 259 -260 295 -262 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 255 -292 231 -266 259 -260 259 -296 265 -268 265 -270 271 -270 231 -262 261 -262 257 -258 293 -262 235 -300 265 -236 299 -264 233 -302 231 -262 257 -290 231 -300 263 -270 267 -266 233 -302 229 -296 229 -296 229 -264 255 -258 291 -264 267 -268 263 -270 271 -268 233 -262 263 -262 255 -260 291 -294 241 -276 239 -268 263 -262 259 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 267 -268 265 -236 299 -264 235 -266 261 -290 231 -300 263 -270 267 -266 233 -300 231 -264 255 -290 233 -298 231 -264 257 -292 265 -270 275 -270 233 -264 261 -294 229 -294 231 -262 289 -298 241 -274 233 -266 261 -262 255 -290 233 -266 259 -292 231 -300 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 265 -272 267 -268 235 -300 231 -296 229 -262 261 -262 257 -258 291 -264 -RAW_Data: 233 -300 265 -236 301 -266 233 -266 259 -260 259 -292 267 -274 267 -268 235 -300 231 -294 231 -262 257 -290 231 -300 231 -264 257 -290 231 -300 263 -236 301 -266 233 -300 231 -264 261 -294 231 -262 257 -290 231 -298 265 -270 267 -266 235 -300 229 -296 231 -262 261 -294 263 -234 301 -266 233 -300 231 -264 257 -290 231 -266 259 -260 259 -294 267 -268 265 -270 271 -268 233 -262 263 -294 229 -262 257 -292 231 -298 265 -270 271 -236 261 -258 259 -294 267 -266 233 -296 263 -268 271 -268 233 -296 229 -262 261 -262 257 -258 293 -262 235 -300 263 -236 299 -266 233 -302 229 -298 229 -262 257 -290 265 -272 267 -268 235 -300 229 -296 231 -262 257 -256 293 -262 267 -234 261 -292 263 -272 267 -268 235 -300 229 -264 257 -258 291 -264 233 -300 231 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -300 263 -270 273 -268 233 -264 255 -258 259 -260 293 -264 269 -268 231 -296 263 -234 299 -264 235 -300 231 -296 229 -264 259 -262 257 -292 233 -298 265 -270 265 -266 235 -300 231 -262 257 -258 291 -262 267 -234 261 -292 265 -272 267 -266 235 -266 259 -260 259 -294 267 -268 263 -236 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 233 -302 229 -298 261 -270 271 -270 231 -264 261 -262 255 -292 265 -272 275 -270 231 -264 261 -262 255 -292 231 -266 259 -260 261 -294 267 -266 265 -270 271 -268 233 -296 229 -262 257 -258 291 -230 293 -264 267 -274 273 -238 261 -292 229 -266 259 -292 265 -270 269 -266 235 -300 231 -296 229 -294 231 -262 257 -290 231 -300 263 -236 301 -266 235 -300 231 -294 229 -262 289 -298 243 -272 235 -264 263 -260 261 -294 229 -264 257 -258 259 -294 267 -268 265 -236 299 -264 235 -300 233 -262 261 -262 257 -292 231 -298 231 -298 263 -268 271 -268 233 -296 229 -262 257 -258 291 -230 295 -264 233 -300 265 -234 299 -232 295 -230 293 -232 293 -264 235 -300 263 -236 299 -264 269 -268 231 -264 261 -294 229 -294 231 -262 257 -290 265 -272 269 -266 235 -300 231 -296 229 -262 261 -294 229 -296 231 -294 229 -296 263 -234 299 -266 267 -268 231 -262 261 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 293 -262 267 -266 233 -296 263 -268 271 -268 233 -262 261 -262 257 -292 231 -298 231 -298 269 -238 265 -260 261 -294 233 -300 231 -296 229 -296 263 -234 299 -266 233 -302 231 -264 261 -260 257 -258 291 -264 267 -268 265 -236 299 -264 233 -300 231 -296 -RAW_Data: 231 -294 263 -236 299 -264 267 -268 231 -264 255 -290 231 -268 259 -292 231 -300 265 -234 301 -266 233 -300 231 -296 229 -264 255 -258 291 -296 241 -276 237 -302 231 -262 259 -262 257 -256 293 -262 269 -266 265 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -262 267 -268 265 -270 271 -270 231 -264 255 -258 291 -262 267 -234 261 -292 231 -298 265 -270 265 -266 235 -300 231 -262 257 -290 265 -272 267 -268 235 -300 231 -262 261 -262 257 -258 293 -228 295 -264 269 -272 267 -266 235 -300 231 -294 229 -296 231 -262 257 -256 291 -264 233 -300 265 -236 301 -232 263 -294 233 -300 231 -296 229 -264 257 -258 257 -294 269 -274 273 -270 233 -264 259 -262 257 -258 291 -264 233 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -290 231 -300 265 -270 271 -270 231 -264 261 -294 229 -296 263 -234 301 -264 233 -302 231 -264 261 -262 257 -258 291 -262 235 -300 233 -262 295 -234 299 -266 235 -300 231 -262 263 -260 289 -266 269 -268 271 -268 231 -264 257 -256 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -296 229 -262 261 -296 229 -296 261 -236 299 -264 235 -302 231 -264 255 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -266 261 -292 265 -272 269 -266 235 -300 229 -264 257 -290 231 -266 259 -294 231 -298 265 -236 301 -266 233 -300 231 -294 231 -262 261 -294 263 -236 299 -266 235 -300 229 -264 257 -258 291 -262 267 -234 261 -292 231 -298 265 -236 299 -264 235 -300 231 -296 229 -264 261 -262 255 -292 231 -300 231 -296 263 -236 299 -266 233 -300 231 -296 229 -296 229 -294 231 -294 229 -296 263 -268 271 -268 233 -296 229 -262 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 293 -268 267 -266 233 -300 231 -298 229 -262 257 -258 291 -262 235 -300 231 -296 263 -268 271 -270 231 -264 261 -262 255 -258 293 -230 295 -296 241 -276 237 -300 231 -262 255 -290 231 -298 231 -298 263 -268 273 -268 231 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 257 -290 265 -270 269 -268 233 -302 231 -262 261 -262 257 -258 291 -264 233 -268 259 -292 265 -272 267 -268 235 -300 231 -294 229 -264 255 -290 231 -300 231 -298 229 -296 261 -236 299 -264 235 -300 231 -296 229 -296 229 -262 257 -258 291 -264 233 -300 265 -234 301 -266 233 -300 231 -296 -RAW_Data: 229 -296 229 -294 229 -264 257 -290 265 -272 267 -268 233 -302 229 -264 257 -258 291 -262 267 -234 261 -292 231 -298 265 -234 301 -266 235 -298 231 -264 257 -258 259 -292 269 -268 263 -270 273 -268 233 -262 257 -256 293 -262 267 -266 265 -236 301 -264 233 -300 231 -296 229 -264 261 -294 229 -296 229 -296 263 -234 301 -264 235 -300 231 -264 257 -290 263 -272 267 -268 235 -302 231 -262 257 -290 229 -300 231 -298 229 -262 295 -268 265 -266 235 -300 231 -296 229 -264 261 -262 257 -290 265 -272 275 -270 231 -264 257 -256 291 -264 233 -300 231 -296 263 -236 299 -264 235 -300 231 -296 229 -264 255 -258 291 -264 267 -272 269 -266 235 -300 231 -294 229 -264 257 -290 231 -300 231 -296 229 -296 261 -236 299 -266 233 -302 231 -264 261 -294 229 -296 263 -234 299 -264 235 -300 233 -262 257 -258 289 -230 295 -264 267 -268 265 -234 299 -266 233 -302 231 -262 261 -262 257 -292 231 -298 233 -264 257 -290 231 -298 265 -270 273 -268 231 -264 261 -296 229 -296 229 -262 257 -324 237 -276 237 -302 229 -262 257 -288 231 -300 231 -296 263 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -268 265 -234 299 -266 235 -300 231 -262 263 -294 263 -234 301 -264 235 -300 231 -294 231 -262 257 -290 231 -266 259 -294 231 -300 263 -236 299 -266 233 -302 229 -264 257 -258 291 -230 295 -304 243 -272 235 -266 255 -290 229 -266 259 -294 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 261 -262 257 -290 265 -274 267 -268 235 -300 231 -294 229 -296 229 -262 295 -268 267 -266 233 -300 231 -264 261 -294 229 -264 257 -290 233 -298 265 -236 299 -266 235 -300 229 -296 231 -294 229 -294 229 -296 229 -296 263 -268 271 -268 233 -264 261 -294 229 -296 229 -262 257 -292 265 -270 275 -270 233 -264 261 -294 229 -262 257 -258 291 -264 233 -300 265 -236 299 -266 235 -300 229 -296 229 -262 257 -292 231 -298 231 -298 229 -294 263 -236 299 -264 235 -300 231 -296 229 -296 229 -296 229 -294 231 -262 293 -268 273 -268 233 -264 257 -290 231 -298 231 -296 263 -234 301 -266 235 -300 229 -296 229 -264 255 -292 231 -298 231 -298 263 -268 271 -268 233 -264 261 -262 261 -294 263 -268 273 -270 231 -264 261 -294 229 -264 257 -290 231 -266 259 -292 233 -298 265 -236 301 -266 233 -300 229 -296 231 -262 261 -262 257 -290 265 -272 275 -270 233 -264 255 -258 257 -294 267 -274 269 -268 233 -300 231 -296 229 -296 229 -262 -RAW_Data: 257 -290 231 -300 263 -236 301 -266 235 -298 231 -296 229 -262 259 -290 229 -266 261 -292 231 -300 263 -270 267 -266 235 -300 229 -296 229 -296 229 -262 257 -290 231 -300 265 -268 273 -270 231 -264 261 -262 255 -258 291 -264 269 -234 259 -292 231 -298 265 -234 301 -266 235 -298 231 -264 261 -296 229 -294 229 -296 231 -262 293 -268 267 -266 235 -300 229 -296 231 -262 261 -262 257 -290 265 -272 275 -272 231 -264 261 -262 255 -290 231 -268 259 -292 231 -300 265 -234 301 -266 235 -298 231 -264 257 -258 291 -262 235 -300 265 -236 299 -232 293 -230 295 -264 233 -300 231 -296 263 -234 299 -266 233 -302 231 -262 261 -262 257 -292 231 -298 233 -296 263 -234 299 -266 233 -300 231 -264 257 -258 293 -268 271 -270 237 -300 233 -262 261 -262 255 -258 293 -262 267 -268 265 -236 299 -264 267 -268 231 -264 261 -262 257 -258 291 -264 267 -272 269 -266 235 -300 231 -262 257 -290 231 -298 265 -270 267 -266 233 -300 231 -296 229 -264 255 -290 233 -266 259 -292 265 -272 269 -266 235 -300 231 -294 229 -264 257 -258 259 -294 267 -268 231 -264 295 -268 271 -268 233 -264 261 -262 255 -258 293 -268 271 -270 239 -302 231 -262 261 -262 257 -258 257 -294 269 -266 233 -296 263 -234 299 -264 269 -266 233 -262 261 -296 229 -296 229 -262 257 -290 233 -298 265 -270 265 -266 235 -300 229 -296 229 -264 261 -262 257 -290 231 -300 265 -270 271 -236 261 -292 229 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -296 229 -264 261 -262 257 -290 231 -300 265 -270 265 -266 235 -300 231 -294 229 -296 263 -234 299 -266 233 -300 231 -296 229 -296 231 -262 257 -256 291 -264 233 -302 265 -236 299 -264 233 -300 231 -296 231 -262 257 -256 293 -296 239 -278 237 -268 257 -258 257 -294 267 -268 231 -264 295 -234 299 -266 235 -300 231 -264 257 -258 257 -260 295 -264 267 -268 265 -236 299 -264 235 -300 231 -296 229 -262 257 -258 291 -230 293 -264 269 -266 265 -236 299 -264 235 -300 231 -264 257 -256 291 -262 269 -268 265 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -258 291 -262 267 -268 265 -236 299 -264 235 -300 233 -262 261 -262 257 -292 231 -266 259 -292 233 -298 265 -236 299 -264 235 -300 233 -262 257 -290 265 -272 267 -268 235 -300 231 -262 263 -294 229 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 233 -262 257 -290 231 -266 259 -294 265 -272 273 -238 259 -292 231 -266 259 -292 231 -300 -RAW_Data: 263 -236 301 -266 233 -300 229 -296 229 -296 229 -296 229 -262 257 -290 265 -272 269 -266 235 -300 231 -262 259 -290 229 -300 265 -270 271 -270 231 -264 257 -256 291 -230 295 -264 233 -302 231 -264 293 -268 271 -270 231 -264 257 -258 257 -294 269 -266 233 -296 269 -272 235 -266 257 -258 259 -260 293 -264 269 -268 265 -234 299 -266 233 -300 231 -296 229 -264 257 -288 231 -300 231 -298 263 -268 271 -268 233 -264 261 -262 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -294 229 -296 229 -296 261 -270 271 -268 233 -264 261 -262 255 -292 231 -266 259 -294 231 -298 231 -298 261 -270 271 -268 231 -298 229 -262 261 -262 257 -290 233 -266 259 -292 233 -298 265 -270 271 -270 231 -262 261 -262 257 -292 231 -298 233 -296 269 -272 235 -300 229 -264 259 -262 257 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -264 257 -290 231 -298 233 -296 263 -234 299 -266 233 -300 231 -298 229 -262 295 -234 299 -266 235 -300 231 -264 261 -262 257 -258 291 -264 233 -268 259 -290 265 -272 267 -268 235 -300 231 -262 257 -258 291 -262 267 -274 275 -270 231 -264 257 -290 229 -300 231 -298 229 -262 295 -268 267 -264 235 -300 231 -264 261 -294 229 -296 231 -262 257 -290 231 -300 229 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -234 261 -292 231 -300 263 -270 273 -234 261 -258 259 -294 267 -268 265 -234 299 -266 233 -302 231 -264 261 -262 257 -258 291 -262 235 -300 231 -296 263 -234 299 -232 263 -294 267 -268 231 -264 261 -262 257 -292 231 -298 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -298 265 -270 267 -232 263 -294 233 -300 231 -298 229 -262 257 -258 291 -264 233 -302 265 -234 299 -266 233 -300 231 -264 257 -258 257 -294 267 -274 277 -270 231 -264 261 -294 229 -262 257 -290 231 -300 231 -296 263 -236 299 -264 235 -300 231 -262 259 -258 259 -260 295 -262 269 -268 265 -268 271 -236 261 -290 231 -300 231 -296 229 -296 261 -236 299 -264 269 -266 231 -264 261 -262 257 -290 231 -300 231 -298 263 -268 271 -270 231 -264 261 -262 255 -258 293 -230 293 -264 235 -300 263 -236 299 -266 233 -300 231 -264 257 -290 233 -298 265 -270 271 -270 231 -264 261 -294 229 -296 229 -262 257 -292 231 -298 265 -236 301 -232 261 -294 267 -268 231 -264 261 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -296 229 -296 229 -264 255 -292 231 -300 231 -296 -RAW_Data: 269 -272 235 -266 259 -256 259 -292 267 -268 233 -296 263 -234 299 -266 233 -300 231 -296 229 -264 255 -258 291 -264 267 -268 265 -268 271 -270 231 -264 257 -288 231 -300 265 -268 267 -266 235 -300 231 -294 229 -264 257 -290 231 -266 259 -292 267 -270 269 -266 235 -300 231 -262 257 -290 231 -300 231 -298 269 -272 235 -298 231 -262 261 -262 257 -290 231 -300 263 -270 267 -266 233 -300 231 -296 229 -262 257 -258 291 -262 267 -274 267 -268 235 -300 231 -294 229 -264 293 -268 267 -232 263 -296 233 -300 231 -296 229 -296 229 -262 257 -258 259 -296 267 -266 265 -270 271 -270 231 -262 261 -262 257 -292 231 -266 259 -292 233 -298 231 -296 263 -270 271 -268 231 -296 231 -262 257 -256 293 -262 233 -268 261 -292 231 -298 265 -236 299 -266 233 -300 231 -296 229 -262 257 -292 231 -298 231 -298 263 -268 271 -268 233 -264 261 -294 229 -264 257 -258 259 -294 233 -302 265 -234 299 -266 233 -300 231 -296 229 -264 257 -290 231 -298 233 -296 269 -272 235 -266 263 -262 255 -292 231 -300 231 -296 229 -296 261 -236 299 -264 235 -300 231 -264 257 -258 259 -292 269 -268 231 -296 263 -268 271 -270 231 -262 261 -296 229 -296 261 -236 299 -266 233 -300 231 -296 231 -262 257 -256 291 -230 295 -264 233 -302 263 -270 271 -268 233 -264 261 -262 257 -290 231 -300 231 -296 231 -294 263 -268 271 -270 231 -296 229 -264 255 -290 231 -300 265 -270 271 -270 231 -264 261 -262 255 -292 231 -298 233 -296 231 -262 295 -234 299 -266 235 -300 231 -294 231 -262 261 -294 229 -264 257 -290 233 -298 231 -296 263 -236 299 -266 235 -300 229 -264 257 -290 231 -300 231 -296 263 -234 299 -232 295 -264 233 -268 259 -292 231 -298 265 -236 299 -266 235 -300 231 -294 231 -262 257 -258 291 -228 295 -262 269 -274 267 -268 233 -300 231 -264 255 -258 291 -270 271 -270 237 -300 231 -296 229 -262 261 -262 257 -258 291 -264 267 -268 265 -236 299 -264 233 -300 231 -264 257 -258 259 -262 293 -264 269 -268 271 -238 263 -294 233 -266 259 -292 229 -300 263 -270 273 -270 231 -264 261 -294 229 -262 257 -292 231 -298 231 -296 263 -270 271 -270 231 -264 261 -262 255 -292 265 -272 273 -272 231 -264 261 -262 261 -262 257 -258 293 -262 267 -266 233 -264 295 -266 273 -268 233 -262 263 -294 229 -296 229 -264 259 -296 261 -236 299 -266 235 -300 231 -296 229 -262 257 -290 231 -266 259 -294 265 -272 267 -266 235 -268 259 -290 231 -266 259 -294 -RAW_Data: 231 -298 265 -270 273 -268 231 -264 261 -294 229 -296 229 -264 261 -262 255 -292 231 -300 263 -270 267 -266 235 -298 231 -296 231 -262 261 -294 229 -296 231 -262 293 -268 267 -266 235 -300 231 -262 263 -294 229 -296 263 -268 271 -268 233 -296 229 -262 257 -258 291 -262 267 -234 261 -292 263 -272 269 -266 235 -300 231 -262 261 -294 231 -294 231 -296 267 -272 235 -268 257 -290 231 -266 259 -260 259 -294 267 -268 265 -236 299 -264 235 -300 231 -262 257 -258 291 -262 267 -268 231 -264 295 -268 271 -270 231 -264 263 -294 227 -296 263 -268 273 -268 233 -262 261 -294 231 -262 257 -290 231 -300 231 -298 263 -268 271 -268 231 -264 261 -262 257 -258 293 -294 239 -278 239 -300 231 -260 261 -262 257 -258 291 -264 267 -268 231 -264 293 -234 301 -266 233 -300 231 -296 229 -262 257 -290 231 -300 265 -270 271 -270 231 -264 261 -262 257 -290 231 -266 261 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -294 231 -294 261 -268 267 -266 235 -300 231 -296 229 -262 261 -262 257 -290 233 -298 231 -298 263 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -230 295 -264 235 -300 263 -236 299 -266 233 -300 231 -296 231 -262 257 -256 293 -268 271 -270 237 -300 231 -296 229 -264 255 -290 231 -300 231 -298 263 -234 299 -264 235 -300 231 -296 229 -264 257 -258 259 -294 267 -268 265 -270 271 -268 231 -264 255 -292 231 -298 265 -270 267 -266 233 -300 231 -296 229 -262 257 -290 231 -300 231 -296 231 -262 295 -268 271 -270 231 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -262 257 -290 231 -300 231 -298 229 -262 295 -268 271 -270 233 -264 257 -288 231 -298 231 -298 231 -262 293 -268 273 -268 233 -264 261 -262 255 -292 231 -298 265 -270 273 -270 231 -264 261 -294 229 -262 259 -258 259 -294 267 -268 265 -234 299 -266 233 -300 231 -298 229 -262 261 -262 257 -290 231 -300 265 -270 267 -266 233 -300 231 -264 261 -294 229 -296 263 -268 271 -270 231 -264 261 -294 229 -296 229 -262 257 -290 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -258 259 -294 267 -234 261 -292 231 -298 265 -268 267 -266 235 -300 231 -294 229 -296 229 -262 257 -290 265 -272 275 -270 233 -264 255 -258 259 -292 269 -266 267 -234 299 -266 235 -300 231 -262 263 -262 255 -260 291 -230 293 -264 267 -274 269 -266 233 -300 231 -294 231 -294 263 -236 299 -264 267 -268 231 -264 261 -262 257 -258 291 -264 267 -234 -RAW_Data: 259 -292 231 -300 263 -270 273 -268 231 -264 257 -256 291 -230 293 -298 239 -278 237 -300 231 -262 255 -290 231 -298 233 -296 263 -236 299 -264 235 -300 231 -296 229 -264 261 -294 229 -296 229 -296 261 -270 271 -268 233 -262 261 -296 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 257 -290 231 -266 259 -292 233 -298 265 -270 271 -270 231 -264 261 -294 229 -264 261 -294 229 -296 263 -234 299 -266 233 -300 231 -296 231 -262 261 -294 229 -264 257 -290 265 -272 275 -270 233 -264 261 -260 261 -262 259 -290 233 -298 265 -270 271 -270 231 -264 261 -294 229 -264 257 -258 289 -264 267 -268 265 -234 299 -266 233 -302 229 -298 229 -262 261 -262 257 -290 233 -298 231 -298 263 -268 271 -270 231 -264 261 -294 229 -296 229 -296 261 -236 299 -264 235 -302 231 -264 261 -294 229 -262 257 -292 229 -300 265 -270 265 -266 235 -300 231 -294 231 -294 229 -296 263 -268 271 -268 233 -296 229 -262 257 -258 291 -262 269 -266 233 -262 295 -234 299 -266 235 -300 231 -262 257 -290 265 -266 233 -264 261 -294 261 -270 271 -270 231 -264 257 -258 289 -264 267 -274 267 -268 235 -300 229 -264 261 -294 229 -264 257 -290 231 -300 263 -270 273 -268 231 -264 261 -262 257 -258 293 -302 243 -274 235 -266 261 -260 257 -256 291 -264 267 -236 259 -292 231 -298 263 -236 301 -266 235 -300 229 -264 255 -258 293 -262 235 -300 263 -236 299 -266 267 -268 231 -264 261 -260 257 -258 293 -262 269 -266 233 -262 295 -234 299 -264 269 -268 231 -264 261 -294 261 -236 299 -266 235 -300 231 -296 229 -262 257 -290 231 -298 233 -264 257 -290 231 -300 265 -236 299 -266 233 -300 231 -296 231 -262 257 -288 231 -300 231 -298 263 -234 299 -266 233 -300 231 -264 257 -290 231 -300 265 -270 271 -270 231 -262 261 -262 257 -292 231 -266 259 -294 231 -298 231 -298 261 -270 271 -268 231 -264 257 -258 291 -262 269 -266 233 -296 269 -270 235 -266 259 -256 291 -262 267 -268 233 -262 295 -234 299 -266 235 -300 231 -262 257 -290 231 -298 233 -296 231 -262 295 -268 271 -270 231 -264 261 -262 261 -294 263 -236 301 -266 233 -300 231 -296 229 -294 231 -262 257 -290 231 -300 263 -270 273 -268 233 -262 261 -294 231 -262 257 -292 231 -298 271 -274 235 -268 261 -262 255 -290 265 -266 233 -296 263 -234 299 -264 235 -300 231 -296 229 -264 257 -256 291 -264 267 -268 265 -234 299 -266 233 -300 231 -296 231 -262 257 -256 291 -264 267 -268 265 -234 -RAW_Data: 299 -266 233 -300 231 -296 231 -294 229 -264 293 -268 267 -266 233 -300 231 -296 229 -264 257 -290 231 -298 233 -296 231 -262 293 -234 301 -264 235 -302 231 -262 261 -294 229 -264 257 -258 293 -262 267 -266 265 -236 299 -266 233 -300 231 -296 229 -264 255 -258 293 -294 241 -276 239 -268 263 -260 257 -290 231 -264 261 -292 233 -298 265 -236 299 -264 269 -268 231 -262 257 -258 291 -230 295 -262 235 -300 263 -270 271 -270 231 -264 255 -290 231 -300 265 -236 301 -232 263 -294 233 -300 231 -296 229 -296 229 -264 255 -290 231 -300 265 -270 271 -270 231 -264 261 -262 257 -258 291 -304 241 -276 233 -266 261 -262 261 -262 255 -292 265 -266 263 -270 267 -266 233 -300 231 -296 229 -262 263 -262 263 -262 261 -294 263 -268 273 -268 233 -264 261 -262 293 -236 299 -264 269 -268 231 -262 263 -262 261 -294 231 -262 257 -290 265 -266 265 -270 271 -270 231 -264 261 -262 255 -292 265 -266 231 -296 263 -236 299 -266 267 -266 231 -262 263 -262 263 -262 257 -258 291 -264 267 -272 269 -266 235 -300 231 -262 261 -264 255 -258 293 -262 267 -266 265 -236 301 -230 295 -262 267 -268 231 -264 257 -290 263 -234 259 -260 293 -262 267 -266 265 -236 299 -266 267 -266 231 -264 261 -262 263 -262 257 -290 265 -272 269 -266 269 -234 259 -290 265 -264 231 -264 297 -234 299 -266 267 -266 231 -264 261 -264 255 -290 265 -234 261 -292 265 -270 269 -266 269 -266 231 -262 263 -262 293 -236 299 -266 269 -268 229 -264 261 -262 255 -292 265 -234 259 -294 231 -298 231 -264 293 -268 273 -270 231 -264 261 -262 261 -262 257 -292 265 -264 273 -274 235 -266 257 -258 289 -262 267 -268 265 -236 299 -266 267 -266 231 -264 261 -264 255 -290 265 -266 231 -264 295 -270 271 -270 231 -264 261 -262 255 -290 265 -272 269 -268 235 -268 263 -262 261 -262 261 -264 257 -290 265 -266 231 -264 295 -270 273 -268 231 -264 261 -260 257 -290 265 -268 231 -264 295 -234 299 -266 269 -266 231 -262 263 -262 diff --git a/assets/resources/subghz/Jamming/Jam_433.42.sub b/assets/resources/subghz/Jamming/Jam_433.42.sub deleted file mode 100644 index 5429a21de..000000000 --- a/assets/resources/subghz/Jamming/Jam_433.42.sub +++ /dev/null @@ -1,15 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 433420000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: 45037 -268 199 -548 48975 -282 217 -544 48999 -244 217 -562 48941 -268 201 -548 49001 -250 217 -546 48981 -224 211 -560 48985 -248 253 -542 48967 -246 211 -566 48979 -228 219 -564 48973 -282 187 -560 48993 -250 215 -550 48963 -282 187 -560 48981 -282 217 -530 48961 -280 217 -526 48977 -282 217 -542 48979 -224 211 -562 48985 -242 245 -526 48977 -280 213 -530 48987 -248 217 -542 48979 -280 211 -532 48977 -282 217 -546 48973 -240 243 -524 48971 -280 211 -532 48997 -246 215 -574 48961 -250 211 -566 48965 -260 227 -528 48987 -280 181 -560 48981 -276 209 -526 48977 -280 217 -528 48981 -282 217 -542 48957 -278 211 -528 48971 -282 181 -564 48981 -270 203 -552 48983 -282 179 -576 48961 -280 217 -524 48973 -282 187 -560 48967 -280 217 -530 48979 -282 217 -524 48977 -248 251 -526 48973 -260 225 -532 48983 -276 217 -528 48987 -256 211 -562 48981 -252 215 -548 48965 -282 217 -542 48947 -280 217 -546 48949 -282 215 -552 48953 -274 219 -562 48965 -256 181 -594 48965 -282 185 -568 48957 -284 215 -544 48973 -238 253 -518 49005 -240 217 -532 49003 -244 217 -530 48979 -278 217 -530 48991 -248 211 -566 48957 -264 229 -540 48989 -246 193 -584 48971 -280 181 -558 48981 -248 215 -558 48987 -250 217 -552 48967 -284 217 -508 49051 -194 225 -570 48949 -246 253 -526 48989 -240 217 -566 48947 -282 215 -550 48947 -280 213 -566 48949 -282 217 -544 48979 -246 213 -570 48945 -282 217 -546 48979 -244 217 -562 48981 -228 217 -562 48983 -248 217 -554 48957 -284 185 -564 48995 -242 211 -566 48963 -262 191 -568 48977 -276 217 -524 48983 -262 223 -536 48983 -276 217 -524 48969 -284 185 -566 48965 -284 217 -544 48951 -272 205 -554 48989 -266 201 -546 48993 -232 231 -542 48999 -240 217 -564 48949 -272 217 -532 49003 -250 211 -534 48995 -258 191 -570 48973 -272 217 -532 48997 -248 187 -566 48973 -274 217 -524 49007 -250 215 -552 48969 -278 209 -526 48981 -270 203 -550 48993 -250 215 -552 48973 -246 253 -524 48975 -274 219 -526 48993 -242 219 -530 49007 -232 211 -562 48983 -246 253 -524 48977 -278 209 -526 48991 -246 215 -574 48961 -252 211 -564 48965 -284 217 -544 48947 -280 213 -532 48973 -280 211 -534 48985 -278 187 -560 48975 -276 217 -528 48965 -272 215 -566 48977 -242 217 -564 48967 -248 217 -556 48977 -248 253 -528 48953 -280 217 -528 48981 -284 217 -542 48967 -258 213 -560 48973 -238 253 -528 48979 -238 255 -528 48971 -240 253 -528 48967 -252 211 -564 48967 -264 229 -542 48965 -278 217 -530 49001 -248 215 -538 49003 -250 217 -548 48963 -278 211 -530 48969 -280 217 -556 48945 -276 217 -564 48953 -278 217 -528 48963 -286 181 -564 48983 -272 203 -552 -RAW_Data: 48975 -282 217 -542 48949 -278 219 -562 48957 -276 209 -528 48997 -246 215 -570 48975 -246 215 -534 49017 -238 217 -528 49025 -246 181 -576 48975 -238 203 -574 49021 -240 203 -546 48983 -242 207 -550 49023 -212 217 -540 48999 -240 217 -542 49019 -244 217 -542 48987 -236 201 -572 48999 -242 205 -548 49021 -202 219 -562 48995 -238 217 -556 48983 -238 217 -562 48997 -238 217 -524 49009 -240 217 -528 49023 -238 217 -528 49031 -202 219 -560 49003 -238 217 -528 48999 -232 223 -564 48993 -238 215 -530 49027 -206 237 -542 48993 -228 215 -560 49013 -240 217 -528 48999 -238 201 -570 49021 -194 215 -560 49027 -204 217 -562 48997 -232 211 -532 49015 -246 207 -550 49025 -202 219 -560 48995 -248 213 -528 49013 -248 215 -542 49013 -214 217 -536 49019 -212 247 -526 49013 -212 247 -526 49029 -214 217 -536 49017 -226 211 -564 48993 -238 219 -530 49013 -228 221 -560 48989 -238 215 -530 49017 -242 207 -548 49019 -214 191 -582 48995 -238 217 -530 49011 -246 217 -544 49005 -214 217 -540 49019 -240 217 -528 49027 -204 253 -528 49017 -204 253 -528 49013 -210 215 -558 49013 -214 217 -538 49015 -248 215 -546 48991 -246 217 -544 49001 -212 253 -506 49031 -214 247 -528 48995 -246 185 -566 49011 -212 253 -508 49031 -214 213 -558 49017 -216 215 -540 49007 -250 217 -534 49007 -246 217 -510 49027 -248 215 -528 48993 -242 217 -552 48993 -242 185 -568 49005 -244 215 -548 48997 -224 211 -566 49013 -232 195 -566 49011 -208 215 -568 48999 -228 189 -558 49003 -238 203 -572 49009 -212 221 -554 48999 -244 217 -546 48997 -248 185 -560 49003 -240 217 -530 49025 -208 251 -524 49021 -208 215 -558 49025 -210 217 -552 49013 -208 217 -556 48999 -250 213 -530 49009 -246 217 -544 48985 -248 215 -540 49025 -208 217 -554 49005 -212 253 -540 48993 -248 213 -526 49011 -240 217 -528 49013 -248 215 -542 48995 -240 219 -526 49029 -208 217 -554 48993 -228 215 -560 49019 -202 253 -528 49017 -206 237 -542 48991 -228 221 -540 49019 -238 217 -530 49013 -230 221 -564 48985 -240 181 -566 49009 -230 221 -530 49031 -212 211 -560 49025 -212 217 -538 49005 -244 215 -550 49003 -240 217 -526 49025 -202 219 -560 49025 -202 217 -564 49011 -238 181 -556 48997 -230 215 -562 49023 -206 215 -556 49011 -244 179 -578 48997 -246 213 -538 48997 -240 181 -566 49015 -240 205 -550 48991 -242 217 -550 49011 -246 175 -558 48997 -246 213 -538 49003 -242 215 -552 48991 -242 215 -552 49017 -212 215 -540 49015 -244 215 -546 49001 -246 211 -526 49013 -240 205 -552 49001 -238 217 -562 48997 -240 181 -562 49017 -238 181 -564 48989 -246 191 -582 49003 -238 181 -562 48999 -242 185 -570 49017 -236 219 -528 -RAW_Data: 49019 -238 217 -528 49001 -226 215 -562 48999 -246 215 -546 49009 -206 217 -556 49017 -240 217 -524 49019 -224 181 -596 48993 -246 211 -528 49011 -238 217 -528 49031 -202 219 -554 48999 -236 217 -556 49017 -202 217 -564 49005 -240 219 -524 49005 -246 213 -534 49019 -242 203 -546 49007 -242 205 -546 49011 -246 177 -560 49013 -240 219 -524 49019 -202 255 -526 49015 -240 217 -526 49009 -240 217 -530 49033 -238 217 -528 48999 -246 185 -560 49025 -202 219 -562 49009 -224 211 -564 49005 -206 203 -572 49001 -240 179 -568 49003 -238 233 -542 49005 -212 215 -576 48981 -244 215 -548 48985 -244 215 -552 49009 -246 181 -576 48975 -248 191 -580 48987 -238 217 -562 49005 -204 217 -562 49009 -238 217 -528 49021 -202 217 -564 48997 -244 185 -566 49025 -208 215 -558 49019 -208 215 -558 49017 -206 237 -542 49017 -212 211 -554 49011 -242 205 -546 48995 -248 213 -532 49027 -214 215 -540 49033 -214 191 -582 48983 -244 209 -554 49003 -246 213 -530 48999 -244 207 -554 49015 -248 175 -560 49009 -248 213 -528 49013 -248 211 -528 49009 -246 213 -530 49017 -246 217 -548 48969 -244 209 -554 49015 -240 217 -524 48999 -236 217 -564 49011 -204 217 -564 48989 -238 217 -528 49013 -238 217 -562 48985 -236 217 -564 49003 -208 215 -556 49023 -210 211 -556 49019 -238 181 -564 49013 -240 217 -524 49029 -208 215 -554 49009 -246 177 -560 49001 -244 207 -552 49019 -212 209 -556 49005 -244 185 -566 48997 -228 221 -538 49015 -244 207 -550 48995 -246 215 -536 49013 -246 217 -544 49009 -214 215 -540 49019 -246 217 -540 49011 -212 245 -522 49017 -214 215 -538 49019 -238 217 -530 49031 -204 217 -562 49017 -208 215 -556 49011 -242 217 -524 49017 -240 217 -524 49005 -246 215 -546 48991 -238 217 -528 49039 -204 217 -564 48991 -238 217 -530 49005 -228 223 -538 49041 -214 215 -540 48999 -242 215 -542 49013 -226 215 -562 49015 -208 215 -556 49005 -244 187 -560 49021 -204 217 -554 49025 -202 217 -554 49015 -232 179 -598 48979 -246 213 -534 49019 -238 217 -526 49015 -246 217 -544 48985 -244 217 -554 48979 -248 213 -536 49003 -244 207 -554 49023 -212 215 -538 49031 -212 215 -540 49035 -214 215 -536 49029 -214 215 -540 49005 -244 209 -554 49013 -208 251 -524 48999 -248 213 -534 49019 -212 251 -540 48991 -248 213 -528 49027 -194 211 -564 49009 -236 201 -570 48983 -242 217 -552 49019 -206 215 -556 48997 -242 209 -554 49017 -212 211 -556 49009 -242 205 -548 49005 -242 205 -548 48995 -244 207 -550 49011 -240 219 -524 49027 -208 215 -554 49017 -206 215 -560 49021 -208 215 -556 48993 -244 209 -554 49007 -242 205 -548 48989 -238 203 -572 49009 -240 205 -544 49003 -248 211 -528 -RAW_Data: 49013 -242 205 -548 48997 -242 207 -550 48999 -244 215 -548 49017 -208 215 -556 48997 -246 215 -546 49011 -206 217 -556 49005 -236 219 -556 48981 -238 211 -566 49003 -212 211 -558 49013 -248 175 -560 49007 -246 215 -532 49017 -240 179 -568 49001 -228 221 -540 49023 -240 205 -544 48993 -246 193 -580 49009 -202 217 -564 49009 -202 255 -554 48987 -240 217 -524 48997 -238 217 -562 48997 -246 185 -560 48997 -238 217 -562 48985 -244 215 -548 49009 -240 181 -558 49015 -246 181 -576 49005 -206 217 -556 48989 -244 215 -554 49003 -246 213 -528 49021 -240 217 -522 49013 -246 215 -544 49007 -210 215 -574 48979 -244 207 -552 49017 -212 215 -576 48971 -242 215 -552 48987 -244 215 -572 48999 -240 181 -572 48979 -232 225 -566 48999 -246 181 -574 48997 -246 181 -576 48995 -240 181 -560 48995 -242 185 -604 48993 -232 197 -570 49003 -238 179 -566 48999 -236 199 -572 48991 -228 217 -562 48987 -236 217 -562 48997 -236 197 -572 48989 -228 217 -562 49005 -238 181 -562 49005 -230 193 -594 49001 -242 181 -556 49001 -236 217 -562 48989 -228 221 -566 48967 -244 209 -558 48997 -236 217 -556 49005 -238 217 -528 49019 -238 217 -524 49007 -244 215 -548 48985 -242 215 -552 48995 -236 217 -564 48981 -244 193 -580 48995 -238 219 -526 49023 -240 181 -560 49009 -246 211 -530 48997 -246 215 -538 49019 -246 211 -528 49015 -246 177 -562 48999 -246 215 -536 49027 -212 215 -540 49019 -246 215 -544 48981 -246 215 -536 49035 -204 217 -562 48983 -242 217 -552 49007 -246 211 -526 49009 -246 213 -530 49027 -206 215 -572 48979 -232 225 -564 48995 -244 217 -542 49005 -238 181 -564 48997 -226 211 -566 48991 -242 209 -554 48991 -242 217 -554 48985 -242 217 -554 49019 -202 217 -564 49003 -236 217 -562 48987 -246 179 -576 48999 -246 179 -576 48995 -240 217 -526 49029 -202 217 -564 49021 -208 215 -554 48995 -246 215 -534 49009 -246 215 -546 48985 -242 217 -550 49007 -248 179 -576 49007 -248 217 -544 48979 -246 179 -578 48979 -244 217 -550 48995 -248 193 -546 49033 -212 191 -584 49011 -206 217 -556 49025 -208 215 -554 48991 -244 215 -552 49005 -246 185 -558 48999 -244 185 -566 49021 -206 251 -560 48987 -212 191 -582 48999 -226 215 -562 48989 -246 215 -532 49033 -208 215 -574 48985 -238 217 -528 49009 -246 213 -534 49029 -206 215 -556 49023 -202 217 -564 48985 -236 217 -562 48989 -236 211 -564 49009 -232 195 -566 48983 -242 187 -570 49013 -238 219 -526 49035 -204 203 -574 49011 -242 203 -542 48989 -238 201 -572 48993 -242 207 -550 48989 -238 203 -576 48983 -246 209 -552 48997 -244 209 -554 49019 -204 203 -574 49013 -210 211 -558 49005 -246 185 -562 49007 -228 213 -562 -RAW_Data: 49011 -212 211 -556 48995 -244 215 -552 49017 -212 211 -554 49007 -248 213 -528 49005 -248 213 -532 49025 -212 217 -538 49013 -244 217 -546 48993 -240 217 -530 49023 -212 253 -506 49009 -244 209 -554 49007 -248 213 -526 49003 -248 215 -532 48999 -244 215 -552 49021 -210 215 -554 48993 -244 217 -548 49003 -208 251 -526 49003 -248 213 -534 49009 -248 213 -530 49025 -214 215 -540 49033 -214 215 -536 49021 -212 247 -524 49025 -214 215 -540 49003 -244 215 -552 49013 -212 211 -558 48997 -242 217 -550 49021 -214 215 -536 49031 -214 215 -540 49011 -248 213 -530 49007 -246 215 -546 48989 -248 215 -532 49025 -212 217 -540 49021 -246 217 -540 49009 -214 217 -536 49025 -212 213 -558 49013 -212 251 -542 48981 -246 215 -548 49001 -212 251 -540 49003 -212 215 -542 49037 -208 215 -556 49015 -204 253 -528 48997 -228 213 -562 48995 -248 213 -532 49021 -212 215 -576 48993 -242 205 -546 48995 -244 207 -550 49005 -240 217 -528 49031 -208 215 -544 49015 -236 219 -528 49013 -246 185 -564 49025 -208 217 -554 49011 -212 251 -508 49027 -226 211 -532 49043 -198 211 -562 49007 -232 211 -564 49009 -202 211 -564 49009 -240 181 -566 49003 -246 185 -568 49015 -234 197 -566 48989 -228 221 -536 49041 -204 217 -562 49013 -198 227 -564 48989 -228 221 -538 49027 -240 217 -528 49009 -238 219 -526 49015 -248 213 -528 49005 -248 215 -532 49015 -246 217 -510 49025 -246 217 -510 49019 -244 217 -548 49009 -208 215 -558 49001 -238 217 -530 49039 -198 211 -564 49019 -202 211 -564 49009 -238 219 -528 49009 -228 223 -528 49017 -228 215 -560 49013 -238 217 -530 48999 -236 201 -568 49021 -194 217 -560 49013 -238 219 -528 49001 -236 201 -572 49019 -196 215 -560 49021 -202 255 -518 49005 -236 199 -570 49019 -194 217 -560 49013 -238 215 -530 49027 -206 237 -540 49021 -194 217 -560 215 -530 49031 -204 203 -572 49009 -202 255 -528 49021 -206 205 -574 49019 -196 215 -560 49013 -238 219 -516 49013 -244 207 -550 49023 -194 217 -560 49013 -238 217 -514 49047 -216 217 -536 49027 -194 217 -560 49015 -238 215 -530 49003 -236 201 -570 49009 -238 217 -530 49001 -236 201 -570 49001 -238 215 -516 49045 -212 209 -554 48997 -226 221 -538 49039 -204 217 -564 49013 -198 227 -562 49019 -194 215 -562 49013 -238 217 -516 49013 -244 209 -550 48999 -228 219 -540 49019 -240 217 -528 49013 -228 221 -526 49017 -228 215 -560 49013 -202 253 -530 49023 -206 203 -572 49019 -194 215 -560 49019 -202 253 -530 49025 -204 203 -572 49019 -194 217 -560 49017 -238 217 -530 49021 -206 239 -542 48987 -228 221 -538 49039 -204 217 -562 48997 -228 223 -528 49017 -226 215 -562 49015 -202 253 -518 49015 -242 -RAW_Data: 205 -546 49021 -194 217 -560 49015 -238 217 -530 49023 -206 239 -540 48993 -228 215 -560 49013 -238 217 -530 49019 -206 237 -542 48993 -228 213 -562 49013 -240 217 -528 49027 -204 203 -570 49021 -196 215 -560 49013 -238 219 -528 49019 -206 237 -542 49021 -194 217 -560 49013 -238 215 -532 49029 -206 203 -572 49021 -196 215 -560 49013 -238 217 -566 48977 -242 205 -544 49021 -194 217 -560 49013 -238 217 -516 49035 -214 215 -540 49029 -226 189 -558 49015 -226 215 -560 49013 -204 253 -528 49021 -206 203 -572 49021 -194 221 -538 49039 -204 253 -530 49005 -226 189 -558 49017 -244 217 -544 49019 -204 217 -562 49009 -208 215 -556 48993 -244 209 -552 49021 -208 217 -554 48995 -238 217 -530 49029 -222 211 -534 49045 -212 211 -552 48999 -250 215 -542 49011 -214 217 -538 49035 -210 215 -556 49001 -246 217 -544 48987 -236 219 -528 49011 -238 217 -530 49045 -208 215 -556 49009 -212 211 -558 49017 -212 213 -558 49027 -202 217 -564 49017 -228 197 -568 48983 -240 217 -530 49011 -228 233 -530 49007 -238 217 -530 49019 -234 199 -568 49021 -194 221 -538 49039 -204 217 -564 49013 -198 227 -562 49019 -194 217 -560 49013 -238 215 -530 49017 -244 209 -548 48991 -238 215 -530 49019 -242 205 -546 48993 -228 221 -538 49019 -238 219 -528 49013 -228 233 -528 49009 -236 217 -530 49023 -234 197 -568 49019 -196 215 -560 49013 -238 215 -518 49011 -246 209 -550 49023 -194 217 -560 49015 -238 217 -516 49043 -212 209 -554 49021 -196 217 -558 49015 -238 215 -530 49031 -206 235 -540 48991 -228 221 -540 49017 -240 217 -530 49021 -228 195 -570 49015 -202 255 -526 49017 -198 227 -562 49019 -194 217 -560 49021 -202 255 -528 49011 -234 195 -564 48995 -240 217 -530 49021 -206 215 -566 49021 -200 225 -562 49009 -206 253 -522 49015 -226 193 -568 49009 -238 217 -528 49023 -198 229 -564 48987 -226 215 -562 49017 -202 253 -518 49029 -206 237 -542 49021 -194 215 -562 49013 -238 217 -518 49013 -244 207 -552 48995 -228 221 -536 49039 -204 217 -562 49013 -198 229 -562 49019 -196 215 -560 49013 -238 215 -530 49019 -206 239 -544 49023 -194 215 -562 49011 -240 217 -528 49019 -240 205 -542 49025 -196 221 -538 49019 -240 217 -530 49009 -230 221 -562 48983 -228 215 -560 49013 -238 217 -530 48999 -236 201 -570 49021 -194 215 -560 49013 -238 217 -530 49025 -206 237 -542 49021 -196 215 -562 49011 -240 217 -528 49021 -206 237 -544 48997 -226 221 -538 49019 -240 217 -528 49015 -228 221 -560 48991 -238 215 -532 49019 -242 205 -544 48993 -228 221 -538 49019 -238 217 -530 49015 -230 231 -532 49013 -238 217 -564 48995 -198 225 -560 48987 -228 215 -560 49013 -240 217 -528 49023 -206 -RAW_Data: 237 -540 49021 -194 215 -562 49013 -238 215 -532 49019 -206 239 -542 49021 -194 215 -560 49023 -240 217 -528 49003 -238 217 -564 49003 -202 217 -564 49011 -202 217 -564 48993 -238 217 -530 49043 -204 217 -562 49011 -196 215 -562 49019 -238 217 -518 49025 -242 205 -542 48993 -228 221 -538 49027 -240 219 -526 49039 -200 227 -562 48989 -240 217 -516 49017 -242 207 -548 49019 -194 217 -560 49027 -204 217 -564 48997 -236 199 -568 48999 -238 215 -546 48993 -244 209 -550 49019 -208 215 -546 49017 -234 197 -568 49005 -238 217 -518 49023 -242 205 -546 48999 -226 221 -536 49041 -204 217 -564 48983 -230 235 -530 49019 -238 217 -524 49021 -234 197 -564 49021 -196 215 -560 49013 -238 215 -530 49017 -242 203 -544 49021 -194 217 -560 49013 -240 215 -516 49043 -212 211 -554 49021 -194 217 -560 49013 -238 215 -530 49027 -206 237 -542 49023 -194 215 -560 49013 -238 217 -516 49035 -214 215 -542 49027 -226 189 -558 49015 -228 213 -560 49011 -240 217 -528 49019 -204 239 -542 49019 -194 217 -560 49013 -238 215 -532 49017 -244 203 -544 49021 -194 223 -538 49017 -238 253 -526 48983 -230 223 -562 48989 -228 213 -562 49015 -202 253 -528 49011 -234 195 -564 49005 -238 217 -530 49005 -234 199 -570 49013 -208 215 -556 48997 -246 215 -550 49015 -214 215 -538 49033 -244 217 -526 48995 -212 251 -542 49013 -214 217 -534 49023 -206 253 -522 49023 -194 217 -560 49015 -238 215 -548 49003 -212 251 -508 49029 -226 195 -570 49015 -202 255 -524 49013 -234 195 -566 49019 -194 215 -560 49013 -238 217 -528 49015 -242 205 -546 49025 -194 221 -538 49039 -204 217 -560 49013 -200 229 -564 49019 -194 215 -560 49013 -238 217 -528 49027 -206 237 -540 49021 -194 217 -560 49019 -208 251 -524 48995 -246 215 -536 49003 -246 217 -548 48999 -238 219 -526 49015 -220 211 -570 49015 -198 227 -564 48985 -246 215 -550 49001 -240 217 -564 48983 -212 213 -556 49033 -210 215 -552 49019 -208 217 -554 49015 -208 215 -544 49033 -214 191 -582 48993 -244 217 -544 48993 -236 219 -562 48987 -244 217 -544 49013 -212 217 -538 49031 -246 217 -544 48989 -212 211 -554 49025 -208 217 -554 49013 -212 215 -544 49025 -246 213 -526 49027 -212 209 -556 49017 -214 215 -540 49013 -238 217 -528 49009 -236 219 -554 49011 -238 219 -524 49007 -244 217 -546 49003 -242 217 -524 48999 -244 209 -552 48997 -244 217 -546 49011 -210 213 -556 49009 -246 213 -528 49009 -244 217 -544 49015 -204 211 -568 48985 -226 217 -560 49017 -248 175 -560 49019 -240 181 -568 49017 -204 203 -574 49015 -206 217 -556 49019 -206 217 -558 49009 -246 217 -542 49003 -212 211 -560 49013 -212 211 -560 49043 -214 217 -536 48993 -244 -RAW_Data: 217 -548 49001 -228 195 -568 49017 -204 253 -528 49003 -236 219 -524 49021 -238 217 -516 49031 -206 239 -544 49003 -238 215 -532 49021 -242 205 -544 49001 -228 213 -560 49017 -212 209 -556 48993 -244 209 -554 48993 -238 217 -562 49005 -240 217 -528 49005 -230 211 -534 49013 -230 225 -562 48995 -240 217 -526 49017 -238 217 -528 49005 -248 193 -546 49027 -240 217 -526 48997 -244 215 -550 49007 -238 217 -524 49031 -214 215 -540 49017 -234 197 -570 49003 -234 197 -566 49005 -248 175 -560 49005 -234 199 -570 49009 -242 205 -552 48979 -238 201 -574 49011 -212 215 -574 48991 -240 219 -522 48997 -246 193 -582 48995 -240 217 -526 49005 -242 215 -550 48991 -244 217 -548 49005 -240 217 -526 49003 -244 217 -548 48991 -238 217 -564 49005 -206 217 -554 48995 -236 217 -564 49005 -240 181 -558 49015 -240 217 -524 48995 -236 219 -560 48989 -236 217 -564 48983 -236 219 -562 49007 -238 181 -564 48995 -242 217 -546 48991 -248 213 -536 49025 -210 215 -544 49017 -218 213 -570 49005 -234 197 -568 48995 -240 217 -528 49031 -206 215 -558 48993 -244 209 -552 48997 -244 213 -576 48989 -212 217 -540 49015 -246 213 -530 49027 -210 211 -554 49019 -248 175 -560 49009 -244 217 -542 48987 -238 217 -530 49009 -246 215 -536 49019 -240 217 -526 49027 -212 209 -556 49027 -214 215 -538 49011 -248 213 -530 49017 -212 253 -540 48991 -240 219 -526 49021 -202 253 -530 49023 -198 225 -562 49011 -204 217 -554 49013 -242 205 -544 49023 -194 217 -560 49015 -238 217 -528 49019 -242 205 -544 49023 -194 221 -538 49041 -202 217 -564 49017 -198 225 -562 48991 -224 211 -564 48997 -230 223 -562 48987 -244 207 -550 48995 -248 217 -540 49007 -248 213 -526 49031 -214 217 -536 49021 -212 217 -540 49025 -210 253 -506 49017 -246 215 -550 49013 -212 211 -556 48999 -248 213 -532 49013 -246 213 -530 49015 -212 215 -542 49039 -206 215 -558 49005 -246 215 -544 48995 -238 181 -566 49017 -246 211 -528 49013 -244 217 -542 48991 -240 217 -530 49003 -244 209 -554 49015 -212 215 -574 48991 -248 211 -526 49029 -212 215 -540 49033 -214 215 -538 49013 -246 213 -530 48999 -242 215 -552 49001 -246 211 -532 48997 -242 215 -554 48991 -242 217 -552 48999 -246 213 -532 49015 -246 217 -548 48985 -244 217 -544 48989 -246 215 -546 48987 -246 215 -534 49025 -206 215 -558 49021 -240 181 -560 49007 -240 217 -526 49007 -244 215 -550 48987 -236 217 -564 48991 -244 217 -546 48981 -248 183 -568 49009 -238 217 -562 48975 -248 215 -534 49029 -212 215 -542 49031 -210 211 -556 49021 -212 215 -540 49027 -240 217 -526 49011 -248 215 -542 48989 -248 213 -532 49011 -240 217 -526 49013 -236 217 -556 49001 -206 -RAW_Data: 253 -522 49015 -248 185 -558 49021 -208 215 -576 48981 -226 221 -568 48989 -228 197 -570 48993 -242 207 -552 49017 -238 217 -564 48953 -244 209 -554 49015 -240 217 -522 49007 -238 217 -562 48989 -238 217 -530 49007 -244 215 -548 49011 -212 217 -574 48989 -244 217 -542 48985 -248 213 -534 49003 -246 215 -534 49021 -248 179 -578 48971 -242 217 -552 49013 -244 179 -576 48975 -258 187 -562 48993 -246 213 -572 48977 -244 215 -548 48995 -244 213 -532 49019 -246 213 -528 48997 -242 209 -554 49005 -246 215 -544 48997 -246 215 -544 48995 -246 215 -542 48979 -244 209 -556 49009 -242 203 -548 49005 -248 211 -528 48995 -246 215 -538 49003 -244 215 -552 49019 -212 215 -576 48965 -248 191 -582 48993 -248 211 -530 49021 -206 215 -570 48993 -236 199 -570 48985 -248 191 -580 48985 -246 193 -578 48979 -244 215 -550 49003 -238 217 -524 49019 -238 217 -528 49019 -240 217 -526 49011 -240 217 -530 49013 -238 217 -530 49039 -202 219 -554 49021 -210 215 -554 49007 -210 253 -540 49001 -212 215 -542 49031 -204 253 -528 49015 -214 215 -540 49013 -246 215 -530 49019 -240 217 -526 49023 -206 215 -570 48983 -228 211 -564 49027 -210 211 -554 49027 -202 217 -562 48991 -244 215 -548 49001 -244 217 -544 48981 -238 211 -566 48985 -230 235 -528 49017 -242 207 -548 49007 -246 217 -540 48997 -238 217 -524 49025 -238 219 -526 49009 -238 217 -528 49005 -244 215 -550 49001 -246 185 -560 48997 -236 217 -556 49005 -238 217 -524 49009 -246 215 -548 48989 -246 193 -580 48981 -246 215 -548 48985 -242 187 -570 49023 -240 181 -560 49003 -248 213 -532 49025 -246 215 -546 48981 -246 211 -528 49009 -246 215 -546 48999 -246 217 -540 48997 -238 219 -524 49025 -212 215 -574 49003 -206 215 -558 49021 -206 215 -556 48993 -238 217 -562 48995 -236 217 -524 49023 -238 217 -528 49037 -204 217 -554 48993 -236 217 -564 49003 -202 255 -524 49013 -240 217 -528 49007 -246 215 -544 49019 -208 215 -554 49009 -214 251 -546 48991 -212 217 -540 49019 -240 217 -528 49013 -240 217 -528 49001 -236 217 -562 48999 -244 215 -544 48991 -244 215 -546 48985 -242 215 -552 48993 -236 219 -562 48981 -242 217 -550 49017 -246 215 -546 48981 -248 213 -526 49009 -240 217 -528 49039 -194 215 -560 49011 -212 251 -526 49013 -208 251 -528 49017 -212 213 -558 49003 -246 217 -544 48991 -240 217 -530 49011 -236 217 -556 49011 -194 217 -558 49019 -214 251 -506 49045 -210 215 -552 49015 -202 255 -526 49015 -214 191 -584 48987 -236 217 -556 49021 -204 217 -564 48991 -228 223 -562 48991 -238 217 -548 48981 -238 203 -572 49023 -204 217 -564 49001 -240 217 -528 49003 -226 219 -540 49021 -234 197 -566 49001 -240 -RAW_Data: 217 -526 49021 -240 217 -524 49003 -248 215 -534 49017 -238 217 -562 48969 -244 217 -548 49019 -208 215 -554 48989 -244 217 -550 49013 -208 215 -558 49011 -248 179 -544 49007 -246 213 -572 48975 -244 215 -550 48993 -244 185 -564 49003 -236 217 -556 49013 -238 179 -564 48999 -236 217 -562 48981 -236 219 -554 49013 -202 211 -566 48995 -244 209 -554 49013 -212 215 -576 48971 -240 217 -554 48989 -252 181 -568 49011 -228 221 -540 48999 -230 227 -564 48987 -248 211 -534 49011 -244 217 -544 48991 -244 215 -548 49001 -240 217 -524 48999 -228 223 -574 49001 -240 181 -558 49021 -242 179 -572 48991 -262 191 -568 48969 -248 213 -536 49003 -246 215 -536 49031 -208 215 -556 49001 -246 215 -546 48985 -244 215 -550 48985 -242 185 -572 49027 -238 181 -554 49013 -238 217 -528 49019 -238 219 -528 49023 -212 211 -560 49007 -242 205 -550 49019 -212 209 -554 49013 -246 213 -524 49023 -242 169 -576 48989 -238 201 -574 49007 -240 205 -544 48991 -244 207 -552 49017 -232 195 -566 48983 -244 209 -554 49011 -234 195 -568 48993 -246 213 -532 49005 -246 215 -546 48985 -244 215 -552 48999 -246 213 -530 49003 -242 217 -550 49005 -246 181 -576 48989 -236 219 -564 48969 -236 219 -562 48995 -224 211 -568 48989 -228 215 -562 49013 -246 175 -560 49011 -242 205 -546 49009 -218 213 -574 48985 -236 181 -566 49029 -234 195 -568 48979 -250 181 -568 49019 -240 217 -524 49025 -212 215 -574 48989 -240 217 -526 49019 -238 217 -526 49003 -246 215 -536 49013 -220 211 -572 49003 -240 203 -548 49015 -206 215 -558 49023 -204 217 -564 49003 -218 211 -544 49041 -202 217 -562 49017 -206 217 -554 49007 -224 211 -566 48989 -236 201 -572 48993 -238 217 -562 48987 -244 217 -544 48999 -218 211 -576 48993 -240 217 -562 48973 -244 205 -550 49021 -214 215 -570 48989 -246 179 -578 48991 -246 215 -544 48999 -246 217 -540 48995 -248 215 -542 48993 -248 211 -528 49007 -242 207 -550 49015 -212 209 -558 49015 -246 179 -578 48977 -238 203 -574 48999 -246 213 -528 49001 -246 193 -580 49007 -202 217 -562 48997 -238 217 -530 49029 -238 217 -524 48997 -244 185 -570 49007 -236 219 -560 48979 -238 217 -562 48981 -244 217 -550 49009 -238 217 -528 49019 -238 217 -528 49023 -206 215 -558 49001 -240 217 -530 49003 -246 215 -536 49029 -212 215 -540 49049 -212 191 -586 48975 -242 207 -550 49017 -206 237 -542 49017 -206 205 -574 49013 -198 229 -564 48991 -236 199 -570 48987 -238 201 -572 49001 -242 205 -548 49005 -236 219 -524 49035 -202 219 -562 49017 -198 211 -564 49025 -212 191 -582 49001 -238 217 -524 49021 -238 219 -526 49007 -238 217 -562 48985 -248 193 diff --git a/assets/resources/subghz/Jamming/Jam_433.92.sub b/assets/resources/subghz/Jamming/Jam_433.92.sub deleted file mode 100644 index 65589d4e4..000000000 --- a/assets/resources/subghz/Jamming/Jam_433.92.sub +++ /dev/null @@ -1,16 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 433920000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: -614 2689 -200 1805 -200 21705 -298 191 -636 48887 -278 215 -618 48867 -310 213 -612 48873 -300 191 -638 48871 -312 183 -634 48869 -284 217 -596 48903 -272 221 -604 48901 -306 185 -626 48879 -300 207 -598 48889 -280 249 -584 48883 -302 187 -624 48909 -268 217 -594 48899 -278 251 -596 48881 -280 215 -598 48917 -252 215 -624 48905 -278 217 -588 48941 -244 215 -598 48937 -254 215 -594 48943 -232 229 -610 48907 -278 213 -600 48917 -242 255 -598 48899 -278 173 -632 48921 -244 251 -576 48927 -274 211 -598 275 -362 231 -436 131 -1192 131 -534 231 -336 65 -100 1957 -68 42797 -242 251 -584 48937 -242 213 -606 48927 -238 243 -596 48929 -242 217 -620 48891 -280 217 -598 48899 -280 215 -622 48891 -252 243 -594 48905 -276 213 -606 48931 -242 219 -626 48893 -252 217 -628 48885 -278 217 -626 48881 -278 215 -630 48893 -266 193 -634 48895 -254 221 -636 48901 -272 211 -598 48895 -274 251 -574 48917 -278 211 -600 48913 -278 215 -598 48891 -264 231 -612 48913 -274 209 -600 48925 -240 243 -592 48931 -238 243 -588 48899 -278 215 -586 48949 -238 243 -590 48917 -242 243 -596 48925 -222 259 -596 48923 -232 231 -610 48923 -244 251 -588 48919 -240 243 -590 48909 -278 213 -598 48941 -242 213 -602 48913 -242 253 -596 48909 -274 211 -600 48919 -240 245 -596 48905 -254 245 -594 48913 -274 211 -600 48903 -260 229 -612 48901 -274 249 -570 48915 -252 219 -624 48909 -244 251 -582 48901 -268 229 -610 48907 -280 215 -600 48911 -276 215 -580 48929 -276 215 -580 48921 -278 215 -600 48919 -232 231 -614 48899 -268 221 -604 48899 -284 229 -596 48923 -234 223 -602 48915 -278 211 -610 48899 -268 223 -602 48919 -258 225 -606 48905 -274 213 -602 48909 -276 213 -600 48911 -268 239 -586 48917 -242 251 -582 48905 -276 213 -604 48909 -274 213 -606 48905 -278 213 -612 48899 -278 215 -604 48919 -236 223 -602 48921 -276 215 -606 48887 -302 209 -596 48899 -272 255 -594 48887 -274 213 -608 48883 -312 213 -582 48891 -286 229 -596 48905 -268 219 -626 48891 -278 211 -602 48909 -274 211 -600 48903 -272 249 -572 48923 -268 201 -618 48897 -274 217 -596 48931 -274 185 -628 48887 -274 255 -594 48887 -272 247 -570 48919 -266 225 -602 48891 -274 253 -560 48901 -288 225 -602 48909 -270 211 -602 48893 -284 217 -594 48903 -284 231 -596 48895 -286 221 -600 48901 -278 211 -614 48887 -268 241 -594 48891 -304 183 -628 48903 -272 211 -602 48919 -270 211 -600 48903 -274 251 -586 48905 -274 217 -596 48889 -292 199 -614 48911 -270 211 -604 48909 -266 227 -604 48913 -274 217 -596 48917 -236 223 -602 48937 -236 223 -632 48879 -272 253 -592 48887 -274 253 -558 48909 -270 223 -600 48911 -RAW_Data: -274 215 -602 48907 -276 249 -562 48925 -274 215 -604 48901 -270 251 -568 48923 -252 215 -626 48901 -272 211 -602 48901 -270 249 -574 48913 -272 215 -622 48881 -270 223 -602 48891 -304 215 -616 48869 -286 225 -606 48885 -270 223 -630 48863 -286 217 -626 48895 -262 239 -594 48895 -270 221 -630 48885 -270 227 -608 48887 -270 217 -628 48879 -302 187 -628 48883 -270 219 -628 48883 -304 213 -610 48877 -292 197 -616 48899 -270 227 -606 48897 -270 231 -596 48915 -268 217 -592 48901 -302 215 -578 48927 -266 231 -596 48881 -286 223 -604 48879 -304 217 -596 48885 -288 223 -606 48895 -272 217 -628 48891 -272 219 -596 48907 -266 229 -608 48871 -290 225 -608 48879 -310 217 -594 48909 -266 215 -594 48909 -272 215 -628 48867 -312 217 -594 48909 -266 223 -604 48897 -264 237 -592 48913 -264 223 -608 48887 -290 219 -602 48905 -264 235 -588 48911 -262 217 -628 48879 -264 221 -634 48877 -278 251 -590 48895 -270 211 -600 48919 -270 211 -600 48915 -280 217 -596 48893 -264 221 -634 48909 -246 253 -590 48883 -270 211 -628 48889 -278 251 -590 48909 -262 215 -592 48925 -256 215 -626 48899 -254 213 -628 48907 -248 253 -592 48881 -280 217 -626 48889 -270 211 -596 48933 -262 223 -602 48917 -272 181 -630 48913 -274 217 -582 48931 -272 209 -596 48919 -276 217 -598 48901 -266 237 -584 48897 -268 241 -592 48909 -274 215 -616 48891 -280 217 -582 48909 -262 229 -612 48901 -260 217 -628 48907 -262 223 -602 48895 -270 211 -628 48915 -242 211 -632 48891 -280 219 -596 48895 -278 217 -598 48915 -256 215 -624 48897 -278 211 -600 48923 -280 219 -596 48883 -286 213 -628 48887 -276 213 -600 48923 -272 181 -630 48885 -308 181 -632 48907 -280 217 -594 48907 -264 223 -604 48873 -294 235 -588 48883 -290 225 -606 48903 -264 237 -594 48893 -300 205 -596 48905 -264 227 -608 48893 -300 205 -596 48915 -264 223 -606 48899 -276 213 -602 48903 -266 241 -592 48911 -276 217 -580 48923 -264 225 -606 48901 -280 217 -596 48907 -270 211 -598 48935 -262 221 -602 48899 -274 217 -612 48915 -246 249 -572 48911 -260 217 -630 48903 -274 215 -580 48933 -282 217 -594 48915 -260 189 -632 48923 -230 213 -626 48903 -260 217 -628 48913 -260 225 -596 48909 -280 219 -594 48905 -260 227 -596 48899 -280 217 -596 48917 -274 215 -612 48895 -280 217 -584 48921 -264 225 -608 48903 -262 227 -604 48925 -266 199 -612 48901 -264 227 -606 48907 -262 225 -606 48925 -258 189 -630 48909 -262 223 -602 48915 -262 221 -602 48919 -260 219 -602 48911 -260 223 -604 48899 -262 229 -608 48909 -262 223 -604 48899 -272 215 -610 48929 -244 211 -598 48921 -280 217 -596 48905 -256 213 -624 48921 -276 219 -588 48907 -RAW_Data: -260 215 -628 48891 -274 215 -614 48887 -280 215 -610 48917 -280 217 -576 48927 -254 215 -594 48915 -268 213 -628 48915 -276 215 -584 48907 -266 239 -590 48917 -258 219 -596 48957 -240 217 -588 48931 -268 203 -614 48919 -274 215 -580 48941 -244 215 -586 48947 -230 225 -596 48949 -230 223 -604 48963 -240 213 -600 48923 -244 251 -572 48953 -230 231 -598 48927 -238 219 -596 48933 -264 193 -630 48915 -266 185 -628 48925 -242 217 -628 48899 -268 201 -618 48933 -234 213 -630 48901 -276 217 -596 48907 -264 231 -596 48917 -276 217 -594 48901 -266 193 -636 48917 -230 225 -634 48903 -274 217 -594 48929 -234 213 -630 48925 -234 211 -630 48907 -276 217 -594 48899 -262 227 -606 48923 -264 183 -630 48907 -264 215 -596 48947 -240 215 -598 48935 -232 217 -626 48921 -240 253 -594 48913 -234 213 -628 48947 -242 217 -596 48921 -258 229 -598 48921 -244 215 -624 48935 -238 203 -616 48927 -230 223 -594 48953 -238 217 -622 48929 -232 229 -602 48945 -240 215 -598 48927 -234 233 -608 48919 -230 217 -626 48933 -240 217 -596 48939 -232 223 -628 48925 -230 221 -606 48951 -240 217 -596 48937 -234 231 -606 48939 -228 231 -596 48931 -230 215 -626 48935 -238 213 -614 48921 -232 211 -628 48949 -228 217 -594 48947 -228 233 -596 48961 -198 227 -598 48947 -244 207 -622 48931 -244 207 -618 48951 -210 247 -590 48939 -246 213 -598 48931 -244 209 -620 48957 -210 211 -622 48941 -244 205 -612 48939 -246 213 -592 48963 -210 211 -622 48955 -210 211 -622 48935 -242 207 -616 48937 -242 207 -612 48927 -246 207 -618 48959 -212 209 -620 48937 -242 207 -614 48945 -212 247 -590 48935 -244 207 -618 48935 -246 213 -598 48937 -246 215 -598 48939 -246 215 -602 48941 -248 213 -594 48965 -212 209 -622 48935 -248 213 -596 48955 -212 211 -622 48951 -210 247 -592 48937 -242 207 -618 48955 -204 203 -638 48943 -240 205 -608 48931 -244 217 -580 48973 -212 245 -588 48959 -210 211 -622 48933 -246 215 -600 48933 -244 209 -620 48951 -206 237 -608 48945 -206 237 -608 48945 -212 247 -592 48957 -210 211 -622 48955 -210 211 -624 48945 -212 247 -592 48955 -210 211 -624 48939 -244 207 -614 48931 -248 213 -598 48945 -248 213 -596 48931 -246 215 -602 48937 -242 207 -620 48953 -210 211 -622 48947 -242 203 -612 48943 -242 205 -610 48951 -212 215 -608 48957 -212 247 -590 48955 -210 211 -624 48951 -212 211 -626 48961 -212 209 -618 48927 -246 207 -618 48943 -246 213 -594 48933 -246 207 -618 48957 -214 217 -602 48961 -210 211 -626 48929 -244 209 -618 48929 -244 207 -618 48933 -244 207 -616 48947 -210 247 -592 48959 -210 209 -622 48931 -244 207 -618 48939 -242 205 -616 48949 -210 211 -624 48941 -RAW_Data: -246 213 -596 48957 -212 211 -622 48957 -210 211 -622 48937 -244 207 -616 48947 -212 211 -622 48941 -246 215 -598 48931 -244 209 -618 48955 -212 209 -620 48953 -212 209 -624 48937 -242 207 -618 48937 -242 205 -614 48955 -212 209 -622 48941 -242 207 -612 48937 -246 213 -596 48955 -210 211 -624 48955 -212 211 -622 48945 -248 211 -594 48943 -242 207 -612 48945 -212 247 -592 48939 -244 205 -614 48941 -246 213 -594 48963 -214 215 -602 48967 -210 209 -622 48951 -212 209 -624 48937 -244 207 -584 48989 -212 209 -618 48935 -244 207 -616 48941 -248 211 -592 48963 -214 215 -604 48965 -214 215 -606 48935 -244 209 -618 48957 -212 211 -618 48955 -212 211 -622 48933 -244 207 -616 48929 -244 209 -616 48957 -212 209 -622 48953 -212 211 -622 48945 -242 205 -610 48933 -236 199 -634 48949 -210 211 -622 48943 -242 205 -612 48951 -212 209 -622 48945 -242 205 -612 48953 -212 209 -622 48933 -248 215 -598 48943 -242 207 -614 48925 -244 209 -618 48947 -212 247 -590 48955 -210 213 -626 48959 -210 209 -618 48961 -212 217 -604 48953 -212 247 -592 48937 -244 207 -614 48945 -242 205 -578 48967 -242 207 -614 48933 -244 207 -584 48977 -208 237 -576 48989 -214 217 -602 48953 -248 211 -592 48945 -212 249 -592 48963 -212 209 -616 48949 -212 247 -590 48951 -206 237 -578 48963 -244 207 -620 48953 -212 209 -620 48937 -244 207 -614 48941 -242 205 -610 48939 -242 205 -614 48955 -212 211 -622 48951 -212 209 -624 48939 -242 207 -612 48929 -244 207 -618 48929 -248 215 -600 48945 -242 205 -614 48945 -240 205 -612 48951 -198 227 -596 48967 -242 203 -612 48941 -212 249 -590 48949 -206 239 -612 48945 -242 203 -610 48947 -206 239 -610 48945 -210 249 -588 48941 -242 207 -614 48953 -212 211 -624 48947 -212 247 -590 48937 -246 215 -580 48959 -244 217 -578 48951 -242 217 -584 48983 -212 191 -616 48959 -238 217 -596 48939 -242 215 -620 48927 -244 207 -616 48941 -244 217 -576 48961 -242 205 -616 48923 -238 203 -606 48967 -246 215 -578 48947 -244 209 -618 48957 -212 209 -622 48943 -212 251 -574 48951 -244 215 -614 48949 -214 215 -604 48947 -246 213 -598 48941 -246 213 -598 48945 -248 211 -596 48959 -212 209 -622 48945 -210 251 -574 48977 -212 215 -606 48937 -244 209 -618 48955 -212 209 -622 48947 -234 197 -602 48949 -244 207 -618 48947 -242 203 -610 48939 -234 199 -602 48971 -234 195 -600 48953 -238 201 -636 48949 -206 203 -640 48939 -246 211 -592 48933 -242 209 -620 48945 -246 211 -592 48955 -212 209 -626 48957 -212 215 -606 48943 -246 213 -602 48959 -210 211 -624 48955 -206 237 -608 48931 -242 207 -616 48933 -246 213 -600 48929 -244 209 -620 48941 -246 215 -578 48965 -RAW_Data: -246 215 -576 48975 -210 209 -622 48933 -236 201 -604 48963 -242 207 -616 48931 -242 207 -618 48941 -240 205 -614 48923 -238 203 -638 48947 -204 239 -544 48995 -246 213 -532 49029 -214 215 -538 49025 -212 211 -560 49001 -248 213 -532 49009 -244 217 -544 48995 -238 217 -528 49027 -206 251 -524 49001 -246 215 -534 49027 -212 217 -538 49031 -210 211 -556 49007 -244 217 -544 48997 -238 219 -526 49001 -244 215 -552 48989 -246 215 -534 49035 -214 191 -582 48995 -238 219 -526 49005 -242 217 -548 49007 -212 251 -508 49011 -244 215 -552 48997 -246 185 -562 49013 -240 217 -526 49029 -208 217 -554 48989 -244 215 -552 49011 -210 221 -556 48997 -238 217 -528 49017 -246 215 -530 49035 -208 215 -552 49017 -212 215 -540 49013 -236 219 -560 48979 -238 217 -564 48993 -238 219 -592 48937 -244 215 -600 48937 -246 215 -602 48965 -210 209 -622 48953 -212 211 -622 48939 -242 205 -616 48933 -244 205 -616 48941 -244 217 -576 48951 -248 215 -600 48947 -246 215 -576 48973 -212 215 -608 48965 -206 217 -622 48937 -246 213 -598 48935 -248 215 -600 48959 -210 211 -622 48951 -242 203 -610 48941 -248 211 -592 48933 -246 215 -600 48951 -246 213 -596 48937 -244 215 -582 48953 -244 215 -584 48963 -248 213 -596 48947 -246 211 -594 48951 -246 211 -594 48953 -242 203 -610 48941 -242 203 -610 48951 -212 209 -622 48943 -242 205 -614 48953 -212 209 -620 48959 -206 203 -606 48983 -212 215 -606 48955 -212 251 -572 48975 -212 191 -614 48957 -242 185 -636 48943 -246 215 -612 48941 -210 215 -608 48951 -246 215 -578 48955 -244 217 -614 48921 -244 215 -618 48929 -246 215 -612 48925 -246 215 -596 48947 -246 215 -576 48951 -244 207 -620 48949 -210 211 -624 48949 -210 253 -572 48945 -242 215 -620 48955 -202 217 -628 48947 -202 219 -630 48925 -244 215 -614 48939 -248 215 -576 48971 -206 215 -624 48943 -246 213 -592 48931 -248 215 -602 48937 -244 215 -616 48929 -246 213 -598 253 -572 48965 -212 211 -628 48959 -212 217 -602 48945 -246 215 -598 48951 -210 247 -592 48959 -212 209 -624 48935 -248 213 -596 48937 -242 209 -616 48953 -212 217 -606 48945 -248 213 -600 48963 -214 217 -602 48965 -212 215 -606 48965 -214 215 -604 48957 -210 247 -592 48955 -212 211 -622 48943 -246 213 -596 48953 -212 215 -608 48959 -212 251 -572 48971 -210 211 -624 48931 -246 215 -602 48949 -246 213 -594 48939 -246 215 -598 48955 -206 253 -590 48955 -214 215 -604 48949 -244 205 -614 48945 -246 211 -592 48939 -246 213 -598 48941 -244 205 -616 48939 -246 213 -596 48937 -246 215 -612 48937 -240 217 -590 48937 -246 215 -600 48959 -210 211 -622 48955 -212 215 -606 48949 -246 215 -614 48929 -246 213 -RAW_Data: -596 48959 -210 211 -620 48957 -210 211 -622 48929 -246 215 -602 48935 -244 207 -622 48957 -214 215 -604 48953 -210 247 -592 48959 -212 215 -606 48967 -214 215 -606 48947 -242 205 -616 48947 -246 175 -628 48933 -246 193 -612 48973 -206 215 -622 48939 -246 215 -578 48961 -242 205 -614 48949 -212 211 -622 48957 -206 203 -640 48935 -242 205 -614 48955 -206 203 -638 48923 -246 213 -602 48951 -246 211 -598 48953 -210 211 -626 48961 -212 215 -604 48945 -246 213 -598 48937 -238 201 -604 48975 -206 239 -610 48945 -240 203 -610 48935 -242 205 -616 48951 -206 205 -640 48943 -210 211 -626 48935 -248 213 -598 48935 -244 215 -586 48979 -212 211 -622 48929 -244 209 -620 48947 -212 215 -608 48953 -246 215 -580 48957 -238 217 -596 48963 -206 251 -590 48947 -210 253 -572 48939 -242 215 -624 48947 -206 253 -588 48933 -246 215 -614 48925 -244 213 -602 48933 -242 207 -622 48941 -240 203 -616 48923 -232 223 -630 48915 -230 225 -630 48919 -236 199 -638 48941 -240 201 -614 48941 -246 211 -596 48925 -242 209 -626 48929 -242 209 -620 48929 -246 213 -604 48947 -246 211 -598 48925 -238 235 -610 48921 -244 207 -622 48937 -240 205 -620 48933 -242 205 -618 48931 -242 207 -618 48921 -244 215 -608 48953 -244 213 -596 48929 -244 209 -620 48939 -244 213 -600 48949 -246 211 -594 48941 -240 207 -618 48939 -240 207 -614 48927 -244 207 -620 48939 -242 205 -616 48941 -240 205 -614 48943 -242 203 -612 48931 -242 209 -618 48951 -212 209 -622 48937 -246 213 -598 48955 -240 203 -612 48917 -238 235 -608 48949 -210 211 -624 48951 -210 211 -624 48959 -212 209 -622 48935 -244 207 -616 48931 -244 207 -616 48949 -212 215 -606 48971 -212 215 -606 48963 -212 217 -606 48969 -210 211 -622 48943 -248 211 -592 48959 -212 215 -606 48963 -212 211 -622 48935 -244 207 -616 48925 -244 209 -620 48951 -234 195 -596 48963 -246 213 -594 48945 -246 213 -594 48949 -246 213 -594 48961 -212 215 -602 48939 -242 217 -618 48933 -242 207 -616 48953 -210 209 -620 48945 -246 211 -592 48955 -212 215 -608 48949 -242 207 -616 48945 -248 211 -592 48933 -246 215 -602 48939 -246 215 -600 48937 -246 215 -598 48949 -248 215 -576 48969 -212 211 -622 48961 -212 209 -622 48935 -246 213 -596 48939 -244 207 -616 48957 -206 203 -606 48985 -212 209 -620 48939 -244 205 -612 48955 -212 217 -602 48943 -246 215 -580 48955 -246 215 -600 48957 -212 215 -606 48973 -212 209 -620 48933 -244 217 -582 48953 -248 215 -600 48943 -246 215 -598 48951 -240 203 -612 48933 -236 199 -632 48937 -242 205 -614 48927 -244 217 -582 48947 -242 211 -622 48945 -246 217 -574 48947 -242 217 -616 48933 -246 215 -578 48965 -248 215 -RAW_Data: -574 48957 -246 215 -580 48963 -246 211 -592 48939 -246 215 -582 48973 -210 211 -626 48931 -246 213 -602 48941 -242 207 -620 48939 -246 213 -594 48957 -210 211 -624 48949 -240 205 -610 48927 -242 209 -618 48953 -212 215 -606 48947 -242 209 -616 48941 -244 203 -610 48951 -212 209 -622 48953 -212 215 -608 48961 -212 247 -590 48947 -212 221 -624 48931 -236 201 -602 48981 -202 253 -562 48967 -242 205 -614 48935 -238 211 -600 48957 -234 197 -598 48983 -212 211 -620 48937 -248 213 -594 48949 -248 213 -590 48939 -244 207 -616 48941 -242 205 -610 48939 -238 217 -596 48961 -198 227 -596 48981 -202 219 -596 48969 -210 253 -572 48955 -246 215 -580 48959 -248 213 -596 48945 -248 211 -596 48939 -244 207 -614 48957 -212 209 -620 48929 -248 215 -600 48957 -206 237 -610 48945 -206 237 -608 48949 -206 237 -576 48957 -246 207 -618 48959 -206 203 -604 48971 -212 247 -592 48955 -214 217 -604 48947 -236 217 -588 48969 -200 231 -596 48957 -238 217 -594 48963 -206 253 -592 48937 -238 217 -596 48961 -206 215 -600 48979 -212 247 -590 48953 -212 215 -606 48969 -206 215 -622 48933 -248 215 -598 48939 -246 215 -580 48977 -212 217 -606 48963 -212 215 -606 48955 -212 249 -594 48967 -208 217 -584 48967 -240 217 -594 48953 -208 239 -576 48975 -242 205 -576 48989 -206 217 -588 48979 -212 217 -606 48957 -238 219 -594 48941 -236 219 -596 48959 -202 253 -596 48939 -240 217 -596 48961 -214 215 -606 48959 -206 251 -592 48963 -202 217 -598 48981 -194 217 -626 48961 -216 217 -602 48961 -202 211 -600 48979 -208 215 -602 48985 -214 217 -602 48945 -240 217 -594 48945 -238 215 -600 48939 -238 201 -604 48963 -248 215 -596 48949 -212 249 -592 48951 -214 215 -608 48969 -204 217 -596 48987 -202 219 -594 48969 -212 249 -588 48933 -246 207 -616 48937 -250 213 -594 48963 -214 217 -602 48945 -244 217 -580 48981 -202 219 -596 48959 -242 207 -614 48935 -244 217 -578 48977 -212 191 -650 48933 -244 217 -576 48965 -212 247 -592 48951 -240 205 -610 48947 -210 211 -626 48941 -244 205 -616 48951 -212 209 -622 48939 -242 205 -616 48929 -244 207 -618 48923 -238 203 -638 48947 -210 211 -622 48931 -238 203 -636 48939 -242 203 -612 48945 -232 197 -600 48943 -238 235 -610 48931 -246 213 -598 48927 -244 217 -606 48951 -246 213 -596 48933 -244 215 -620 48925 -246 213 -602 48947 -246 211 -594 48935 -244 207 -620 48947 -248 175 -628 48957 -210 209 -622 48929 -230 225 -596 48971 -206 239 -610 48945 -212 211 -624 48933 -246 215 -600 48937 -246 215 -600 48955 -212 215 -610 48945 -246 215 -600 48933 -244 209 -620 48929 -236 203 -638 48951 -212 209 -620 48935 -246 215 -598 48959 -212 209 -RAW_Data: -622 48957 -206 205 -640 48931 -236 199 -634 48941 -242 203 -612 48953 -204 203 -638 48945 -212 209 -626 48959 -186 211 -634 48961 -228 181 -628 48953 -242 205 -612 48927 -244 209 -620 48941 -242 205 -612 48935 -244 205 -614 48925 -248 213 -604 48949 -246 217 -576 48947 -238 201 -636 48925 -244 209 -618 48935 -246 213 -598 48935 -242 217 -584 48961 -248 213 -598 48939 -244 207 -614 48955 -206 235 -576 48971 -246 213 -594 48959 -212 209 -622 48947 -210 253 -572 48975 -212 211 -620 48955 -210 209 -622 48939 -244 205 -616 48953 -212 209 -622 48947 -210 247 -590 48947 -248 213 -592 48945 -242 205 -614 48943 -246 211 -592 48951 -242 203 -612 48939 -248 211 -592 48949 -210 249 -590 48965 -212 209 -618 48951 -212 213 -622 48939 -246 215 -594 48937 -248 215 -600 48943 -242 207 -614 48951 -212 211 -624 48945 -244 203 -610 48937 -244 205 -610 48945 -242 205 -610 48947 -206 237 -608 48935 -244 205 -614 48951 -212 211 -622 48957 -212 211 -622 48951 -212 211 -622 48937 -248 213 -596 48961 -212 211 -622 48939 -248 213 -596 48935 -244 207 -616 48945 -212 247 -594 48939 -244 205 -612 48959 -210 211 -618 48937 -244 207 -582 48971 -246 213 -596 48947 -242 205 -612 48927 -244 209 -618 48937 -242 205 -614 48931 -244 207 -616 48941 -242 205 -612 48955 -212 211 -556 48997 -244 207 -550 49019 -212 211 -556 49001 -244 207 -548 48997 -244 209 -550 49013 -212 213 -558 49011 -242 205 -544 49021 -212 209 -556 49011 -242 205 -544 48999 -244 207 -548 49001 -242 207 -548 49025 -212 209 -552 49001 -248 215 -530 49005 -244 207 -548 49023 -212 211 -552 49009 -246 213 -528 49013 -248 211 -528 49019 -212 211 -558 49013 -212 251 -508 49013 -248 215 -600 48935 -246 207 -618 48955 -212 211 -622 48941 -242 205 -546 48995 -244 207 -550 49019 -212 211 -556 49007 -248 213 -596 48931 -244 209 -586 48961 -246 207 -550 49001 -248 215 -596 48949 -246 213 -594 48939 -248 193 -610 48939 -248 213 -600 48941 -244 209 -616 48939 -242 205 -616 48947 -206 237 -610 48935 -244 205 -546 48993 -246 209 -552 49019 -212 209 -622 48955 -212 209 -622 48953 -210 213 -624 48957 -212 211 -622 48937 -242 207 -612 48951 -212 211 -624 48945 -244 203 -610 48935 -242 207 -614 48939 -248 213 -594 48957 -212 211 -622 48959 -214 215 -604 48959 -212 215 -606 48943 -246 193 -612 48967 -204 253 -592 48941 -244 217 -578 48971 -212 211 -624 48931 -246 207 -618 48953 -214 215 -606 48969 -212 209 -620 48933 -242 209 -616 48927 -246 209 -618 48939 -244 205 -610 48957 -212 209 -620 48941 -242 205 -580 48975 -246 213 -592 48939 -242 207 -584 48969 -244 207 -612 48957 -212 209 -618 48941 -244 205 -RAW_Data: -614 48951 -210 211 -624 48937 -242 207 -612 48953 -212 211 -622 48941 -242 207 -614 48929 -246 207 -616 48953 -212 211 -622 48957 -212 209 -618 48953 -212 211 -622 48933 -248 191 -612 48957 -240 217 -596 48947 -244 217 -576 48955 -248 213 -598 48967 -214 215 -604 48953 -212 247 -588 48935 -244 217 -614 48929 -246 215 -580 48971 -210 221 -624 48953 -204 217 -594 48977 -202 253 -594 48935 -246 193 -610 48967 -210 215 -608 48963 -210 213 -624 48961 -210 211 -620 48959 -212 209 -620 48947 -212 211 -628 48951 -212 211 -624 48935 -248 213 -598 48965 -214 193 -612 48975 -206 215 -626 48941 -246 213 -594 48939 -242 207 -616 48939 -246 215 -578 48955 -246 215 -596 48965 -212 211 -620 48935 -246 215 -596 48939 -244 207 -616 48929 -244 209 -616 48957 -212 209 -622 48935 -246 215 -598 48941 -248 213 -594 48955 -242 203 -610 48927 -244 207 -616 48957 -212 209 -622 48929 -246 217 -602 48959 -212 211 -622 48959 -212 211 -618 48951 -212 211 -622 48941 -242 207 -614 48953 -212 209 -624 48957 -212 209 -620 48931 -246 193 -610 48979 -214 215 -602 48961 -212 211 -556 49005 -246 215 -528 49021 -212 211 -556 49001 -248 215 -534 49011 -248 213 -528 49015 -248 213 -526 49021 -212 211 -558 49011 -212 249 -522 49007 -244 207 -548 49007 -212 249 -524 49021 -212 211 -558 49021 -212 211 -558 48999 -248 215 -530 49025 -214 215 -538 49013 -248 213 -534 49003 -244 207 -550 49015 -212 211 -558 49027 -214 193 -580 49011 -202 217 -564 48989 -236 217 -556 49011 -194 219 -558 49029 -208 217 -556 48999 -238 217 -530 49035 -208 215 -556 48999 -246 215 -546 49009 -208 215 -556 49015 -238 217 -524 49007 -236 217 -562 48983 -236 219 -562 49007 -206 217 -554 49021 -212 191 -582 49009 -214 215 -540 49007 -248 215 -534 49017 -246 217 -542 48987 -248 213 -532 49019 -212 215 -542 49017 -246 215 -548 48999 -246 217 -508 49013 -246 193 -546 49043 -208 217 -554 48993 -244 217 -548 48995 -246 215 -546 49015 -214 191 -582 48989 -238 217 -530 49013 -244 215 -548 49011 -212 191 -582 49011 -206 251 -524 49019 -210 217 -574 48987 -246 215 -544 48985 -248 191 -578 48979 -244 215 -552 49011 -212 191 -586 49009 -208 251 -522 49017 -210 215 -578 48981 -244 185 -566 49029 -208 215 -554 49011 -212 211 -560 48997 -246 193 -578 49001 -212 215 -576 48997 -212 211 -558 49027 -204 217 -562 48993 -246 215 -546 49015 -212 217 -538 49027 -212 217 -540 49019 -244 217 -542 48983 -248 215 -534 49013 -244 217 -546 48999 -240 217 -524 49005 -244 217 -548 49003 -240 217 -526 49015 -242 217 -522 49031 -208 215 -556 48989 -236 217 -564 48993 -238 217 -528 49005 -242 217 -552 49021 -208 215 -RAW_Data: -554 49021 -208 215 -554 49007 -240 217 -526 49005 -244 217 -546 49011 -212 215 -542 49015 -244 217 -578 48967 -242 205 -612 48935 -236 197 -634 48933 -244 205 -616 48921 -238 203 -638 48925 -244 207 -618 48947 -242 203 -612 48937 -234 197 -634 48947 -204 205 -640 48937 -242 203 -614 48933 -234 201 -636 48923 -238 201 -636 48919 -238 203 -640 48943 -242 201 -610 48945 -240 203 -544 48987 -246 207 -554 49003 -244 205 -616 48927 -238 203 -606 48953 -236 203 -636 48925 -238 201 -638 48943 -206 237 -542 49017 -212 209 -558 49017 -212 211 -560 48995 -244 209 -618 48925 -238 201 -640 48927 -246 213 -600 48933 -242 209 -620 48955 -210 209 -624 48949 -240 205 -610 48931 -246 213 -598 48949 -246 213 -594 48947 -246 211 -594 48959 -212 215 -608 48959 -210 211 -626 48945 -242 205 -614 48949 -206 237 -610 48949 -210 209 -622 48931 -244 207 -622 48947 -242 203 -612 48937 -242 203 -614 48933 -242 207 -616 48945 -246 175 -626 48955 -246 175 -626 48941 -246 211 -596 48939 -244 207 -618 48935 -242 207 -616 48953 -210 211 -620 48929 -244 207 -620 48955 -212 217 -604 48953 -248 211 -594 48937 -244 207 -620 48923 -244 209 -618 48929 -244 209 -620 48925 -244 209 -620 48953 -210 211 -624 48951 -210 211 -626 48937 -246 213 -598 48963 -208 215 -620 48923 -242 215 -606 48933 -272 179 -632 48943 -236 219 -594 48933 -242 217 -584 48957 -248 213 -602 48949 -248 211 -594 48961 -214 191 -614 48959 -246 215 -578 48951 -246 193 -612 48975 -208 215 -622 48927 -244 215 -618 48947 -210 211 -620 48933 -246 215 -602 48935 -244 207 -620 48949 -246 177 -626 48951 -212 211 -626 48933 -244 217 -616 48917 -246 215 -604 48961 -210 211 -624 48951 -212 209 -626 48925 -246 215 -602 48963 -246 175 -626 48925 -244 217 -604 48961 -246 175 -624 48927 -244 215 -606 48939 -248 191 -612 48979 -202 217 -598 48961 -246 215 -578 48969 -212 211 -628 48927 -246 215 -604 48953 -248 175 -626 48931 -244 209 -620 48933 -246 213 -600 48967 -214 215 -602 48935 -246 207 -620 48955 -212 209 -622 48937 -246 215 -580 48963 -242 205 -582 48979 -212 215 -608 48949 -246 215 -598 48943 -248 215 -594 48969 -216 215 -602 48941 -244 207 -578 48983 -214 213 -588 48987 -198 227 -594 48969 -206 253 -592 48939 -240 217 -596 48957 -212 249 -590 48959 -214 217 -602 48949 -238 217 -588 48959 -238 217 -590 48945 -244 217 -580 48961 -240 217 -596 48955 -214 247 -590 48959 -204 217 -632 48923 -238 217 -588 48957 -236 217 -588 48963 -226 221 -600 48957 -234 197 -600 48975 -210 209 -626 48943 -246 213 -594 48957 -206 239 -608 48925 -236 203 -604 48977 -210 211 -624 48929 -248 213 -602 48943 -244 215 -RAW_Data: -582 48963 -248 213 -594 48963 -210 211 diff --git a/assets/resources/subghz/Jamming/Jam_438.90.sub b/assets/resources/subghz/Jamming/Jam_438.90.sub deleted file mode 100644 index e9d04dbc5..000000000 --- a/assets/resources/subghz/Jamming/Jam_438.90.sub +++ /dev/null @@ -1,16 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 438900000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: 49165 -230 189 -400 1566 -3756 12839 -308 143 -612 48965 -242 207 -552 49019 -214 213 -538 49015 -246 215 -548 49009 -212 215 -540 49015 -246 213 -530 49003 -246 193 -580 48997 -240 219 -522 49023 -206 215 -556 49007 -244 217 -544 49009 -212 211 -556 48995 -244 209 -554 49005 -246 213 -528 49025 -206 205 -574 49011 -212 211 -560 49021 -212 209 -556 48999 -220 211 -568 49021 -232 193 -566 48983 -226 211 -564 49023 -210 211 -558 49019 -214 215 -540 49011 -248 213 -532 49005 -246 213 -534 49009 -218 213 -568 48999 -228 221 -538 49027 -240 203 -544 48991 -242 253 -520 49011 -216 211 -572 48981 -236 219 -562 48995 -242 181 -558 48995 -244 215 -552 49007 -224 211 -566 48979 -232 225 -564 48995 -244 215 -554 48999 -212 215 -540 49029 -202 217 -562 48995 -238 217 -562 49009 -224 181 -564 49021 -234 195 -568 48997 -218 213 -576 48995 -224 211 -566 48985 -244 207 -552 48991 -248 213 -536 49029 -212 215 -540 49023 -248 179 -578 48993 -246 213 -524 49019 -240 217 -526 49023 -218 211 -540 49029 -208 215 -558 49013 -246 177 -560 49021 -212 215 -540 49011 -246 215 -548 49007 -246 175 -560 49005 -224 211 -566 49013 -234 195 -564 49001 -248 175 -562 48997 -248 215 -534 49007 -246 215 -550 49007 -222 181 -600 48987 -234 229 -538 48989 -238 217 -564 48975 -246 215 -538 49027 -212 217 -538 49027 -212 211 -558 49005 -246 215 -546 48987 -246 215 -534 48999 -246 193 -580 48993 -224 211 -566 48989 -228 221 -538 49013 -228 221 -540 49009 -228 215 -562 48999 -236 199 -570 48989 -242 209 -554 48991 -246 209 -552 48997 -246 215 -548 48985 -244 217 -548 48991 -220 243 -566 48985 -234 199 -566 49007 -212 215 -540 49031 -248 213 -534 49003 -212 211 -562 48999 -220 211 -568 49003 -228 219 -540 49015 -242 207 -546 49005 -218 213 -544 49025 -222 211 -566 48995 -244 207 -550 49021 -214 217 -536 49017 -238 219 -528 48999 -220 213 -566 49013 -228 191 -594 48969 -252 181 -566 49027 -234 195 -566 48987 -246 215 -546 48991 -218 213 -568 48999 -230 223 -562 48981 -248 215 -534 49031 -194 211 -564 49019 -242 205 -544 48999 -232 211 -564 49001 -240 205 -544 48993 -248 215 -536 49009 -240 217 -530 49017 -222 211 -566 48995 -248 215 -528 49007 -248 215 -544 49015 -192 211 -564 49031 -206 203 -572 49003 -212 251 -508 49029 -212 253 -506 49009 -242 217 -552 49019 -208 253 -556 48979 -216 211 -540 49025 -240 219 -522 49001 -226 211 -566 49023 -210 209 -556 49003 -244 215 -548 48987 -244 217 -548 49007 -212 211 -558 48999 -244 217 -548 48993 -238 217 -530 49019 -240 181 -564 49031 -198 227 -566 49009 -212 209 -554 49013 -246 217 -540 49007 -212 209 -556 49019 -234 -RAW_Data: 193 -566 48989 -242 207 -552 49011 -212 213 -558 49005 -246 193 -578 48993 -238 217 -524 49025 -202 253 -528 48999 -228 213 -562 49017 -206 203 -574 49005 -216 213 -578 48987 -240 217 -528 49005 -244 215 -550 48987 -246 215 -536 49033 -208 215 -554 49013 -206 251 -524 49015 -240 217 -524 49019 -238 217 -528 49017 -206 253 -522 49009 -248 213 -526 49027 -214 215 -538 49021 -210 253 -540 49007 -214 215 -540 49019 -224 211 -568 48989 -228 223 -528 49033 -216 211 -544 49029 -204 253 -528 49015 -206 253 -524 49005 -246 217 -510 49017 -238 217 -530 49039 -202 217 -556 49017 -202 253 -526 49023 -204 253 -526 49017 -208 215 -556 49021 -214 217 -538 49031 -212 217 -540 49021 -204 253 -526 49003 -244 217 -548 48987 -248 215 -532 49001 -248 215 -534 49031 -214 215 -538 49023 -216 213 -542 49025 -218 211 -544 49035 -194 211 -564 49021 -242 205 -546 49003 -246 217 -540 48985 -220 211 -568 49029 -196 225 -564 48997 -242 205 -548 49015 -206 217 -558 49027 -210 215 -552 49001 -240 217 -528 49007 -240 217 -530 49017 -238 219 -528 49005 -226 211 -564 49021 -214 215 -540 49009 -232 211 -564 48997 -248 215 -528 49009 -232 211 -532 49033 -212 247 -526 48999 -238 217 -530 49033 -196 223 -570 48995 -240 217 -528 49025 -228 187 -560 49025 -238 217 -528 49029 -198 225 -530 49025 -238 215 -564 48993 -206 237 -542 49003 -236 219 -528 49009 -238 253 -516 48995 -240 217 -530 49027 -204 253 -528 49011 -222 211 -566 48993 -228 221 -560 49019 -246 217 -542 48987 -214 215 -534 49015 -246 217 -544 48993 -248 213 -528 49015 -212 247 -526 49023 -214 217 -538 49017 -248 213 -528 49021 -216 211 -542 49027 -208 251 -524 49015 -218 211 -542 49017 -228 213 -560 48989 -226 211 -562 49031 -212 209 -552 48999 -244 241 -520 49005 -246 217 -540 49007 -214 217 -538 49031 -212 211 -554 49011 -216 213 -546 49013 -224 211 -564 49023 -198 225 -564 49005 -212 213 -558 48995 -220 213 -566 49003 -230 223 -528 49039 -208 251 -522 49027 -214 215 -534 49005 -246 215 -534 49023 -238 217 -528 49017 -246 177 -558 49001 -246 215 -534 49015 -246 211 -528 49025 -212 211 -556 49007 -244 217 -542 49011 -208 217 -554 48997 -244 217 -548 48991 -248 213 -532 49005 -238 217 -562 49001 -242 217 -530 48991 -238 217 -564 48997 -222 211 -536 49039 -206 203 -572 49015 -212 193 -582 48997 -238 217 -524 49031 -224 181 -564 49011 -244 209 -550 48995 -238 217 -564 48981 -238 217 -556 48985 -226 211 -564 49007 -242 205 -548 49003 -242 207 -550 49011 -208 251 -522 49027 -208 215 -556 48995 -220 211 -568 48999 -228 223 -538 49007 -228 223 -536 49015 -230 235 -528 49003 -244 207 -554 48995 -246 -RAW_Data: 215 -538 49001 -248 213 -534 49015 -246 215 -544 49005 -212 215 -542 49013 -246 215 -532 49011 -246 213 -530 49001 -244 209 -552 49015 -210 211 -558 49023 -214 215 -540 49003 -244 209 -552 49023 -210 209 -556 49021 -212 211 -554 49011 -246 213 -528 49027 -214 215 -538 49015 -246 215 -544 48981 -228 211 -562 49019 -212 215 -576 48991 -224 211 -564 49001 -242 205 -548 48987 -246 209 -554 49005 -224 211 -566 48987 -226 217 -560 48999 -244 207 -550 48993 -248 191 -578 49013 -188 213 -564 49029 -198 227 -566 48999 -218 211 -546 49013 -224 211 -564 49025 -206 203 -572 49011 -212 211 -558 49007 -242 205 -550 49019 -190 211 -566 49019 -238 215 -516 49039 -204 217 -564 49009 -238 217 -516 49019 -236 219 -528 49013 -224 211 -566 49013 -232 195 -566 48989 -246 215 -548 49011 -194 211 -564 49001 -232 225 -530 49043 -214 217 -536 49021 -222 211 -566 48993 -228 221 -562 49009 -210 211 -552 49021 -214 215 -540 49031 -212 217 -538 49037 -208 217 -554 48987 -238 217 -562 48987 -246 215 -548 48989 -244 215 -550 49015 -212 253 -544 48983 -212 211 -562 49011 -222 211 -566 49011 -206 205 -572 48991 -246 215 -548 48991 -220 211 -568 48997 -230 225 -564 49003 -212 217 -540 49015 -248 213 -530 49029 -214 191 -582 49005 -222 211 -534 49037 -198 227 -566 49011 -208 215 -554 49011 -212 215 -542 49023 -218 213 -546 49011 -244 215 -550 49005 -238 217 -528 49001 -246 215 -536 49011 -248 213 -528 49029 -214 215 -538 49029 -212 211 -556 49023 -214 215 -538 49017 -246 217 -544 48981 -244 217 -548 49005 -246 187 -566 48995 -242 205 -548 48999 -246 215 -546 48987 -244 217 -548 49019 -208 217 -554 48995 -246 215 -548 49007 -218 211 -542 49027 -238 219 -526 48999 -238 217 -562 48979 -244 215 -550 48997 -224 211 -564 49007 -228 189 -558 49021 -246 213 -530 49015 -212 251 -508 49025 -246 217 -542 48991 -248 213 -528 49021 -212 215 -540 49011 -220 213 -566 49029 -204 203 -572 49013 -214 215 -540 49015 -246 215 -546 49011 -214 217 -538 49021 -248 175 -562 48995 -246 217 -536 49007 -226 211 -564 49029 -248 179 -576 48977 -246 177 -560 49001 -238 217 -562 48999 -238 217 -528 49013 -238 217 -528 49031 -202 217 -564 48995 -236 215 -564 48985 -236 217 -562 48989 -246 217 -540 48997 -212 251 -540 48983 -242 207 -552 49013 -240 205 -546 49001 -234 199 -570 49009 -232 195 -566 49009 -204 203 -574 48995 -246 213 -534 48999 -244 217 -550 49001 -242 207 -552 49013 -210 211 -554 49011 -246 213 -528 49011 -242 205 -550 49005 -240 217 -528 49013 -246 213 -528 49007 -246 215 -548 48981 -236 219 -560 49003 -224 211 -566 48981 -244 207 -552 49025 -208 215 -554 49019 -202 -RAW_Data: 217 -564 48991 -238 217 -564 48977 -238 217 -564 49001 -238 217 -524 49029 -202 219 -564 49005 -226 179 -566 49015 -236 201 -570 48999 -238 217 -528 49003 -236 217 -556 49007 -238 217 -528 49009 -246 215 -544 48989 -274 217 -524 48983 -246 215 -534 49001 -246 207 -554 49005 -246 213 -528 49003 -246 215 -534 49025 -212 209 -556 48999 -244 207 -552 49001 -248 213 -528 49013 -246 215 -546 48987 -244 217 -548 48997 -240 217 -528 49031 -208 217 -554 49011 -210 215 -576 49005 -202 219 -562 49019 -202 253 -562 48977 -224 181 -564 49021 -246 211 -526 49013 -240 217 -528 49021 -210 215 -578 48995 -248 175 -562 49007 -246 213 -530 49009 -236 199 -570 49009 -210 211 -554 49007 -242 207 -552 49007 -248 175 -562 49007 -248 211 -530 49017 -242 205 -546 49019 -212 209 -554 49009 -234 199 -568 49013 -204 203 -574 48999 -236 199 -570 48983 -244 207 -554 49001 -244 207 -550 48991 -244 209 -554 49007 -236 197 -568 49005 -234 195 -566 49003 -240 205 -544 48993 -238 201 -572 49007 -242 203 -544 49013 -212 211 -556 48997 -244 209 -552 49015 -240 203 -544 48991 -244 243 -520 49005 -244 203 -548 48999 -242 205 -550 49019 -212 209 -556 49017 -210 211 -560 49007 -244 185 -562 49019 -238 215 -530 49019 -232 211 -566 48973 -246 193 -546 49015 -226 211 -564 48995 -246 193 -578 49007 -208 215 -554 49023 -204 217 -562 48999 -240 217 -528 49007 -246 215 -548 48985 -244 215 -550 49019 -208 215 -554 49007 -242 217 -526 49009 -240 217 -530 49011 -246 215 -546 49001 -240 217 -524 49013 -240 217 -528 49019 -240 217 -526 49027 -208 215 -554 49005 -246 215 -544 49011 -208 215 -556 48995 -226 211 -564 49007 -242 205 -548 49005 -240 217 -528 49033 -208 217 -552 49005 -240 217 -526 49007 -244 217 -546 49015 -208 217 -554 49005 -226 211 -568 48999 -212 213 -560 49019 -212 191 -584 49003 -236 219 -524 49033 -204 217 -554 49021 -202 253 -526 49011 -240 217 -526 49017 -240 219 -524 49027 -204 217 -562 48983 -226 211 -564 49033 -204 203 -572 49001 -240 217 -528 49029 -206 215 -570 48997 -238 215 -528 49003 -230 225 -562 48991 -246 215 -546 48991 -246 215 -552 48985 -244 217 -546 48993 -244 185 -564 49027 -208 215 -554 49017 -212 211 -556 48999 -244 207 -552 49011 -242 203 -546 49005 -248 211 -526 49027 -214 217 -536 49023 -240 217 -526 49011 -248 211 -530 49015 -240 217 -526 49017 -240 219 -522 49019 -224 181 -598 48997 -234 195 -566 48985 -246 191 -580 48983 -236 217 -564 48985 -244 217 -546 49003 -246 217 -540 48993 -248 215 -542 48983 -246 193 -578 48981 -244 217 -550 48989 -248 215 -532 49003 -248 213 -534 49029 -212 209 -556 49015 -248 175 -560 49017 -212 -RAW_Data: 215 -542 49009 -248 213 -536 49017 -248 215 -542 48995 -248 215 -542 49003 -222 211 -534 49035 -196 225 -536 49043 -206 203 -572 49019 -202 217 -564 49001 -212 251 -542 48999 -212 217 -542 49029 -212 211 -558 49017 -240 203 -546 49009 -248 213 -524 49011 -248 215 -508 49039 -212 217 -540 49007 -248 213 -534 49007 -244 215 -556 49003 -212 217 -540 49007 -248 215 -534 49021 -212 211 -560 49019 -214 191 -584 48989 -242 217 -552 48999 -246 217 -544 48989 -246 213 -532 49025 -208 215 -558 49021 -208 217 -554 49003 -246 217 -542 48999 -212 251 -540 48983 -248 213 -534 49011 -248 213 -528 49021 -212 215 -542 49035 -214 217 -538 49033 -214 215 -536 49033 -208 215 -556 48993 -244 215 -552 48993 -246 215 -550 49005 -208 215 -558 49001 -244 217 -548 49001 -246 217 -550 48995 -212 217 -538 49011 -246 215 -550 49009 -214 215 -540 49011 -248 213 -530 49007 -244 187 -564 49025 -208 217 -556 48995 -246 215 -550 49003 -210 215 -544 49035 -202 255 -526 48995 -244 217 -548 48991 -238 217 -530 49035 -212 215 -542 49021 -248 215 -508 49031 -212 215 -544 49011 -242 217 -550 49013 -208 215 -558 49023 -202 217 -562 49015 -202 217 -564 49003 -236 219 -528 49029 -240 181 -554 49017 -226 211 -562 48985 -242 209 -552 49013 -214 215 -542 49011 -226 211 -566 49001 -242 207 -548 49003 -246 215 -548 48991 -238 217 -530 49015 -226 213 -564 48997 -246 213 -528 48999 -248 213 -536 49011 -240 217 -530 49021 -224 211 -566 49007 -212 251 -544 48997 -204 217 -562 49009 -206 215 -570 49003 -228 195 -572 48993 -246 213 -532 49031 -208 215 -542 49007 -236 211 -566 48985 -244 207 -550 48995 -242 185 -572 49007 -238 211 -534 49007 -240 203 -572 48991 -246 193 -546 49039 -210 211 -556 49005 -244 205 -548 49011 -234 195 -566 48987 -238 201 -570 48987 -244 207 -552 48995 -244 209 -552 49015 -212 215 -576 217 -564 48989 -238 217 -530 49015 -240 217 -528 49011 -240 179 -568 49021 -234 197 -566 48981 -242 217 -552 49021 -204 217 -562 48989 -238 217 -564 48997 -242 217 -524 48995 -246 215 -536 49007 -246 215 -548 48983 -244 215 -554 49007 -248 177 -560 48995 -244 209 -552 49021 -214 215 -540 49017 -248 211 -528 49005 -246 215 -548 49005 -212 217 -542 49035 -206 215 -558 49017 -214 215 -540 49021 -240 217 -526 49013 -238 219 -528 49001 -242 217 -550 49003 -238 217 -528 49027 -200 255 -524 48999 -238 217 -528 49013 -244 217 -550 49001 -244 217 -544 49009 -214 215 -540 49005 -246 215 -534 49015 -246 211 -530 49015 -248 179 -578 48999 -246 181 -576 48983 -248 213 -532 49003 -244 215 -550 49009 -214 215 -540 49035 -214 191 -582 48991 -230 221 -560 48989 -244 207 -550 -RAW_Data: 49007 -212 249 -522 49025 -214 215 -540 49021 -212 253 -506 49047 -208 217 -552 49007 -212 217 -542 49029 -206 253 -524 49027 -210 215 -554 49007 -206 251 -528 48999 -238 217 -530 49041 -214 217 -538 49029 -214 217 -536 49013 -240 217 -530 49015 -210 253 -510 49041 -210 215 -554 49005 -224 211 -532 49043 -206 237 -542 48991 -248 217 -532 49019 -208 251 -526 49003 -246 215 -546 48997 -246 217 -510 49031 -212 249 -524 49025 -212 211 -554 49003 -244 207 -546 49013 -212 251 -508 49017 -238 219 -528 49015 -250 213 -530 49003 -248 213 -532 49033 -212 209 -552 49007 -212 249 -534 49019 -212 209 -554 49003 -246 217 -544 49017 -216 217 -534 49013 -212 249 -528 49005 -242 207 -548 48999 -244 217 -548 49011 -216 211 -540 49027 -204 211 -564 49027 -214 217 -536 49019 -240 217 -528 49023 -208 217 -558 48997 -238 217 -530 49011 -238 217 -530 49039 -208 215 -556 49003 -238 217 -530 49017 -246 217 -510 49031 -212 249 -524 48999 -248 215 -534 49017 -212 251 -508 49033 -196 211 -562 49013 -236 219 -554 49003 -228 187 -560 49027 -202 253 -526 49029 -198 225 -562 48999 -238 219 -516 49023 -236 197 -566 49003 -228 211 -530 49019 -232 211 -562 49021 -204 217 -530 49023 -230 223 -528 49029 -212 253 -506 49015 -236 219 -530 49021 -246 217 -544 49001 -208 251 -524 49015 -238 219 -528 49021 -202 255 -518 49009 -234 199 -566 48991 -236 217 -564 49013 -202 219 -562 49005 -202 255 -526 49023 -214 193 -578 49001 -228 189 -558 49035 -202 211 -566 49017 -208 215 -568 48985 -246 207 -552 49005 -240 181 -566 49003 -248 193 -546 49025 -226 191 -556 49035 -202 253 -528 49019 -200 225 -562 48987 -244 209 -552 49003 -242 207 -546 49007 -246 185 -560 49023 -194 217 -558 49031 -202 217 -554 49025 -206 203 -572 48991 -228 215 -560 49029 -238 215 -550 48987 -206 237 -540 48999 -228 219 -538 49025 -240 219 -526 49023 -194 223 -538 49031 -206 251 -528 49001 -228 221 -540 49037 -208 215 -544 49023 -232 197 -564 49007 -238 217 -518 49005 -238 201 -574 49001 -226 213 -562 48993 -232 211 -568 48987 -240 181 -564 49011 -244 217 -548 49013 -202 217 -554 49009 -238 217 -528 49017 -240 217 -528 49025 -212 211 -558 48997 -244 209 -550 49043 -210 215 -554 48989 -212 251 -508 49025 -212 251 -508 49039 -208 215 -558 49021 -214 217 -536 49029 -212 211 -556 49009 -212 249 -526 49009 -248 215 -526 49003 -246 215 -548 49013 -216 215 -538 49017 -248 185 -558 49025 -202 217 -564 48991 -244 217 -548 48993 -240 181 -564 49007 -236 201 -572 49011 -206 253 -524 49017 -206 215 -570 49009 -194 219 -560 49029 -208 215 -556 48993 -226 211 -564 48999 -248 215 -534 49025 -208 251 -522 -RAW_Data: 49019 -212 211 -554 49015 -212 247 -524 49007 -246 217 -544 49001 -242 217 -526 49031 -210 253 -522 48999 -212 217 -540 49015 -248 193 -544 49043 -202 219 -562 48983 -238 217 -564 48989 -238 217 -524 49015 -236 217 -556 48993 -230 211 -564 49015 -208 215 -558 48999 -228 213 -562 49017 -202 219 -562 49005 -226 195 -570 48997 -238 217 -530 49023 -226 195 -568 49025 -202 217 -562 48997 -230 231 -534 48997 -238 217 -530 49037 -194 223 -568 49013 -202 255 -524 48993 -230 225 -528 49043 -204 217 -564 49015 -198 241 -562 48983 -246 187 -560 49005 -228 211 -562 48993 -244 207 -550 49021 -214 217 -536 49017 -212 253 -506 49023 -238 219 -530 49009 -248 215 -532 49009 -246 217 -510 49049 -208 217 -540 49031 -204 217 -564 48985 -238 211 -568 48991 -236 197 -568 49017 -208 217 -554 48995 -236 219 -560 48991 -236 255 -528 48989 -238 219 -528 49009 -238 217 -528 49031 -210 215 -554 49023 -214 215 -536 49003 -246 215 -534 49007 -244 217 -548 49017 -210 215 -554 49011 -208 251 -524 49021 -210 215 -554 49007 -240 181 -566 49021 -196 225 -570 48987 -244 209 -550 48997 -244 217 -548 49011 -214 215 -540 49021 -248 217 -506 49045 -204 219 -562 49001 -212 253 -506 49013 -248 215 -532 49029 -214 217 -536 49009 -248 215 -530 49015 -240 217 -528 49021 -226 193 -568 48991 -238 217 -530 49019 -230 233 -528 49011 -236 219 -528 49031 -200 227 -564 48989 -226 213 -562 49005 -238 217 -530 49005 -238 217 -564 48999 -212 217 -540 49039 -214 217 -536 49031 -216 215 -538 49005 -246 215 -548 49007 -212 253 -506 49037 -214 217 -536 49035 -214 209 -550 48999 -244 217 -548 49001 -214 251 -508 49027 -206 251 -528 49011 -214 249 -524 49015 -206 253 -526 49027 -208 253 -526 49007 -204 253 -528 49021 -210 215 -554 49015 -208 217 -556 49021 -216 215 -538 49011 -248 213 -530 49011 -212 249 -528 49003 -248 215 -514 49033 -206 253 -526 49019 -206 253 -522 49013 -214 251 -508 49023 -212 253 -508 49033 -208 251 -524 49017 -212 217 -540 49021 -210 253 -510 49023 -248 215 -528 49023 -212 213 -556 48999 -244 207 -548 49015 -214 217 -540 49013 -244 217 -546 49019 -210 217 -552 48995 -244 217 -546 49017 -216 215 -534 49035 -216 217 -534 49015 -206 251 -528 49029 -208 215 -556 49021 -214 193 -578 48999 -212 253 -506 49017 -248 215 -548 48999 -212 251 -508 49041 -210 215 -556 48999 -248 215 -526 49019 -214 251 -506 49019 -248 215 -528 49005 -244 217 -548 49009 -216 215 -538 49031 -214 211 -554 49005 -248 215 -540 48987 -248 215 -534 49019 -212 221 -556 49001 -228 221 -528 49017 -238 217 -530 49023 -246 217 -508 49047 -216 215 -536 49033 -210 215 -552 49013 -210 251 -522 -RAW_Data: 49019 -214 249 -528 48987 -248 217 -530 49021 -216 215 -540 49019 -212 251 -508 49019 -246 215 -548 48989 -236 219 -554 49003 -228 195 -568 49029 -212 253 -506 49013 -208 251 -522 49019 -212 217 -540 49021 -212 251 -542 49003 -212 215 -542 49031 -214 215 -540 49035 -216 215 -538 49017 -212 251 -548 49001 -214 217 -536 49009 -246 215 -532 49005 -246 215 -534 49033 -210 215 -552 49015 -202 253 -528 48999 -246 215 -528 49003 -244 215 -550 48989 -248 215 -534 49001 -248 215 -534 49033 -214 215 -538 49027 -216 215 -538 49005 -248 215 -534 49021 -212 213 -558 48999 -248 215 -534 49017 -212 247 -526 49003 -244 217 -548 49005 -212 215 -540 49031 -212 215 -542 49023 -246 217 -508 49041 -208 215 -558 49009 -212 253 -506 49019 -246 215 -530 49005 -248 215 -532 49029 -210 215 -554 48991 -248 215 -534 49003 -246 215 -536 49003 -248 193 -578 48993 -248 215 -540 48991 -248 215 -546 48983 -246 215 -548 48987 -248 215 -534 49017 -246 217 -540 48991 -248 215 -544 48999 -208 251 -526 49011 -248 213 -526 49023 -206 215 -558 49017 -212 215 -542 49029 -212 253 -544 48999 -212 211 -552 48995 -248 215 -534 49023 -212 215 -540 49011 -244 209 -552 49017 -212 217 -540 49025 -212 213 -558 49031 -208 217 -550 48993 -248 193 -544 49013 -246 215 -534 49025 -212 215 -540 49039 -214 215 -538 49023 -214 215 -540 49017 -246 215 -548 48999 -246 217 -508 49013 -244 215 -552 49001 -238 217 -530 49029 -208 251 -526 48993 -244 207 -548 49017 -212 217 -540 49025 -206 253 -524 49007 -246 213 -530 49019 -212 217 -542 49041 -210 215 -552 49007 -212 221 -556 49005 -238 219 -530 49033 -214 215 -536 49003 -244 217 -548 49001 -248 213 -526 49023 -214 191 -584 49003 -212 251 -540 49001 -212 217 -540 49037 -212 217 -540 49007 -244 217 -548 49003 -212 253 -506 49035 -214 211 -556 48993 -244 217 -550 49005 -248 211 -526 49027 -212 209 -556 49015 -214 215 -540 49025 -212 251 -540 48993 -248 215 -542 48991 -246 213 -530 49003 -246 215 -548 49001 -212 249 -524 49021 -212 217 -542 49035 -214 209 -554 48997 -246 193 -546 49033 -206 251 -524 49031 -208 217 -550 48995 -246 215 -548 49007 -212 217 -540 49013 -240 253 -530 48989 -206 253 -522 49017 -206 253 -524 49015 -210 253 -506 49045 -216 215 -538 49015 -248 213 -526 49023 -214 215 -538 49031 -212 211 -556 48995 -248 215 -534 49031 -212 209 -552 49007 -246 217 -544 48993 -238 217 -530 49017 -246 217 -540 49003 -214 215 -540 49013 -246 215 -534 49017 -210 253 -540 48981 -248 215 -532 49011 -248 213 -528 49023 -212 217 -540 49015 -248 213 -532 49027 -212 209 -556 49003 -246 215 -532 49027 -214 217 -536 49005 -246 215 -548 -RAW_Data: 49001 -248 215 -542 48983 -248 215 -534 48999 -244 209 -552 49045 -210 215 -578 48949 -248 193 -544 49021 -248 213 -530 49017 -212 217 -542 49023 -246 217 -508 49033 -206 251 -526 49003 -242 207 -552 49003 -248 215 -512 49039 -206 215 -544 49035 -204 217 -564 49003 -240 217 -526 49021 -208 251 -524 49019 -208 251 -522 49007 -248 215 -544 49011 -208 217 -554 48993 -246 215 -534 49003 -248 251 -544 48973 -212 211 -560 49027 -212 209 -552 49009 -212 247 -526 49011 -246 215 -530 49011 -248 211 -528 49027 -216 215 -538 49013 -248 249 -538 48981 -212 211 -554 49011 -212 249 -526 49011 -212 253 -506 49037 -202 211 -564 49021 -206 237 -542 49019 -214 217 -536 49007 -248 215 -534 49009 -248 215 -544 48985 -238 217 -528 49023 -228 221 -528 49019 -228 213 -562 49001 -248 249 -536 48957 -238 201 -572 48989 -246 215 -534 49017 -212 251 -508 49015 -248 215 -532 49005 -244 253 -528 48977 -228 213 -562 48999 -212 221 -560 49017 -214 215 -542 49029 -208 251 -524 49019 -214 215 -540 49023 -212 251 -542 48979 -238 253 -528 48997 -214 251 -546 48977 -240 253 -526 48983 -248 215 -552 48993 -212 253 -546 48995 -214 253 -540 48989 -216 251 -542 48969 -246 243 -518 49009 -212 253 -546 48997 -216 253 -506 49003 -248 215 -538 49011 -212 253 -546 48985 -212 251 -548 48987 -212 249 -530 49009 -212 251 -548 48979 -246 217 -552 48983 -244 217 -556 49001 -214 251 -510 49025 -208 215 -566 49005 -214 221 -562 48993 -238 217 -564 48969 -236 255 -524 48989 -240 253 -528 48985 -248 215 -548 48987 -248 213 -534 48993 -248 251 -504 49019 -214 253 -542 48967 -246 243 -520 48991 -246 251 -538 48967 -248 215 -570 48985 -214 253 -544 48977 -212 251 -550 48987 -248 213 -534 49019 -214 243 -524 49017 -214 253 -540 48999 -210 251 -524 49013 -208 253 -522 49001 -212 247 -532 49009 -212 249 -530 49015 -212 245 -526 49017 -212 253 -542 48989 -212 247 -528 49019 -212 245 -522 49001 -246 217 -552 48977 -246 215 -538 49019 -210 247 -528 49007 -248 211 -534 49011 -246 215 -570 48949 -248 215 -540 49023 -216 251 -508 49023 -214 253 -540 48965 -246 253 -502 49011 -248 213 -532 49001 -246 215 -536 49017 -212 253 -542 48993 -212 253 -540 48973 -246 253 -528 48987 -242 205 -548 48993 -244 217 -552 49003 -214 251 -542 48991 -212 253 -542 48987 -248 213 -530 49017 -208 251 -524 48995 -246 215 -552 48993 -248 215 -550 49007 -204 255 -526 48991 -226 219 -570 48997 -212 253 -542 48991 -226 211 -562 48999 -202 255 -522 49007 -230 221 -560 49015 -204 211 -564 49001 -212 249 -530 49019 -196 243 -562 48983 -238 217 -554 49005 -194 261 -530 48999 -238 219 -528 49027 -230 233 -562 -RAW_Data: 48967 -214 251 -544 48983 -238 217 -554 49001 -200 229 -566 48999 -238 215 -546 48993 -244 241 -558 48979 -196 241 -530 49023 -202 241 -532 49031 -210 215 -570 48975 -248 223 -552 48981 -228 257 -530 48989 -248 213 -534 49023 -196 241 -562 48985 -230 211 -564 48985 -238 211 -564 48983 -244 215 -544 49017 -200 227 -564 49009 -202 255 -530 49009 -234 195 -564 49005 -240 217 -530 48997 -236 233 -538 48991 -238 253 -528 49003 -200 227 -564 49005 -228 219 -566 48989 -208 251 -526 49015 -194 261 -532 48999 -240 211 -536 49013 -212 249 -530 49011 -196 243 -560 49001 -238 217 -530 49001 -230 223 -562 48995 -240 217 -546 49005 -206 239 -544 48989 -236 253 -530 48989 -238 217 -564 48985 -232 211 -564 48991 -240 211 -534 49019 -212 223 -560 48997 -238 215 -568 48985 -232 195 -566 49013 -202 255 -528 49019 -202 255 -528 49013 -204 253 -528 48997 -246 217 -546 49005 -216 215 -542 49023 -248 215 -536 49003 -212 245 -522 48999 -248 215 -534 49005 -248 215 -532 49007 -248 213 -532 49021 -214 243 -520 48997 -244 217 -554 48983 -238 253 -524 48995 -248 215 -550 48989 -212 253 -548 48983 -246 217 -550 48991 -212 221 -564 48995 -246 215 -568 48979 -214 221 -562 49013 -210 251 -524 48987 -248 215 -572 48979 -212 247 -532 49019 -212 245 -522 49011 -212 253 -544 48977 -244 217 -554 49003 -208 251 -528 49001 -240 215 -568 48991 -198 225 -562 48987 -244 253 -520 49001 -212 251 -546 48987 -246 211 -530 49003 -246 211 -570 48981 -246 211 -532 49007 -248 213 -530 49003 -240 217 -562 48987 -212 247 -532 49025 -204 253 -528 48985 -238 253 -528 48973 -242 253 -522 48979 -246 251 -540 48987 -246 211 -530 49009 -246 217 -546 49003 -210 245 -524 48993 -244 217 -554 49007 -212 245 -526 49015 -210 247 -528 48999 -246 215 -552 48981 -244 215 -556 49017 -246 215 -552 48951 -244 209 -588 48963 -242 207 -588 48955 -246 215 -576 48977 -246 213 -566 48979 -232 197 -566 49007 -206 253 -526 48995 -238 253 -528 48989 -228 221 -560 49005 -204 253 -524 49019 -208 251 -526 48997 -248 215 -536 49005 -208 241 -548 49011 -214 247 -526 49001 -236 229 -534 49023 -212 243 -520 49001 -238 219 -562 48983 -228 231 -528 49021 -198 229 -564 48993 -244 217 -552 49003 -208 251 -526 49005 -212 253 -544 48993 -212 251 -546 48999 -214 251 -540 48995 -210 251 -522 48989 -246 215 -556 49007 -212 245 -522 49025 -208 253 -524 49011 -204 253 -528 48993 -228 221 -562 48993 -238 215 -546 49045 -208 251 -528 48965 -236 217 -566 48993 -196 241 -560 48983 -244 243 -518 49011 -212 253 -546 48995 -212 247 -522 48999 -248 215 -536 49007 -214 251 -548 48973 -248 215 -538 48997 -248 215 -540 -RAW_Data: 49009 -212 249 -532 49021 -212 243 -524 49013 -214 245 -528 49013 -214 251 -544 48987 -212 247 -530 48995 -248 215 -538 48997 -228 243 -528 49013 -212 251 diff --git a/assets/resources/subghz/Jamming/Jam_868.35.sub b/assets/resources/subghz/Jamming/Jam_868.35.sub deleted file mode 100644 index 9dee7722b..000000000 --- a/assets/resources/subghz/Jamming/Jam_868.35.sub +++ /dev/null @@ -1,16 +0,0 @@ -Filetype: Flipper SubGhz RAW File -Version: 1 -Frequency: 868350000 -Preset: FuriHalSubGhzPresetOok650Async -Protocol: RAW -RAW_Data: 10447 -306 135 -454 49115 -244 209 -394 49169 -236 217 -396 49147 -226 211 -402 49177 -208 251 -358 49185 -196 219 -398 49185 -212 213 -392 49181 -200 229 -368 49185 -238 235 -348 49205 -204 235 -346 49211 -198 225 -368 49187 -244 207 -352 49197 -230 233 -340 49199 -236 197 -372 49183 -244 217 -384 49153 -230 235 -370 49177 -242 205 -350 49195 -238 217 -396 49149 -244 241 -354 49157 -236 217 -398 49149 -244 207 -354 49219 -202 255 -360 49167 -234 197 -372 49209 -214 217 -372 49181 -212 247 -362 49185 -202 255 -362 49171 -234 197 -374 49191 -238 217 -362 49189 -206 239 -382 49183 -204 217 -396 49169 -242 203 -382 49181 -198 211 -400 49173 -240 211 -370 49191 -200 229 -370 49193 -234 197 -374 49211 -212 209 -390 49175 -208 251 -360 49187 -194 211 -402 49181 -230 189 -398 49193 -212 209 -394 49165 -244 205 -386 49167 -228 197 -408 49175 -238 217 -360 49165 -244 215 -386 49179 -206 205 -380 49197 -242 205 -382 49181 -198 227 -370 49193 -244 203 -382 49173 -228 195 -380 49201 -200 227 -370 49177 -232 235 -430 49117 -236 197 -434 49143 -212 211 -454 49103 -248 213 -430 49101 -248 213 -440 49111 -244 203 -416 49137 -246 213 -430 49105 -218 213 -438 49121 -228 211 -400 49191 -196 217 -400 49177 -198 229 -372 49193 -242 203 -352 49193 -244 205 -386 49181 -206 237 -380 49165 -242 205 -384 49161 -246 215 -368 49189 -212 211 -394 49185 -212 211 -394 49173 -242 203 -382 49159 -244 207 -388 49171 -242 205 -382 49169 -242 205 -382 49169 -236 197 -372 49191 -242 207 -384 49189 -214 193 -380 49209 -204 217 -398 49165 -212 251 -342 49205 -212 211 -390 49171 -212 251 -378 49173 -204 217 -398 49167 -234 199 -372 49209 -204 217 -396 49181 -212 209 -390 49185 -202 253 -360 49187 -212 211 -390 49183 -202 255 -360 49181 -212 211 -394 49185 -196 211 -402 49197 -204 209 -372 49213 -208 187 -404 49171 -244 207 -356 49215 -204 217 -396 49183 -212 209 -390 49183 -202 253 -360 49185 -212 211 -392 49183 -196 211 -400 49197 -204 211 -402 49185 -210 215 -374 49171 -244 207 -386 49181 -202 253 -360 49181 -212 213 -394 49179 -238 217 -360 49193 -206 205 -378 49195 -242 205 -384 49169 -242 205 -384 49181 -206 203 -380 49203 -206 237 -380 49155 -246 207 -388 49189 -206 203 -378 49211 -206 203 -378 49191 -236 197 -376 49191 -244 205 -384 49171 -242 203 -384 49157 -244 207 -388 49159 -222 211 -404 49169 -228 235 -342 49185 -232 225 -368 49201 -204 237 -348 49209 -212 211 -390 49163 -238 201 -378 49203 -208 237 -346 49191 -242 207 -356 49199 -234 199 -374 49197 -242 205 -350 49187 -244 209 -358 49191 -236 201 -374 49183 -244 209 -358 49193 -244 205 -388 -RAW_Data: 49173 -228 195 -380 49201 -232 197 -372 49197 -228 195 -378 49197 -234 195 -406 49167 -238 181 -398 49153 -272 169 -414 49143 -272 181 -396 49179 -242 169 -414 49163 -242 203 -386 49165 -238 217 -396 49141 -236 201 -410 49145 -244 215 -374 49163 -256 181 -402 49183 -244 179 -414 49165 -222 211 -372 49203 -204 211 -402 49167 -238 213 -394 49143 -236 219 -396 49175 -214 215 -374 49169 -232 235 -370 49173 -236 215 -394 49145 -228 215 -370 49181 -230 227 -370 49185 -248 185 -394 49171 -244 205 -354 49205 -214 251 -340 49197 -206 239 -350 49199 -200 231 -370 49203 -214 213 -362 49199 -226 211 -370 49193 -242 205 -354 49213 -202 217 -396 49183 -212 211 -360 49215 -202 253 -360 49183 -212 211 -362 49211 -196 211 -400 49201 -204 211 -404 49149 -244 185 -404 49171 -244 207 -386 49185 -202 217 -396 49183 -212 211 -390 49181 -204 253 -360 49183 -212 211 -394 49185 -208 217 -388 49181 -212 217 -376 49199 -212 211 -390 49167 -236 199 -374 49193 -242 207 -384 49165 -248 213 -366 49179 -238 219 -360 49193 -206 203 -378 49213 -202 219 -394 49183 -212 211 -390 49187 -202 217 -396 49155 -244 209 -388 49161 -236 217 -396 49155 -244 205 -354 49215 -196 211 -400 49179 -212 251 -342 49207 -208 217 -388 49157 -220 213 -400 49175 -230 191 -400 49195 -206 203 -380 49191 -244 217 -380 49177 -210 211 -392 49181 -232 195 -374 49193 -240 205 -382 49155 -236 219 -398 49157 -236 197 -376 49175 -234 219 -396 49153 -244 207 -390 49169 -244 215 -380 49155 -246 213 -368 49193 -210 209 -394 49177 -246 179 -412 49157 -246 211 -364 49163 -246 191 -420 49159 -244 217 -378 49159 -244 217 -378 49151 -220 213 -404 49183 -226 193 -380 49187 -234 197 -376 49199 -246 175 -396 49177 -242 203 -384 49167 -246 211 -364 49165 -246 213 -372 49173 -246 215 -380 49149 -246 215 -372 49175 -244 215 -382 49179 -222 179 -402 49185 -236 197 -376 49183 -246 213 -366 49191 -210 209 -394 49179 -246 175 -396 49185 -246 175 -396 49165 -242 207 -388 49171 -242 205 -384 49169 -246 177 -398 49175 -246 215 -378 49165 -240 217 -358 49175 -226 211 -404 49157 -242 207 -386 49181 -212 215 -376 49167 -270 179 -396 49169 -242 205 -386 49175 -246 175 -394 49169 -246 215 -368 49185 -212 211 -396 49181 -218 211 -408 49155 -226 211 -370 49185 -244 217 -380 49153 -238 217 -400 49157 -212 251 -376 49163 -238 217 -360 49185 -214 211 -396 49163 -226 211 -402 49169 -228 221 -366 49177 -236 217 -396 49149 -244 209 -354 49189 -236 219 -396 49173 -194 211 -402 49179 -240 217 -362 49197 -198 225 -368 49185 -240 217 -362 49179 -236 217 -396 49151 -242 207 -354 49217 -202 217 -396 49157 -244 209 -356 -RAW_Data: 49221 -198 211 -402 49161 -240 211 -370 49179 -244 217 -380 49181 -204 217 -396 49161 -236 217 -362 49191 -224 213 -370 49203 -206 235 -346 49215 -212 209 -358 49191 -244 209 -356 49207 -206 239 -380 49185 -212 211 -388 49181 -198 229 -370 49209 -212 209 -390 49163 -244 207 -386 49165 -242 207 -384 49159 -244 207 -388 49187 -204 203 -378 49185 -244 209 -390 49159 -244 209 -388 49175 -212 215 -378 49185 -248 213 -364 49171 -242 207 -386 49161 -244 207 -386 49167 -250 211 -364 49187 -212 211 -396 49173 -238 217 -362 49185 -206 239 -350 49217 -204 217 -396 49155 -244 207 -386 49177 -212 211 -394 49167 -244 207 -384 49171 -248 213 -362 49177 -248 211 -364 49167 -244 207 -356 49207 -206 239 -348 49193 -244 207 -354 49189 -244 209 -356 49209 -208 237 -346 49201 -242 205 -350 49203 -242 205 -350 49205 -212 213 -396 49191 -214 215 -370 49199 -188 211 -402 49187 -234 195 -372 49195 -232 197 -374 49211 -210 209 -390 49189 -212 209 -390 49177 -208 237 -380 49161 -244 205 -384 49167 -244 205 -384 49175 -206 239 -380 49157 -246 207 -388 49169 -242 205 -384 49159 -244 209 -386 49171 -242 205 -382 49167 -244 205 -382 49175 -242 203 -352 49187 -236 201 -376 49201 -212 245 -362 49167 -242 207 -388 49187 -204 203 -380 49189 -234 199 -376 49209 -212 211 -392 49173 -244 205 -350 49201 -208 239 -350 49185 -244 209 -390 49165 -234 201 -376 49207 -204 237 -346 49197 -212 251 -344 49199 -208 215 -370 49207 -240 217 -360 49177 -238 217 -362 49193 -202 217 -400 49185 -210 215 -386 49157 -246 207 -356 49219 -204 203 -380 49187 -244 217 -382 49165 -210 249 -362 49193 -212 211 -388 49185 -214 209 -392 49189 -212 207 -390 49165 -242 207 -386 49165 -244 205 -384 49167 -242 205 -386 49173 -242 205 -382 49175 -212 211 -394 49167 -244 205 -386 49157 -244 209 -388 49175 -246 213 -360 49173 -242 205 -384 49161 -244 207 -388 49169 -236 197 -374 49177 -246 207 -390 49167 -248 213 -366 49193 -204 203 -380 49197 -212 247 -362 49191 -212 209 -390 49165 -244 207 -386 49175 -242 203 -380 49173 -228 195 -380 49193 -238 215 -360 49203 -208 217 -386 49177 -210 211 -396 49183 -206 239 -348 49215 -206 203 -380 49197 -242 203 -352 49195 -228 197 -408 49175 -238 217 -360 49191 -202 211 -404 49171 -228 195 -380 49187 -236 199 -376 49201 -226 195 -410 49177 -204 217 -396 49175 -202 255 -362 49181 -206 217 -390 49175 -238 217 -396 49135 -228 215 -400 49165 -236 199 -376 49201 -212 215 -376 49191 -240 219 -358 49193 -208 217 -388 49179 -212 215 -376 49185 -242 205 -386 49167 -248 211 -364 49187 -212 211 -394 49173 -248 213 -362 49191 -198 227 -368 49199 -206 239 -348 -RAW_Data: 49209 -198 229 -370 49195 -236 195 -374 49207 -206 201 -380 49195 -234 197 -374 49201 -206 239 -348 49199 -242 205 -352 49195 -244 207 -354 49205 -212 211 -396 49163 -244 209 -356 49209 -212 211 -394 49169 -242 207 -356 49185 -246 209 -356 49209 -212 211 -394 49181 -212 211 -394 49163 -244 209 -358 49185 -244 209 -388 49189 -212 209 -390 49185 -210 211 -394 49169 -234 199 -374 49199 -242 203 -382 49171 -248 175 -398 49171 -246 213 -364 49163 -222 211 -402 49195 -198 227 -368 49183 -248 213 -368 49183 -206 253 -358 49175 -240 217 -396 49147 -238 181 -402 49171 -226 215 -398 49175 -206 237 -380 49153 -244 215 -386 49181 -214 191 -418 49169 -204 253 -358 49167 -244 215 -384 49179 -212 215 -376 49187 -246 211 -364 49161 -244 209 -390 49165 -244 205 -386 49175 -210 211 -396 49179 -212 247 -360 49163 -246 215 -372 49163 -246 209 -390 49187 -214 191 -418 49165 -238 217 -362 49177 -222 211 -372 49205 -206 237 -348 49189 -244 205 -388 49181 -206 237 -346 49193 -236 199 -374 49205 -212 215 -378 49181 -248 185 -396 49165 -226 215 -400 49161 -248 213 -366 49175 -238 217 -396 49151 -244 205 -382 49175 -206 253 -358 49189 -202 217 -396 49183 -206 235 -346 49213 -204 217 -396 49181 -212 211 -390 49185 -202 253 -360 49185 -212 209 -392 49181 -196 211 -402 49199 -202 211 -404 49183 -210 215 -374 49173 -244 207 -386 49179 -202 255 -358 49181 -214 211 -394 49181 -204 253 -360 49183 -212 211 -394 49167 -236 219 -396 49151 -234 199 -374 49213 -204 217 -396 49183 -212 209 -388 49189 -202 217 -396 49183 -212 209 -388 49183 -198 211 -400 49199 -202 211 -370 49215 -208 215 -376 49173 -244 207 -354 49211 -202 255 -360 49181 -212 213 -394 49189 -202 217 -398 49171 -196 219 -398 49179 -210 253 -344 49199 -206 215 -402 49171 -242 205 -382 49181 -208 217 -390 49163 -236 217 -396 49157 -238 215 -394 49161 -204 211 -402 49177 -234 197 -372 49209 -206 203 -378 49197 -210 253 -376 49173 -216 215 -374 49169 -244 209 -386 49165 -244 207 -384 49177 -212 211 -396 49181 -206 239 -348 49197 -242 205 -382 49171 -242 205 -382 49167 -248 213 -364 49195 -206 201 -378 49203 -206 239 -348 49217 -214 191 -382 49189 -238 219 -364 49199 -208 217 -388 49183 -214 215 -374 49171 -244 209 -386 49185 -206 237 -346 49209 -212 211 -392 49161 -248 193 -380 49193 -212 251 -342 49193 -206 251 -360 49171 -230 221 -368 49175 -236 217 -396 49151 -244 209 -356 49215 -202 255 -360 49181 -214 209 -390 49183 -202 253 -360 49183 -212 211 -394 49181 -196 211 -400 49205 -202 211 -404 49151 -246 185 -400 49171 -244 207 -384 49179 -204 253 -360 49181 -212 211 -396 49181 -202 253 -360 -RAW_Data: 49183 -212 213 -392 49181 -202 255 -360 49183 -212 211 -394 49179 -196 243 -368 49201 -204 211 -402 49185 -208 215 -378 49169 -246 207 -388 49179 -202 255 -358 49181 -214 211 -394 49181 -202 255 -358 49185 -212 211 -394 49189 -202 217 -396 49157 -244 209 -386 49179 -202 211 -402 49163 -246 207 -388 49189 -216 193 -380 49183 -228 221 -380 49181 -212 253 -342 49199 -214 215 -376 49185 -212 249 -362 49185 -214 217 -374 49177 -244 205 -386 49183 -212 211 -392 49185 -198 229 -370 49191 -242 205 -382 49169 -228 195 -410 49179 -202 217 -396 49171 -246 185 -396 49159 -238 217 -400 49149 -244 215 -382 49151 -244 209 -388 49165 -236 199 -408 49175 -200 227 -370 49199 -196 219 -398 49191 -206 203 -412 49161 -242 207 -384 49163 -242 207 -386 49167 -242 205 -384 49173 -240 203 -384 49181 -210 209 -394 49183 -246 173 -396 49173 -246 211 -364 49175 -244 213 -366 49163 -246 215 -372 49179 -246 213 -364 49171 -246 195 -382 49177 -238 217 -398 49147 -238 217 -398 49159 -212 215 -378 49195 -234 195 -372 49183 -230 233 -372 49175 -238 215 -358 49171 -236 217 -400 49175 -208 217 -388 49185 -214 213 -374 49187 -246 211 -362 49183 -240 203 -382 49163 -244 205 -386 49175 -240 203 -382 49173 -242 203 -382 49165 -242 205 -386 49177 -206 205 -412 49173 -240 203 -382 49167 -240 205 -384 49181 -232 181 -404 49155 -242 185 -438 49141 -242 207 -388 49177 -228 183 -400 49167 -236 217 -398 49155 -230 199 -406 49157 -228 211 -402 49163 -242 217 -382 49169 -238 213 -394 49143 -236 201 -410 49151 -242 207 -390 49161 -244 205 -388 49173 -242 203 -384 49179 -206 237 -378 49173 -234 197 -374 49179 -236 201 -378 49197 -242 203 -382 49171 -234 197 -374 49179 -236 203 -380 49187 -246 211 -368 49179 -228 183 -400 49199 -202 217 -400 49155 -236 203 -380 49175 -236 217 -396 209 -392 49181 -238 213 -360 49187 -246 175 -396 49181 -238 215 -360 49185 -246 175 -396 49181 -228 183 -400 49167 -236 211 -404 49155 -244 185 -432 49135 -238 201 -412 49171 -238 181 -398 49157 -238 201 -412 49145 -234 217 -396 49151 -244 209 -392 49179 -236 215 -394 49153 -246 175 -396 49179 -236 213 -394 49163 -210 211 -394 49177 -228 183 -400 49201 -202 211 -402 49153 -240 185 -438 49141 -244 207 -388 49177 -238 215 -394 49149 -246 175 -398 49177 -238 213 -394 49149 -246 213 -366 49191 -238 179 -396 49153 -244 209 -392 49181 -238 215 -360 49183 -246 175 -398 49177 -238 213 -394 49153 -244 211 -366 49177 -228 183 -400 49201 -238 181 -398 49155 -238 203 -380 49177 -272 181 -396 49151 -244 207 -392 49181 -238 179 -396 49187 -246 175 -394 49181 -238 179 -430 49153 -246 175 -396 49177 -226 -RAW_Data: 183 -402 49167 -236 217 -398 49155 -236 201 -410 49147 -236 219 -396 49149 -244 207 -390 49179 -238 215 -394 49153 -246 175 -394 49179 -228 183 -400 49167 -236 211 -402 49151 -242 185 -438 49141 -242 207 -388 49179 -236 215 -394 49151 -246 175 -398 49177 -238 213 -394 49153 -246 211 -364 49177 -238 213 -394 49163 -246 175 -394 49185 -226 181 -402 49165 -236 211 -402 49151 -244 185 -402 49175 -242 207 -388 49179 -226 181 -402 49165 -236 217 -398 49157 -236 199 -378 49175 -236 217 -396 49151 -244 207 -392 49179 -238 179 -396 49187 -246 175 -394 49181 -238 213 -360 49187 -246 175 -396 49181 -228 183 -400 49199 -202 211 -402 49151 -240 215 -410 49141 -242 207 -388 49177 -228 183 -400 49167 -236 219 -398 49155 -236 199 -408 49145 -272 181 -396 49151 -242 209 -392 49179 -238 213 -394 49155 -246 175 -396 49181 -228 183 -400 49177 -238 181 -434 49151 -244 185 -396 49175 -242 203 -384 49179 -228 181 -402 49171 -238 217 -396 49167 -232 193 -374 49183 -236 217 -396 49149 -234 201 -408 49151 -236 219 -398 49169 -228 191 -382 49177 -242 207 -388 49169 -246 215 -378 49153 -244 215 -384 49179 -206 215 -390 49173 -244 217 -378 49159 -244 211 -366 49175 -242 205 -386 49171 -240 205 -384 49153 -244 207 -390 49173 -242 205 -384 49165 -242 207 -384 49159 -244 207 -388 49171 -240 205 -384 49161 -236 201 -378 49179 -238 201 -412 49147 -238 203 -410 49157 -242 207 -388 49177 -210 211 -394 49161 -244 209 -390 49153 -238 203 -410 49155 -236 201 -406 49155 -236 201 -410 49157 -242 207 -384 49169 -242 205 -386 49161 -242 205 -388 49163 -242 205 -390 49169 -244 205 -382 49183 -204 205 -412 49159 -246 213 -368 49175 -246 213 -364 49175 -246 215 -380 49167 -210 211 -396 49187 -240 203 -350 49201 -234 197 -372 49193 -242 205 -384 49177 -206 237 -348 49195 -236 197 -376 49179 -244 209 -390 49175 -240 203 -382 49157 -236 201 -378 49195 -234 199 -374 49189 -236 197 -376 49193 -234 199 -374 49177 -246 207 -388 49177 -240 203 -382 49171 -248 211 -362 49185 -214 215 -376 49189 -212 211 -398 49171 -246 213 -366 49171 -242 207 -386 49169 -242 205 -386 49171 -248 175 -398 49191 -210 209 -392 49183 -212 215 -376 49175 -244 207 -388 49181 -212 215 -376 49179 -244 205 -386 49163 -248 211 -368 49163 -244 209 -390 49161 -244 207 -388 49163 -236 199 -376 49193 -230 195 -408 49161 -228 235 -370 49171 -236 215 -394 49153 -240 181 -400 49175 -236 217 -396 49147 -236 219 -396 49159 -238 217 -360 49185 -232 181 -402 49173 -228 221 -394 49179 -196 223 -380 49191 -232 197 -372 49197 -226 195 -380 49203 -238 181 -396 49173 -246 217 -376 49155 -246 213 -366 49171 -246 -RAW_Data: 213 -368 49179 -242 205 -384 49179 -232 195 -372 49175 -238 203 -378 49179 -238 203 -378 49201 -242 203 -350 49209 -206 205 -412 49179 -206 203 -412 49165 -248 211 -362 49165 -244 209 -388 49179 -212 209 -396 49181 -210 211 -396 49183 -206 237 -380 49153 -238 201 -410 49165 -242 203 -384 49157 -236 201 -380 49189 -236 199 -378 49189 -242 205 -386 49155 -236 203 -380 49197 -242 203 -382 49173 -240 203 -382 49151 -238 203 -410 49175 -206 237 -380 49177 -210 211 -394 49171 -242 207 -384 49183 -210 209 -392 49167 -242 207 -386 49181 -206 237 -378 49167 -246 213 -364 49169 -246 215 -382 49167 -248 175 -396 49177 -242 205 -382 49161 -236 199 -378 49181 -232 233 -370 49169 -228 221 -380 49181 -238 215 -360 49201 -208 215 -388 49161 -236 201 -378 49193 -230 197 -378 49187 -236 199 -378 49195 -228 189 -398 49167 -228 223 -380 49167 -232 225 -368 49201 -194 225 -380 49175 -230 237 -368 49177 -234 197 -374 49195 -230 195 -410 49181 -202 219 -396 49153 -242 187 -402 49189 -212 215 -378 49193 -232 197 -370 49193 -228 195 -410 49161 -228 213 -370 49187 -236 199 -376 49199 -228 193 -380 49177 -236 217 -396 49155 -244 185 -402 49189 -212 191 -418 49159 -246 193 -382 49183 -242 205 -386 49169 -228 197 -378 49195 -236 197 -374 49191 -230 189 -398 49169 -228 221 -380 49171 -236 217 -396 49175 -212 215 -376 49171 -238 201 -380 49185 -228 223 -370 49175 -244 207 -388 49161 -244 207 -386 49183 -198 229 -370 49199 -206 237 -350 49203 -242 203 -350 49197 -246 211 -366 49165 -246 215 -368 49177 -248 213 -366 49185 -210 217 -410 49143 -246 215 -368 49175 -242 207 -388 49161 -236 199 -408 49159 -242 205 -386 49169 -242 205 -384 49157 -246 215 -384 49179 -212 209 -392 49161 -238 201 -378 49183 -238 201 -378 49201 -232 197 -370 49207 -212 209 -390 49169 -234 201 -374 49191 -242 205 -384 49169 -228 197 -408 49181 -196 219 -398 49179 -242 203 -380 49183 -204 203 -380 49207 -212 209 -394 49175 -242 205 -382 49169 -230 195 -380 49183 -228 215 -400 49161 -244 207 -354 49199 -242 205 -384 49167 -242 205 -386 49167 -230 189 -398 49187 -206 237 -380 49185 -212 209 -392 49159 -238 201 -378 49205 -206 237 -348 49187 -238 199 -378 49201 -212 215 -376 49183 -246 193 -380 49193 -206 251 -358 49181 -212 221 -390 49175 -230 181 -402 49177 -230 223 -368 49197 -206 235 -378 49161 -244 205 -386 49171 -242 205 -382 49179 -212 217 -374 49181 -238 217 -398 49145 -244 217 -380 49167 -212 221 -394 49155 -236 219 -398 49177 -214 215 -370 49191 -206 237 -380 49185 -206 203 -378 49181 -238 203 -378 49197 -242 205 -382 49171 -228 195 -378 49203 -200 227 -372 49193 -228 -RAW_Data: 197 -378 49197 -234 197 -374 49189 -244 205 -384 49159 -236 201 -376 49203 -210 211 -396 49157 -244 209 -390 49185 -210 209 -392 49165 -242 207 -390 49163 -244 215 -368 49169 -246 213 -370 49171 -244 215 -384 49153 -244 207 -388 49161 -242 207 -390 49157 -242 209 -390 49167 -236 199 -406 49171 -210 211 -396 49171 -236 197 -406 49159 -242 205 -386 49183 -210 211 -392 49161 -244 207 -388 49173 -240 205 -384 49163 -242 205 -388 49175 -212 209 -396 49183 -240 203 -384 49155 -238 199 -408 49163 -242 205 -386 49167 -242 203 -384 49155 -238 203 -380 49209 -206 203 -410 49171 -240 205 -382 49165 -246 213 -364 49191 -210 209 -392 49157 -244 209 -390 49165 -242 207 -388 49183 -206 203 -380 49185 -236 201 -380 49181 -238 201 -380 49189 -236 199 -376 49197 -234 195 -372 49179 -236 203 -378 49203 -240 203 -352 49205 -240 203 -352 49205 -240 203 -350 49181 -238 201 -380 49195 -236 197 -374 49199 -232 197 -372 49205 -198 227 -370 49201 -232 195 -372 49193 -234 197 -374 49203 -204 237 -380 49169 -232 197 -374 49189 -234 199 -406 49175 -200 227 -370 49185 -230 233 -370 49177 -228 189 -398 49177 -236 197 -374 49191 -236 197 -374 49189 -236 199 -378 49179 -236 201 -410 49155 -238 199 -378 49197 -212 211 -398 49173 -246 211 -364 49169 -246 215 -368 49193 -212 215 -376 49193 -212 209 -394 49165 -236 201 -378 49193 -236 197 -374 49199 -206 239 -380 49155 -238 201 -378 49181 -238 203 -378 49201 -226 195 -378 49203 -202 219 -396 49171 -248 215 -376 49159 -234 199 -374 49177 -232 235 -368 49183 -238 215 -360 49173 -244 215 -384 49159 -236 199 -376 49191 -230 189 -398 49199 -196 223 -380 49199 -200 227 -370 49197 -228 195 -378 49175 -236 219 -396 49171 -212 215 -410 49143 -244 207 -388 49173 -242 205 -382 49171 -230 195 -378 49187 -236 199 -376 49193 -244 203 -384 49157 -244 209 -388 49165 -236 199 -376 49195 -228 195 -380 49207 -194 211 -400 49185 -228 181 -400 49183 -238 213 -394 49173 -208 215 -388 49157 -244 209 -390 49173 -230 195 -378 49203 -232 195 -372 49197 -228 193 -380 49209 -202 217 -396 49175 -212 215 -376 49197 -200 227 -372 49191 -228 191 -396 49197 -196 225 -380 49197 -200 225 -370 49199 -226 195 -378 49203 -204 217 -396 49171 -248 215 -378 49163 -236 195 -372 49179 -232 225 -368 49193 -240 205 -350 49193 -230 197 -406 49181 -202 217 -396 49167 -238 217 -398 49139 -246 215 -370 49197 -198 237 -370 49181 -238 215 -392 49141 -244 215 -384 49163 -242 205 -384 49167 -228 199 -406 49177 -228 189 -398 49163 -238 203 -378 49201 -206 237 -382 49179 -206 205 -410 49171 -242 203 -380 49155 -236 203 -408 49179 -206 203 -410 49169 -242 -RAW_Data: 205 -384 49167 -246 213 -364 49187 -212 211 -392 49173 -242 205 -382 49177 -232 195 -372 49183 -244 207 -386 49173 -228 197 -378 49199 -234 197 -372 49179 -246 207 -390 49165 -248 211 -364 49173 -242 207 -386 49173 -242 203 -382 49159 -244 207 -388 49179 -206 237 -350 49197 -246 213 -362 49183 -206 239 -350 49213 -206 203 -380 49185 -244 207 -388 49171 -242 205 -352 49205 -248 175 -398 49157 -238 203 -378 49195 -244 203 -382 49175 -242 203 -380 49181 -206 205 -410 49167 -242 205 -382 49171 -242 205 -384 49153 -238 201 -378 49195 -234 199 -374 49183 -236 201 -378 49187 -236 199 -376 49199 -242 203 -380 49167 -230 189 -398 49195 -194 225 -378 49205 -202 217 -396 49185 -206 217 -388 49163 -248 213 -370 49163 -238 203 -378 49181 -238 203 -378 49181 -246 193 -382 49181 -238 211 -370 49189 -228 213 -370 49195 -234 195 -372 49191 -242 205 -382 49171 -248 213 -362 49175 -246 213 -364 49193 -212 209 -390 49157 -248 213 -372 49179 -220 211 -412 49157 -232 181 -402 49189 -234 195 -374 49199 -212 209 -396 49181 -212 209 -396 49183 -206 251 -358 49167 -246 193 -380 49205 -212 215 -374 49175 -246 213 -368 49173 -246 193 -380 49191 -212 215 -410 49145 -244 215 -384 49181 -212 217 -374 49177 -246 213 -366 49187 -210 211 -396 49175 -244 217 -378 49163 -212 247 -362 49169 -242 207 -388 49167 -236 197 -376 49177 -236 203 -408 49175 -206 237 -380 49179 -212 211 -390 49187 -210 209 -392 49175 -242 207 -384 49161 -236 199 -376 49199 -206 239 -380 49159 -236 201 -376 49193 -242 205 -384 49187 -204 203 -380 49201 -240 203 -382 49153 -244 209 -390 49173 -242 205 -382 49155 -238 203 -378 49183 -238 201 -378 49187 -244 205 -384 49175 -242 203 -380 49155 -244 209 -390 49185 -212 209 -392 49159 -244 207 -388 49189 -212 215 -372 49201 -214 215 -370 49193 -208 215 -390 49169 -246 215 -382 49149 -244 209 -388 49159 -244 207 -388 49189 -212 209 -388 49187 -210 209 -392 49189 -204 205 -410 49157 -244 207 -386 49179 -212 211 -392 49163 -244 207 -388 49159 -244 209 -388 49169 -242 205 -384 49173 -246 211 -362 49193 -212 215 -374 49183 -246 211 -364 49167 -248 213 -370 49183 -210 247 -360 49187 -212 215 -376 49191 -242 203 -380 49163 -242 207 -386 49175 -212 211 -394 49173 -246 213 -366 49173 -218 213 -404 49175 -234 195 -372 49201 -210 211 -394 49167 -246 213 -368 49193 -208 215 -390 49159 -244 185 -402 49187 -202 253 -362 49173 -240 217 -362 49167 -244 209 -390 49177 -210 253 -340 49189 -248 211 -364 49163 -230 237 -368 49177 -244 205 -352 49203 -234 199 -372 49177 -244 209 -388 49187 -212 209 -390 49167 -248 213 -366 49193 -214 215 -374 49167 -244 -RAW_Data: 215 -386 49169 -212 251 -376 49151 -244 217 -380 49153 -242 217 -386 49171 -212 251 -374 49167 -240 203 -350 49201 -242 203 -382 49167 -242 205 -384 49165 -242 207 -384 49157 -246 207 -388 49183 -214 215 -376 49189 -210 213 -396 49185 -206 215 -392 49163 -244 217 -382 49159 -244 207 -386 49157 -238 201 -378 49199 -242 203 -352 49197 -242 205 -384 49167 -230 195 -380 49209 -202 217 -396 49173 -246 217 -376 49163 -234 197 -372 49197 -228 195 -380 49195 -234 197 -372 49193 -242 205 -352 49199 -230 189 -398 49197 -194 223 -380 49199 -198 227 -370 49179 -242 209 -390 49165 -228 223 -370 49167 -230 221 -380 49177 -230 211 -404 49171 -196 211 -402 49183 -228 181 -402 49185 -228 213 -370 49185 -234 199 -376 49195 -242 205 -382 49163 -230 197 -408 49177 -238 217 -358 49165 -244 185 -404 49189 -212 215 -376 49199 -198 227 -372 49191 -230 195 -378 49207 -202 217 -396 49169 -246 215 -380 49159 -236 197 -374 49199 -240 203 -384 49165 -228 197 -408 49177 -202 217 -396 49169 -246 215 -380 49161 -234 199 -374 49197 -228 195 -380 49197 -234 197 -374 49197 -228 195 -378 49197 -234 197 -372 49203 -200 225 -372 49195 -228 195 -380 49205 -196 211 -402 49161 -226 223 -380 49201 -202 217 -396 49169 -240 217 -362 49171 -244 215 -382 49165 -246 185 -396 49167 -234 201 -408 49159 -236 197 -406 49161 -242 203 -384 49165 -242 205 -388 49169 -240 205 -386 49153 -244 209 -390 49163 -246 213 -368 49161 -244 209 -392 49161 -244 207 -388 49161 -242 209 -390 49181 -206 237 -350 49213 -212 209 -390 49179 -196 211 -400 49201 -202 211 -404 49159 -248 213 -366 49183 -242 203 -380 49169 -242 205 -384 49157 -238 199 -378 49207 -204 205 -380 49185 -244 213 -372 49165 -242 215 -386 49171 -228 195 -408 49163 -236 219 -398 49161 -232 195 -374 49169 -272 181 -400 49173 -240 181 -392 49163 -236 217 -398 49173 -238 181 -400 49155 -236 217 -398 49155 -236 217 -396 49151 -236 199 -380 49175 -236 217 -396 49151 -244 207 -390 49157 -246 215 -372 49167 -238 201 -412 49163 -246 215 -378 49171 -228 193 -380 49193 -240 217 -392 49127 -236 211 -404 49167 -246 185 -394 49187 -202 211 -404 49179 -232 195 -370 49183 -242 207 -388 49159 -232 233 -370 49175 -238 213 -394 49139 -236 217 -398 49149 -244 207 -390 49173 -234 197 -374 49207 -204 203 -412 49145 -238 203 -412 49171 -240 205 -382 49151 -244 215 -374 49163 -246 213 -374 49191 -246 175 -398 49181 -242 203 -380 49155 -244 209 -390 49177 -210 211 -396 49179 -246 181 -376 49179 -244 207 -390 49167 -242 205 -384 49167 -242 207 -386 49165 -242 205 -384 49165 -236 199 -376 49199 -210 209 -396 49185 -210 211 -394 49185 -246 -RAW_Data: 175 -396 49171 -242 207 -386 49151 -232 227 -370 49175 -242 207 -390 49157 -246 215 -372 49173 -246 213 -366 49163 -238 203 -412 49169 -240 203 -384 49151 -244 209 -390 49175 -246 175 -396 49163 -242 215 -386 49157 -240 217 -386 49179 -246 175 -394 49185 -246 181 -408 49151 -238 217 -396 49161 -236 179 -396 49181 -228 189 -400 49173 -234 201 -378 49197 -246 175 -398 49183 -248 175 -396 49161 -244 207 -388 49165 -242 207 -388 From e521dfee2c66c63e52969890b2285aa11a369fcb Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 12 Feb 2023 16:50:24 +0100 Subject: [PATCH 197/231] godspeed unauthorized --- ReadMe.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ReadMe.md b/ReadMe.md index bb167c543..de9d5207d 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -113,9 +113,9 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - NSFW Animations tied to the level system. Read more above - Folder handling for empty ones (Now indicate they are empty) -- Jamming Files - Custom subghz presets - Multiple NFC protocols +- Multiple Sub-Ghz protocols | Merged from Unleashed, thanks @xMasterX - Subghz and IR signal replication via gpio | Credits to @ankris812 - Honda Keys (CVE-2022-27254) & Ford blockers From 36324e56d0783e41829444cd3c1d1497b1803ead Mon Sep 17 00:00:00 2001 From: Clara K Date: Sun, 12 Feb 2023 16:55:14 +0100 Subject: [PATCH 198/231] Update ReadMe.md --- ReadMe.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ReadMe.md b/ReadMe.md index de9d5207d..c36b1861b 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -127,6 +127,7 @@ Note: This repo is always updated with OFW & Unleashed. No need to mention all t - All Assets - Tons of apps +- File browser - Massive compiler re-do - About 1k files to speed things up a lot - Applications to now use the new Locale setting From 7f493b58d33205f51b1c4294b5af358730c86a88 Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Sun, 12 Feb 2023 19:40:33 +0100 Subject: [PATCH 199/231] minor changes --- applications/plugins/pomodoro/application.fam | 4 ++-- assets/slideshow/update_default/frame_00.png | Bin 4006 -> 4049 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/plugins/pomodoro/application.fam b/applications/plugins/pomodoro/application.fam index 750373342..ceb8cccc9 100644 --- a/applications/plugins/pomodoro/application.fam +++ b/applications/plugins/pomodoro/application.fam @@ -1,6 +1,6 @@ App( - appid="Pomodoro2", - name="Pomodoro 2", + appid="Pomodoro", + name="Pomodoro", apptype=FlipperAppType.EXTERNAL, entry_point="flipp_pomodoro_app", requires=["gui", "notification", "dolphin"], diff --git a/assets/slideshow/update_default/frame_00.png b/assets/slideshow/update_default/frame_00.png index 5be2666065d378318fdb823c9c0a45e1f1e10c07..9d2da78f84c8c915427e872caedd94f91bb1ad7f 100644 GIT binary patch delta 4030 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 t!_bV3^o;cX Date: Sun, 12 Feb 2023 12:28:40 +0000 Subject: [PATCH 200/231] Remove unnecessary sarcastic comment --- applications/services/gui/modules/file_browser_worker.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 28a968428..5955d8714 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -233,8 +233,7 @@ static bool // chances are, the new files are higher in the sorted list... // so the files keep shifting around while scrolling... // now this does something intelligent: loads and sorts all in one go. - // might take a few milliseconds longer, but atleast it works :kekw: - // and yes skotopes, most definitely a "limitation of fatfs driver" :roflmao: + // might take a few milliseconds longer, but atleast it works UNUSED(offset); UNUSED(count); if(browser->list_load_cb) { From e5b9289da8b221dea1b2b29b281bbed1da1c2f54 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 12 Feb 2023 23:55:39 +0300 Subject: [PATCH 201/231] Fix large folders parsing --- .../services/gui/modules/file_browser.c | 41 +++++--- .../gui/modules/file_browser_worker.c | 94 ++++++++++++++++++- 2 files changed, 119 insertions(+), 16 deletions(-) diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 7eae1dfaf..00b575922 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -164,8 +164,12 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context); static void browser_folder_open_cb(void* context, uint32_t item_cnt, int32_t file_idx, bool is_root); static void browser_list_load_cb(void* context, uint32_t list_load_offset); -static void - browser_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last); +static void browser_list_item_cb( + void* context, + FuriString* item_path, + uint32_t idx, + bool is_folder, + bool is_last); static void browser_long_load_cb(void* context); static void file_browser_scroll_timer_callback(void* context) { @@ -419,8 +423,12 @@ static void browser_list_load_cb(void* context, uint32_t list_load_offset) { BrowserItem_t_clear(&back_item); } -static void - browser_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last) { +static void browser_list_item_cb( + void* context, + FuriString* item_path, + uint32_t idx, + bool is_folder, + bool is_last) { furi_assert(context); FileBrowser* browser = (FileBrowser*)context; @@ -473,16 +481,21 @@ static void browser->view, FileBrowserModel * model, { - FuriString* selected = NULL; - if(model->item_idx > 0) { - selected = furi_string_alloc_set(items_array_get(model->items, model->item_idx)->path); - } - items_array_sort(model->items); - if(selected) { - for(uint32_t i = 0; i < model->item_cnt; i++) { - if(!furi_string_cmp(items_array_get(model->items, i)->path, selected)) { - model->item_idx = i; - break; + if(model->item_cnt < 430) { + FuriString* selected = NULL; + if(model->item_idx > 0) { + selected = furi_string_alloc_set( + items_array_get(model->items, model->item_idx)->path); + } + + items_array_sort(model->items); + + if(selected != NULL) { + for(uint32_t i = 0; i < model->item_cnt; i++) { + if(!furi_string_cmp(items_array_get(model->items, i)->path, selected)) { + model->item_idx = i; + break; + } } } } diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 5955d8714..faf307d55 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -189,6 +189,87 @@ static bool browser_folder_init( return state; } +static bool browser_folder_load_chunked( + BrowserWorker* browser, + FuriString* path, + uint32_t offset, + uint32_t count) { + FileInfo file_info; + + Storage* storage = furi_record_open(RECORD_STORAGE); + File* directory = storage_file_alloc(storage); + + char name_temp[FILE_NAME_LEN_MAX]; + FuriString* name_str; + name_str = furi_string_alloc(); + + uint32_t items_cnt = 0; + + do { + if(!storage_dir_open(directory, furi_string_get_cstr(path))) { + break; + } + + items_cnt = 0; + while(items_cnt < offset) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + if(storage_file_get_error(directory) == FSE_OK) { + furi_string_set(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + items_cnt++; + } + } else { + break; + } + } + if(items_cnt != offset) { + break; + } + + if(browser->list_load_cb) { + browser->list_load_cb(browser->cb_ctx, offset); + } + + items_cnt = 0; + while(items_cnt < count) { + if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { + break; + } + if(storage_file_get_error(directory) == FSE_OK) { + furi_string_set(name_str, name_temp); + if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { + furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); + if(browser->list_item_cb) { + browser->list_item_cb( + browser->cb_ctx, + name_str, + items_cnt, + (file_info.flags & FSF_DIRECTORY), + false); + } + items_cnt++; + } + } else { + break; + } + } + if(browser->list_item_cb) { + browser->list_item_cb(browser->cb_ctx, NULL, 0, false, true); + } + } while(0); + + furi_string_free(name_str); + + storage_dir_close(directory); + storage_file_free(directory); + + furi_record_close(RECORD_STORAGE); + + return (items_cnt == count); +} + static bool browser_folder_load(BrowserWorker* browser, FuriString* path, uint32_t offset, uint32_t count) { FileInfo file_info; @@ -246,7 +327,11 @@ static bool furi_string_printf(name_str, "%s/%s", furi_string_get_cstr(path), name_temp); if(browser->list_item_cb) { browser->list_item_cb( - browser->cb_ctx, name_str, items_cnt, (file_info.flags & FSF_DIRECTORY), false); + browser->cb_ctx, + name_str, + items_cnt, + (file_info.flags & FSF_DIRECTORY), + false); } items_cnt++; } @@ -361,7 +446,12 @@ static int32_t browser_worker(void* context) { if(flags & WorkerEvtLoad) { FURI_LOG_D( TAG, "Load offset: %lu cnt: %lu", browser->load_offset, browser->load_count); - browser_folder_load(browser, path, browser->load_offset, browser->load_count); + if(items_cnt > 430) { + browser_folder_load_chunked( + browser, path, browser->load_offset, browser->load_count); + } else { + browser_folder_load(browser, path, browser->load_offset, browser->load_count); + } } if(flags & WorkerEvtStop) { From 0ff5164d999d9805243ddbfb86a80c1a5c7ef332 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 21:11:00 +0000 Subject: [PATCH 202/231] Format (and thanks nano for the big folder fix <3) --- .../main/archive/helpers/archive_browser.c | 8 ++++++-- .../subghz/views/subghz_frequency_analyzer.c | 13 +++++++------ .../main/subghz/views/subghz_test_carrier.c | 5 ++++- applications/services/gui/gui.c | 9 +++++++-- applications/settings/xtreme_app/xtreme_app.c | 3 +-- firmware/targets/f7/ble_glue/hid_service.c | 17 ++++++++++++----- firmware/targets/f7/furi_hal/furi_hal_bt_hid.c | 4 ++-- .../targets/furi_hal_include/furi_hal_version.h | 3 ++- 8 files changed, 41 insertions(+), 21 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index fd3d5957e..467d253d4 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -56,8 +56,12 @@ static void archive_list_load_cb(void* context, uint32_t list_load_offset) { false); } -static void - archive_list_item_cb(void* context, FuriString* item_path, uint32_t idx, bool is_folder, bool is_last) { +static void archive_list_item_cb( + void* context, + FuriString* item_path, + uint32_t idx, + bool is_folder, + bool is_last) { furi_assert(context); UNUSED(idx); ArchiveBrowserView* browser = (ArchiveBrowserView*)context; diff --git a/applications/main/subghz/views/subghz_frequency_analyzer.c b/applications/main/subghz/views/subghz_frequency_analyzer.c index 24f24c39d..ce2e34297 100644 --- a/applications/main/subghz/views/subghz_frequency_analyzer.c +++ b/applications/main/subghz/views/subghz_frequency_analyzer.c @@ -21,12 +21,13 @@ #define MAX_HISTORY 4 static const uint32_t subghz_frequency_list[] = { - 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, 309000000, - 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, 314350000, 314980000, - 315000000, 318000000, 330000000, 345000000, 348000000, 350000000, 387000000, 390000000, - 418000000, 433075000, 433220000, 433420000, 433657070, 433889000, 433920000, 434075000, - 434176948, 434390000, 434420000, 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, - 868350000, 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; + 300000000, 302757000, 303875000, 304250000, 307000000, 307500000, 307800000, + 309000000, 310000000, 312000000, 312100000, 313000000, 313850000, 314000000, + 314350000, 314980000, 315000000, 318000000, 330000000, 345000000, 348000000, + 350000000, 387000000, 390000000, 418000000, 433075000, 433220000, 433420000, + 433657070, 433889000, 433920000, 434075000, 434176948, 434390000, 434420000, + 434775000, 438900000, 440175000, 464000000, 467750000, 779000000, 868350000, + 868400000, 868800000, 868950000, 906400000, 915000000, 925000000, 928000000}; typedef enum { SubGhzFrequencyAnalyzerStatusIDLE, diff --git a/applications/main/subghz/views/subghz_test_carrier.c b/applications/main/subghz/views/subghz_test_carrier.c index 0cc9a2966..2cbde6e32 100644 --- a/applications/main/subghz/views/subghz_test_carrier.c +++ b/applications/main/subghz/views/subghz_test_carrier.c @@ -120,7 +120,10 @@ bool subghz_test_carrier_input(InputEvent* event, void* context) { furi_hal_subghz_rx(); } else { furi_hal_gpio_init( - furi_hal_subghz.cc1101_g0_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + furi_hal_subghz.cc1101_g0_pin, + GpioModeOutputPushPull, + GpioPullNo, + GpioSpeedLow); furi_hal_gpio_write(furi_hal_subghz.cc1101_g0_pin, true); if(!furi_hal_subghz_tx()) { furi_hal_gpio_init( diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 2fdec197b..daec0c606 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -101,7 +101,11 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { canvas_set_color(gui->canvas, ColorBlack); // ViewPort draw canvas_frame_set( - gui->canvas, x - xtreme_settings->status_bar, GUI_STATUS_BAR_Y + 2, width, GUI_STATUS_BAR_WORKAREA_HEIGHT); + gui->canvas, + x - xtreme_settings->status_bar, + GUI_STATUS_BAR_Y + 2, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); view_port_draw(view_port, gui->canvas); } ViewPortArray_next(it); @@ -175,7 +179,8 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box(gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); canvas_set_color(gui->canvas, ColorBlack); // Draw Icon canvas_frame_set( diff --git a/applications/settings/xtreme_app/xtreme_app.c b/applications/settings/xtreme_app/xtreme_app.c index cdacf6cca..c6d67b915 100644 --- a/applications/settings/xtreme_app/xtreme_app.c +++ b/applications/settings/xtreme_app/xtreme_app.c @@ -82,8 +82,7 @@ XtremeApp* xtreme_app_alloc() { variable_item_list_get_view(app->var_item_list)); app->popup = popup_alloc(); - view_dispatcher_add_view( - app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); + view_dispatcher_add_view(app->view_dispatcher, XtremeAppViewPopup, popup_get_view(app->popup)); return app; } diff --git a/firmware/targets/f7/ble_glue/hid_service.c b/firmware/targets/f7/ble_glue/hid_service.c index f983ec13b..cc1a2fbc5 100644 --- a/firmware/targets/f7/ble_glue/hid_service.c +++ b/firmware/targets/f7/ble_glue/hid_service.c @@ -47,11 +47,18 @@ static SVCCTL_EvtAckStatus_t hid_svc_event_handler(void* event) { // that specify attibute handle value from char handle (or the reverse) if(req->Attribute_Handle == (hid_svc->led_state_char_handle + 1)) { hid_svc->led_state_event_callback(req->Data[0], hid_svc->led_state_ctx); - aci_gatt_write_resp(req->Connection_Handle, req->Attribute_Handle, - 0x00, /* write_status = 0 (no error))*/ - 0x00, /* err_code */ - req->Data_Length, req->Data); - aci_gatt_write_char_value(req->Connection_Handle, hid_svc->led_state_char_handle, req->Data_Length, req->Data); + aci_gatt_write_resp( + req->Connection_Handle, + req->Attribute_Handle, + 0x00, /* write_status = 0 (no error))*/ + 0x00, /* err_code */ + req->Data_Length, + req->Data); + aci_gatt_write_char_value( + req->Connection_Handle, + hid_svc->led_state_char_handle, + req->Data_Length, + req->Data); ret = SVCCTL_EvtAckFlowEnable; } } diff --git a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c index 6921fbbc5..be0bd2af3 100644 --- a/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c +++ b/firmware/targets/f7/furi_hal/furi_hal_bt_hid.c @@ -179,8 +179,8 @@ uint8_t furi_hal_bt_hid_get_led_state(void) { hid_host_led_state.s_shift); return (hid_host_led_state.s_value >> 1); // bit 0 is undefined (after shift bit location - // match with HID led state bits defines) - // see bad_kb_script.c (ducky_numlock_on function) + // match with HID led state bits defines) + // see bad_kb_script.c (ducky_numlock_on function) } void furi_hal_bt_hid_start() { diff --git a/firmware/targets/furi_hal_include/furi_hal_version.h b/firmware/targets/furi_hal_include/furi_hal_version.h index 63cb948db..889b29777 100644 --- a/firmware/targets/furi_hal_include/furi_hal_version.h +++ b/firmware/targets/furi_hal_include/furi_hal_version.h @@ -17,7 +17,8 @@ extern "C" { #define FURI_HAL_VERSION_NAME_LENGTH 8 #define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1) /** BLE symbol + name */ -#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) + 9 // for bad kb custom name +#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH \ + (1 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH) + 9 // for bad kb custom name /** OTP Versions enum */ typedef enum { From efcfcedaed8496953828fa5af8d0d2fe706b6731 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 13 Feb 2023 00:41:50 +0300 Subject: [PATCH 203/231] Rename functions and cleanup a bit --- .../gui/modules/file_browser_worker.c | 36 +++---------------- 1 file changed, 4 insertions(+), 32 deletions(-) diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index faf307d55..8e56ab273 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -189,6 +189,7 @@ static bool browser_folder_init( return state; } +// Load files list by chunks, like it was originally, not compatible with sorting, sorting needs to be disabled to use this static bool browser_folder_load_chunked( BrowserWorker* browser, FuriString* path, @@ -270,8 +271,8 @@ static bool browser_folder_load_chunked( return (items_cnt == count); } -static bool - browser_folder_load(BrowserWorker* browser, FuriString* path, uint32_t offset, uint32_t count) { +// Load all files at once, may cause memory overflow so need to limit that to about 400 files +static bool browser_folder_load_full(BrowserWorker* browser, FuriString* path) { FileInfo file_info; Storage* storage = furi_record_open(RECORD_STORAGE); @@ -288,35 +289,6 @@ static bool if(!storage_dir_open(directory, furi_string_get_cstr(path))) { break; } - - // items_cnt = 0; - // while(items_cnt < offset) { - // if(!storage_dir_read(directory, &file_info, name_temp, FILE_NAME_LEN_MAX)) { - // break; - // } - // if(storage_file_get_error(directory) == FSE_OK) { - // furi_string_set(name_str, name_temp); - // if(browser_filter_by_name(browser, name_str, (file_info.flags & FSF_DIRECTORY))) { - // items_cnt++; - // } - // } else { - // break; - // } - // } - // if(items_cnt != offset) { - // break; - // } - - // ROGUE MASTER MOMENT - // this used to load the file list in chunks, which makes sense - // but then RM made it sort the files, still in chunks... - // so while scrolling, it loads more files and sorts them... - // chances are, the new files are higher in the sorted list... - // so the files keep shifting around while scrolling... - // now this does something intelligent: loads and sorts all in one go. - // might take a few milliseconds longer, but atleast it works - UNUSED(offset); - UNUSED(count); if(browser->list_load_cb) { browser->list_load_cb(browser->cb_ctx, 0); } @@ -450,7 +422,7 @@ static int32_t browser_worker(void* context) { browser_folder_load_chunked( browser, path, browser->load_offset, browser->load_count); } else { - browser_folder_load(browser, path, browser->load_offset, browser->load_count); + browser_folder_load_full(browser, path); } } From 025b4e27ea7d542d5b3efe44987cac3f882e7a3c Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 22:35:30 +0000 Subject: [PATCH 204/231] Fix statusbar without battery --- applications/services/gui/gui.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index daec0c606..24936e8eb 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -93,7 +93,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); // Hide battery background - if(xtreme_settings->status_bar) { + if(xtreme_settings->status_bar && xtreme_settings->battery_style != BatteryStyleOff) { canvas_set_color(gui->canvas, ColorWhite); canvas_draw_box( gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); @@ -119,7 +119,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { right_used + 4, GUI_STATUS_BAR_HEIGHT); // Disable battery border - if(xtreme_settings->status_bar) { + if(xtreme_settings->status_bar && xtreme_settings->battery_style != BatteryStyleOff) { canvas_set_color(gui->canvas, ColorBlack); canvas_draw_rframe( gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); From 9800d1686e48270a1972626ef46a6789209246e1 Mon Sep 17 00:00:00 2001 From: jbohack Date: Sun, 12 Feb 2023 17:49:41 -0500 Subject: [PATCH 205/231] added demo_ios for badusb over bluetooth / connector thanks @Peaakss for the payload :D --- assets/resources/badkb/demo_ios.txt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 assets/resources/badkb/demo_ios.txt diff --git a/assets/resources/badkb/demo_ios.txt b/assets/resources/badkb/demo_ios.txt new file mode 100644 index 000000000..17d0722d3 --- /dev/null +++ b/assets/resources/badkb/demo_ios.txt @@ -0,0 +1,24 @@ +REM Version 1.0 +REM OS: iOS +REM Author: Peaakss +REM Description: A simple payload that opens safari and inserts a link +REM NOTICE CHANGE "STRING" to your desired link | EXAMPLE: STRING https://github.com/ClaraCrazy/Flipper-Xtreme | +REM NOITCE Payload was made on iOS 16.1 - iPhone | Timing may have have to be changed based on version/model + +GUI h +DELAY 100 +GUI SPACE +DELAY 150 +BACKSPACE +DELAY 250 +STRING Safari +DELAY 100 +ENTER +DELAY 500 +GUI t +DELAY 250 +GUI l +DELAY 100 +STRING https://github.com/ClaraCrazy/Flipper-Xtreme +DELAY 250 +ENTER \ No newline at end of file From 8a2434c9781ce8fa8ae0ba68d00da93d24cd08d0 Mon Sep 17 00:00:00 2001 From: jbohack Date: Sun, 12 Feb 2023 17:55:19 -0500 Subject: [PATCH 206/231] added android demo file for badkb (badusb) over bluetooth / connector thanks for the payload @John Hickens ! --- assets/resources/badkb/demo_android.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 assets/resources/badkb/demo_android.txt diff --git a/assets/resources/badkb/demo_android.txt b/assets/resources/badkb/demo_android.txt new file mode 100644 index 000000000..2e4820490 --- /dev/null +++ b/assets/resources/badkb/demo_android.txt @@ -0,0 +1,14 @@ +REM Just another rickroll payload but for android +REM Credit: John Hickens + +GUI b +DELAY 600 +ENTER +DELAY 1000 +CTRL l +DELAY 100 +STRING https://github.com/ClaraCrazy/Flipper-Xtreme +DELAY 100 +ENTER +DELAY 500 +STRING f \ No newline at end of file From 2f4adec619d9ad632d1086f069576d1a24430d14 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 23:24:18 +0000 Subject: [PATCH 207/231] Fix asset animation free on load fail --- applications/services/xtreme/assets.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index 4d3f3470b..cde34c216 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -38,7 +38,17 @@ void anim(const Icon** replace, const char* name, FuriString* path, File* file) if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; storage_file_close(file); } - if(!ok) break; + if(!ok) { + for(int i = 0; i < icon->frame_count; ++i) { + if(icon->frames[i]) { + free((void*)icon->frames[i]); + } + } + free((void*)icon->frames); + free(icon); + + break; + } *replace = icon; } while(false); From 8812dbc2cd968274c1cc97f562d968c101524b67 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Sun, 12 Feb 2023 23:24:58 +0000 Subject: [PATCH 208/231] Fix formatter escape code bug warning --- .../plugins/spi_mem_manager/scenes/spi_mem_scene_about.c | 2 ++ .../spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c index dc0cc4fe4..84fa974ff 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -1,3 +1,5 @@ +#pragma warning disable c:S796 + #include "../spi_mem_app_i.h" #include "../lib/spi/spi_mem_chip.h" diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c index bb5142452..c134c62d0 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c @@ -1,3 +1,5 @@ +#pragma warning disable c:S796 + #include "../spi_mem_app_i.h" #include "../spi_mem_files.h" From 53780e72ed296f7fa558075fdd2a22ca4612f021 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 00:01:49 +0000 Subject: [PATCH 209/231] Misc sonarcloud fixes --- applications/main/bad_kb/bad_kb_script.c | 2 -- applications/plugins/protoview/view_settings.c | 2 +- .../plugins/spi_mem_manager/lib/spi/spi_mem_tools.c | 2 +- .../services/desktop/animations/animation_storage.c | 6 ++++-- scripts/fbt/appmanifest.py | 3 ++- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/applications/main/bad_kb/bad_kb_script.c b/applications/main/bad_kb/bad_kb_script.c index 98177209e..e2fd464ff 100644 --- a/applications/main/bad_kb/bad_kb_script.c +++ b/applications/main/bad_kb/bad_kb_script.c @@ -595,8 +595,6 @@ static int32_t ducky_script_execute_next(BadKbScript* bad_kb, File* script_file) bad_kb->buf_len = 0; if(bad_kb->file_end) return SCRIPT_STATE_END; } - - return 0; } static void bad_kb_bt_hid_state_callback(BtStatus status, void* context) { diff --git a/applications/plugins/protoview/view_settings.c b/applications/plugins/protoview/view_settings.c index 09abf5a2a..94d80cfb5 100644 --- a/applications/plugins/protoview/view_settings.c +++ b/applications/plugins/protoview/view_settings.c @@ -88,7 +88,7 @@ void process_input_settings(ProtoViewApp* app, InputEvent input) { if(input.key == InputKeyUp) { modid = modid == 0 ? count - 1 : modid - 1; } else if(input.key == InputKeyDown) { - modid = (modid + 1) % count; + modid = (modid + 1) % (count ? count : 1); } else { return; } diff --git a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c index 3518ca25c..7a788241b 100644 --- a/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c +++ b/applications/plugins/spi_mem_manager/lib/spi/spi_mem_tools.c @@ -74,8 +74,8 @@ bool spi_mem_tools_read_chip_info(SPIMemChip* chip) { bool spi_mem_tools_check_chip_info(SPIMemChip* chip) { SPIMemChip new_chip_info; - spi_mem_tools_read_chip_info(&new_chip_info); do { + if(!spi_mem_tools_read_chip_info(&new_chip_info)) break; if(chip->vendor_id != new_chip_info.vendor_id) break; if(chip->type_id != new_chip_info.type_id) break; if(chip->capacity_id != new_chip_info.capacity_id) break; diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 4ac36762e..4808521a8 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -291,9 +291,10 @@ static void animation_storage_free_frames(BubbleAnimation* animation) { const Icon* icon = &animation->icon_animation; for(int i = 0; i < icon->frame_count; ++i) { - if(icon->frames[i]) { - free((void*)icon->frames[i]); + if(!icon->frames[i]) { + break; } + free((void*)icon->frames[i]); } free((void*)icon->frames); @@ -336,6 +337,7 @@ static bool animation_storage_load_frames( frames_ok = false; furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); + FURI_CONST_ASSIGN_PTR(icon->frames[i], 0); if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK) break; if(file_info.size > max_filesize) { diff --git a/scripts/fbt/appmanifest.py b/scripts/fbt/appmanifest.py index 1c815c902..4f960de83 100644 --- a/scripts/fbt/appmanifest.py +++ b/scripts/fbt/appmanifest.py @@ -194,6 +194,8 @@ class AppBuildset: return self.appmgr.get(app_name).supports_hardware_target(self.hw_target) def _get_app_depends(self, app_name: str) -> List[str]: + app_def = self.appmgr.get(app_name) + # Skip app if its target is not supported by the target we are building for if not self._check_if_app_target_supported(app_name): self._writer( @@ -201,7 +203,6 @@ class AppBuildset: ) return [] - app_def = self.appmgr.get(app_name) return list( filter( self._check_if_app_target_supported, From e7e508cf599997631651e13b5cf61f511c4175af Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 00:13:47 +0000 Subject: [PATCH 210/231] Change sonar disable method --- .../spi_mem_manager/scenes/spi_mem_scene_about.c | 10 ++++------ .../scenes/spi_mem_scene_delete_confirm.c | 4 +--- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c index 84fa974ff..9c9951ad0 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -1,13 +1,11 @@ -#pragma warning disable c:S796 - #include "../spi_mem_app_i.h" #include "../lib/spi/spi_mem_chip.h" #define SPI_MEM_VERSION_APP "0.1.0" #define SPI_MEM_DEVELOPER "DrunkBatya" #define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" -#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" -#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" +#define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" // NOSONAR +#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" // NOSONAR void spi_mem_scene_about_on_enter(void* context) { SPIMemApp* app = context; @@ -17,11 +15,11 @@ void spi_mem_scene_about_on_enter(void* context) { app->widget, 0, 0, 128, 14, AlignCenter, AlignBottom, SPI_MEM_BLANK_INV, false); widget_add_text_box_element( app->widget, 0, 2, 128, 14, AlignCenter, AlignBottom, SPI_MEM_NAME, false); - furi_string_printf(tmp_string, "\e#%s\n", "Information"); + furi_string_printf(tmp_string, "\e#%s\n", "Information"); // NOSONAR furi_string_cat_printf(tmp_string, "Version: %s\n", SPI_MEM_VERSION_APP); furi_string_cat_printf(tmp_string, "Developed by: %s\n", SPI_MEM_DEVELOPER); furi_string_cat_printf(tmp_string, "Github: %s\n\n", SPI_MEM_GITHUB); - furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); + furi_string_cat_printf(tmp_string, "\e#%s\n", "Description"); // NOSONAR furi_string_cat_printf( tmp_string, "SPI memory dumper\n" diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c index c134c62d0..5d0c0b8d8 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_delete_confirm.c @@ -1,5 +1,3 @@ -#pragma warning disable c:S796 - #include "../spi_mem_app_i.h" #include "../spi_mem_files.h" @@ -18,7 +16,7 @@ void spi_mem_scene_delete_confirm_on_enter(void* context) { FuriString* file_name = furi_string_alloc(); FuriString* message = furi_string_alloc(); path_extract_filename(app->file_path, file_name, true); - furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); + furi_string_printf(message, "\e#Delete %s?\e#", furi_string_get_cstr(file_name)); // NOSONAR widget_add_text_box_element( app->widget, 0, 0, 128, 27, AlignCenter, AlignCenter, furi_string_get_cstr(message), true); widget_add_button_element( From 22cf51e92b456bea33d9db4ddf3e6de8e3b338e3 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 00:41:47 +0000 Subject: [PATCH 211/231] Fix garbage memory freeing --- .../desktop/animations/animation_storage.c | 63 ++++++++++--------- applications/services/xtreme/assets.c | 21 ++++--- 2 files changed, 46 insertions(+), 38 deletions(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 4808521a8..c9f4cc68f 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -291,10 +291,9 @@ static void animation_storage_free_frames(BubbleAnimation* animation) { const Icon* icon = &animation->icon_animation; for(int i = 0; i < icon->frame_count; ++i) { - if(!icon->frames[i]) { - break; + if(icon->frames[i]) { + free((void*)icon->frames[i]); } - free((void*)icon->frames[i]); } free((void*)icon->frames); @@ -326,7 +325,7 @@ static bool animation_storage_load_frames( FURI_CONST_ASSIGN(icon->width, width); icon->frames = malloc(sizeof(const uint8_t*) * icon->frame_count); - bool frames_ok = false; + bool frames_ok = true; File* file = storage_file_alloc(storage); FileInfo file_info; FuriString* filename; @@ -334,35 +333,39 @@ static bool animation_storage_load_frames( size_t max_filesize = ROUND_UP_TO(width, 8) * height + 2; for(int i = 0; i < icon->frame_count; ++i) { - frames_ok = false; - furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); - FURI_CONST_ASSIGN_PTR(icon->frames[i], 0); - if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK) - break; - if(file_info.size > max_filesize) { - FURI_LOG_E( - TAG, - "Filesize %lld, max: %d (width %d, height %d)", - file_info.size, - max_filesize, - width, - height); - break; - } - if(!storage_file_open( - file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { - FURI_LOG_E(TAG, "Can't open file \'%s\'", furi_string_get_cstr(filename)); - break; - } + if(frames_ok) { + frames_ok = false; + furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); + do { + if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK) + break; + if(file_info.size > max_filesize) { + FURI_LOG_E( + TAG, + "Filesize %lld, max: %d (width %d, height %d)", + file_info.size, + max_filesize, + width, + height); + break; + } + if(!storage_file_open( + file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { + FURI_LOG_E(TAG, "Can't open file \'%s\'", furi_string_get_cstr(filename)); + break; + } - FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); - if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) { - FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); - break; + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); + if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) { + FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); + break; + } else { + frames_ok = true; + } + storage_file_close(file); + } while(0); } - storage_file_close(file); - frames_ok = true; } if(!frames_ok) { diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index cde34c216..20afb9f90 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -28,15 +28,20 @@ void anim(const Icon** replace, const char* name, FuriString* path, File* file) bool ok = true; for(int i = 0; ok && i < icon->frame_count; ++i) { - ok = false; - furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) - break; + FURI_CONST_ASSIGN_PTR(icon->frames[i], 0); + if(ok) { + ok = false; + furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); + do { + if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + break; - uint64_t size = storage_file_size(file); - FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); - if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; - storage_file_close(file); + uint64_t size = storage_file_size(file); + FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(size)); + if(storage_file_read(file, (void*)icon->frames[i], size) == size) ok = true; + storage_file_close(file); + } while(0); + } } if(!ok) { for(int i = 0; i < icon->frame_count; ++i) { From 4b9d270af7b0b2dad5ef0772d7b22dfc82971918 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 01:10:06 +0000 Subject: [PATCH 212/231] Fix garbage memory free again --- applications/services/xtreme/assets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index 20afb9f90..95ddf4ded 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -27,7 +27,7 @@ void anim(const Icon** replace, const char* name, FuriString* path, File* file) const char* pack = XTREME_SETTINGS()->asset_pack; bool ok = true; - for(int i = 0; ok && i < icon->frame_count; ++i) { + for(int i = 0; i < icon->frame_count; ++i) { FURI_CONST_ASSIGN_PTR(icon->frames[i], 0); if(ok) { ok = false; From 85d5e8a0101dcd20bc79a4ff6c0bd8a9c100a98d Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 01:10:26 +0000 Subject: [PATCH 213/231] Ignore some global sonarcloud hotspots --- sonar-project.properties | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/sonar-project.properties b/sonar-project.properties index 43c6c8dec..f707b3819 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -10,3 +10,19 @@ sonar.organization=claracrazy # Encoding of the source code. Default is default system encoding #sonar.sourceEncoding=UTF-8 + + +# Ignore some rules +sonar.issue.ignore.multicriteria=c1,c2,c3 + +# Make sure use of "strcpy" is safe here +sonar.issue.ignore.multicriteria.c1.ruleKey=c:S5801 +sonar.issue.ignore.multicriteria.c1.resourceKey=**/*.c + +# Make sure use of "strlen" is safe here +sonar.issue.ignore.multicriteria.c2.ruleKey=c:S5813 +sonar.issue.ignore.multicriteria.c2.resourceKey=**/*.c + +# Make sure that using this pseudorandom number generator "rand" is safe here +sonar.issue.ignore.multicriteria.c3.ruleKey=c:S2245 +sonar.issue.ignore.multicriteria.c4.resourceKey=**/*.c From 5ed6abc4f7b30c9a22dd980bbb1fad0201ece8ef Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 01:40:49 +0000 Subject: [PATCH 214/231] Format --- .../plugins/spi_mem_manager/scenes/spi_mem_scene_about.c | 3 ++- .../services/desktop/animations/animation_storage.c | 8 +++++--- applications/services/xtreme/assets.c | 3 ++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c index 9c9951ad0..9e0f6f0c6 100644 --- a/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c +++ b/applications/plugins/spi_mem_manager/scenes/spi_mem_scene_about.c @@ -5,7 +5,8 @@ #define SPI_MEM_DEVELOPER "DrunkBatya" #define SPI_MEM_GITHUB "https://github.com/flipperdevices/flipperzero-firmware" #define SPI_MEM_NAME "\e#\e! SPI Mem Manager \e!\n" // NOSONAR -#define SPI_MEM_BLANK_INV "\e#\e! \e!\n" // NOSONAR +#define SPI_MEM_BLANK_INV \ + "\e#\e! \e!\n" // NOSONAR void spi_mem_scene_about_on_enter(void* context) { SPIMemApp* app = context; diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index c9f4cc68f..1b5f20e55 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -338,7 +338,8 @@ static bool animation_storage_load_frames( frames_ok = false; furi_string_printf(filename, "%s/%s/frame_%d.bm", ANIMATION_DIR, name, i); do { - if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != FSE_OK) + if(storage_common_stat(storage, furi_string_get_cstr(filename), &file_info) != + FSE_OK) break; if(file_info.size > max_filesize) { FURI_LOG_E( @@ -351,13 +352,14 @@ static bool animation_storage_load_frames( break; } if(!storage_file_open( - file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { + file, furi_string_get_cstr(filename), FSAM_READ, FSOM_OPEN_EXISTING)) { FURI_LOG_E(TAG, "Can't open file \'%s\'", furi_string_get_cstr(filename)); break; } FURI_CONST_ASSIGN_PTR(icon->frames[i], malloc(file_info.size)); - if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != file_info.size) { + if(storage_file_read(file, (void*)icon->frames[i], file_info.size) != + file_info.size) { FURI_LOG_E(TAG, "Read failed: \'%s\'", furi_string_get_cstr(filename)); break; } else { diff --git a/applications/services/xtreme/assets.c b/applications/services/xtreme/assets.c index 95ddf4ded..1862f1ec7 100644 --- a/applications/services/xtreme/assets.c +++ b/applications/services/xtreme/assets.c @@ -33,7 +33,8 @@ void anim(const Icon** replace, const char* name, FuriString* path, File* file) ok = false; furi_string_printf(path, ICONS_FMT "/frame_%02d.bm", pack, name, i); do { - if(!storage_file_open(file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) + if(!storage_file_open( + file, furi_string_get_cstr(path), FSAM_READ, FSOM_OPEN_EXISTING)) break; uint64_t size = storage_file_size(file); From d3cef9b42f9704f52241ee01dfee2f6143662175 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 02:26:02 +0000 Subject: [PATCH 215/231] Fix sonarcloud config --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index f707b3819..30fc27a96 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -25,4 +25,4 @@ sonar.issue.ignore.multicriteria.c2.resourceKey=**/*.c # Make sure that using this pseudorandom number generator "rand" is safe here sonar.issue.ignore.multicriteria.c3.ruleKey=c:S2245 -sonar.issue.ignore.multicriteria.c4.resourceKey=**/*.c +sonar.issue.ignore.multicriteria.c3.resourceKey=**/*.c From 9fe313699383a2b61afb6ed1c886bb6c95422ec4 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 04:57:44 +0000 Subject: [PATCH 216/231] Skip browser inputs while loading / sorting --- applications/main/archive/views/archive_browser_view.c | 5 +++++ applications/services/gui/modules/file_browser.c | 5 ++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/applications/main/archive/views/archive_browser_view.c b/applications/main/archive/views/archive_browser_view.c index c7d0e14f2..59c20ce6b 100644 --- a/applications/main/archive/views/archive_browser_view.c +++ b/applications/main/archive/views/archive_browser_view.c @@ -398,15 +398,20 @@ static bool archive_view_input(InputEvent* event, void* context) { bool in_menu; bool move_fav_mode; + bool is_loading; with_view_model( browser->view, ArchiveBrowserViewModel * model, { in_menu = model->menu; move_fav_mode = model->move_fav; + is_loading = model->folder_loading || model->list_loading; }, false); + if(is_loading) { + return false; + } if(in_menu) { if(event->type != InputTypeShort) { return true; // RETURN diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 00b575922..86a53808b 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -649,7 +649,10 @@ static bool file_browser_view_input_callback(InputEvent* event, void* context) { bool is_loading = false; with_view_model( - browser->view, FileBrowserModel * model, { is_loading = model->folder_loading; }, false); + browser->view, + FileBrowserModel * model, + { is_loading = model->folder_loading || model->list_loading; }, + false); if(is_loading) { return false; From abeb34b983fe5c75b0526886efc16ebbc5b7a6e7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 05:01:31 +0000 Subject: [PATCH 217/231] Fix archive sorting (skip on big folders) --- applications/main/archive/helpers/archive_browser.c | 4 +++- applications/services/gui/modules/file_browser.c | 2 +- applications/services/gui/modules/file_browser_worker.c | 2 +- applications/services/gui/modules/file_browser_worker.h | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/applications/main/archive/helpers/archive_browser.c b/applications/main/archive/helpers/archive_browser.c index 467d253d4..78b010a78 100644 --- a/applications/main/archive/helpers/archive_browser.c +++ b/applications/main/archive/helpers/archive_browser.c @@ -73,7 +73,9 @@ static void archive_list_item_cb( browser->view, ArchiveBrowserViewModel * model, { - files_array_sort(model->files); + if(model->item_cnt <= BROWSER_SORT_THRESHOLD) { + files_array_sort(model->files); + } model->list_loading = false; }, true); diff --git a/applications/services/gui/modules/file_browser.c b/applications/services/gui/modules/file_browser.c index 86a53808b..5477192a0 100644 --- a/applications/services/gui/modules/file_browser.c +++ b/applications/services/gui/modules/file_browser.c @@ -481,7 +481,7 @@ static void browser_list_item_cb( browser->view, FileBrowserModel * model, { - if(model->item_cnt < 430) { + if(model->item_cnt <= BROWSER_SORT_THRESHOLD) { FuriString* selected = NULL; if(model->item_idx > 0) { selected = furi_string_alloc_set( diff --git a/applications/services/gui/modules/file_browser_worker.c b/applications/services/gui/modules/file_browser_worker.c index 8e56ab273..9d219429b 100644 --- a/applications/services/gui/modules/file_browser_worker.c +++ b/applications/services/gui/modules/file_browser_worker.c @@ -418,7 +418,7 @@ static int32_t browser_worker(void* context) { if(flags & WorkerEvtLoad) { FURI_LOG_D( TAG, "Load offset: %lu cnt: %lu", browser->load_offset, browser->load_count); - if(items_cnt > 430) { + if(items_cnt > BROWSER_SORT_THRESHOLD) { browser_folder_load_chunked( browser, path, browser->load_offset, browser->load_count); } else { diff --git a/applications/services/gui/modules/file_browser_worker.h b/applications/services/gui/modules/file_browser_worker.h index 19a9337ff..859b11be4 100644 --- a/applications/services/gui/modules/file_browser_worker.h +++ b/applications/services/gui/modules/file_browser_worker.h @@ -7,6 +7,8 @@ extern "C" { #endif +#define BROWSER_SORT_THRESHOLD 400 + typedef struct BrowserWorker BrowserWorker; typedef void (*BrowserWorkerFolderOpenCallback)( void* context, From 61e792eeeb1678d9a429aef70309ae85ac53c817 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 22:06:59 +0000 Subject: [PATCH 218/231] Remove xfw setting animation speed cap --- applications/services/desktop/animations/animation_storage.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/applications/services/desktop/animations/animation_storage.c b/applications/services/desktop/animations/animation_storage.c index 1b5f20e55..0d09f18dd 100644 --- a/applications/services/desktop/animations/animation_storage.c +++ b/applications/services/desktop/animations/animation_storage.c @@ -530,9 +530,7 @@ static BubbleAnimation* animation_storage_load_animation(const char* name) { uint16_t anim_speed = XTREME_SETTINGS()->anim_speed; anim_speed = (anim_speed == 0 ? 100 : anim_speed); u32value = (u32value * anim_speed) / 100; - u32value = u32value < 1 ? 1 : u32value; - u32value = u32value > 10 ? 10 : u32value; - FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value); + FURI_CONST_ASSIGN(animation->icon_animation.frame_rate, u32value < 1 ? 1 : u32value); if(!flipper_format_read_uint32(ff, "Duration", &u32value, 1)) break; animation->duration = u32value; if(!flipper_format_read_uint32(ff, "Active cooldown", &u32value, 1)) break; From 6a1869b9f15947831a97229da4d414f1d88b39e1 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 22:13:01 +0000 Subject: [PATCH 219/231] Rename batt style to icon and improve value logic --- ReadMe.md | 2 +- applications/services/gui/gui.c | 4 +-- .../services/power/power_service/power.c | 18 ++++++------- .../services/power/power_service/power.h | 17 ++++++------ applications/services/xtreme/settings.h | 2 +- .../xtreme_app/scenes/xtreme_app_scene_main.c | 26 +++++++------------ 6 files changed, 31 insertions(+), 38 deletions(-) diff --git a/ReadMe.md b/ReadMe.md index c36b1861b..027fb2fce 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -42,7 +42,7 @@ We wrote a powerful yet easy-to-use application specifically for our Firmware, t
Anim Speed: Speed in which the animations play
Cycle Anims: Duration of how long animations are played before switching to next
Unlock Anims: Custom setting just for NSFW fallback animations. Figure it out ;) -
Battery style: Classic Firmware battery style toggle, just at a more convenient place +
Battery Icon: Classic Firmware battery style toggle, just at a more convenient place
XP Level: Changes your Flippers level
SubGhz Extend: Allows you to extend the subghz range beyond what FZ devs planned
SubGhz Bypass: Allows you to bypass the subghz region locks of the Flipper diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 24936e8eb..2084b6337 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -93,7 +93,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); // Hide battery background - if(xtreme_settings->status_bar && xtreme_settings->battery_style != BatteryStyleOff) { + if(xtreme_settings->status_bar && xtreme_settings->battery_icon != BatteryIconOff) { canvas_set_color(gui->canvas, ColorWhite); canvas_draw_box( gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); @@ -119,7 +119,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { right_used + 4, GUI_STATUS_BAR_HEIGHT); // Disable battery border - if(xtreme_settings->status_bar && xtreme_settings->battery_style != BatteryStyleOff) { + if(xtreme_settings->status_bar && xtreme_settings->battery_icon != BatteryIconOff) { canvas_set_color(gui->canvas, ColorBlack); canvas_draw_rframe( gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); diff --git a/applications/services/power/power_service/power.c b/applications/services/power/power_service/power.c index 6f31f3044..aff3d946d 100644 --- a/applications/services/power/power_service/power.c +++ b/applications/services/power/power_service/power.c @@ -9,8 +9,8 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { furi_assert(context); Power* power = context; - BatteryStyle battery_style = XTREME_SETTINGS()->battery_style; - if(battery_style == BatteryStyleOff) return; + BatteryIcon battery_icon = XTREME_SETTINGS()->battery_icon; + if(battery_icon == BatteryIconOff) return; canvas_draw_icon(canvas, 0, 0, &I_Battery_25x8); canvas_set_color(canvas, ColorWhite); @@ -25,7 +25,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { char batteryPercentile[4]; snprintf(batteryPercentile, sizeof(batteryPercentile), "%d", power->info.charge); - if((battery_style == BatteryStylePercent) && + if((battery_icon == BatteryIconPercent) && (power->state != PowerStateCharging)) { //if display battery percentage, black background white text canvas_set_font(canvas, FontBatteryPercent); @@ -34,14 +34,14 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_set_color(canvas, ColorWhite); canvas_draw_str_aligned(canvas, 11, 4, AlignCenter, AlignCenter, batteryPercentile); } else if( - (battery_style == BatteryStyleInvertedPercent) && + (battery_icon == BatteryIconInvertedPercent) && (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, 11, 4, AlignCenter, AlignCenter, batteryPercentile); } else if( - (battery_style == BatteryStyleRetro3) && + (battery_icon == BatteryIconRetro3) && (power->state != PowerStateCharging)) { //Retro style segmented display, 3 parts if(power->info.charge > 25) { canvas_draw_box(canvas, 2, 2, 6, 4); @@ -53,7 +53,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_draw_box(canvas, 16, 2, 6, 4); } } else if( - (battery_style == BatteryStyleRetro5) && + (battery_icon == BatteryIconRetro5) && (power->state != PowerStateCharging)) { //Retro style segmented display, 5 parts if(power->info.charge > 10) { canvas_draw_box(canvas, 2, 2, 3, 4); @@ -71,7 +71,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_draw_box(canvas, 18, 2, 3, 4); } } else if( - (battery_style == BatteryStyleBarPercent) && + (battery_icon == BatteryIconBarPercent) && (power->state != PowerStateCharging) && // Default bar display with percentage (power->info.voltage_battery_charging >= 4.2)) { // not looking nice with low voltage indicator @@ -145,7 +145,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { if(power->state == PowerStateCharging) { canvas_set_bitmap_mode(canvas, 1); // TODO: replace -1 magic for uint8_t with re-framing - if(battery_style == BatteryStylePercent || battery_style == BatteryStyleBarPercent) { + if(battery_icon == BatteryIconPercent || battery_icon == BatteryIconBarPercent) { canvas_set_color(canvas, ColorBlack); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); @@ -154,7 +154,7 @@ void power_draw_battery_callback(Canvas* canvas, void* context) { canvas_set_font(canvas, FontBatteryPercent); canvas_draw_str_aligned( canvas, 16, 4, AlignCenter, AlignCenter, batteryPercentile); - } else if(battery_style == BatteryStyleInvertedPercent) { + } else if(battery_icon == BatteryIconInvertedPercent) { canvas_set_color(canvas, ColorWhite); canvas_draw_box(canvas, 1, 1, 22, 6); canvas_draw_icon(canvas, 2, -1, &I_Charging_lightning_9x10); diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 58c85a9a9..1752643d1 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -26,14 +26,15 @@ typedef enum { } PowerEventType; typedef enum { - BatteryStyleOff = 1, - BatteryStyleBar = 2, - BatteryStylePercent = 3, - BatteryStyleInvertedPercent = 4, - BatteryStyleRetro3 = 5, - BatteryStyleRetro5 = 6, - BatteryStyleBarPercent = 0, -} BatteryStyle; + BatteryIconOff, + BatteryIconBar, + BatteryIconPercent, + BatteryIconInvertedPercent, + BatteryIconRetro3, + BatteryIconRetro5, + BatteryIconBarPercent, + BatteryIconCount, +} BatteryIcon; typedef union { uint8_t battery_level; diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index 7f93e6071..da4c1e839 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -19,7 +19,7 @@ typedef struct { int32_t cycle_anims; bool unlock_anims; char asset_pack[MAX_PACK_NAME_LEN]; - BatteryStyle battery_style; + BatteryIcon battery_icon; uint16_t anim_speed; bool sort_ignore_dirs; bool status_bar; diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index 4b7d14ff2..ae7dd3405 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -60,21 +60,13 @@ static void xtreme_app_scene_main_unlock_anims_changed(VariableItem* item) { app->settings_changed = true; } -const char* const battery_style_names[] = +const char* const battery_icon_names[] = {"OFF", "Bar", "%", "Inv. %", "Retro 3", "Retro 5", "Bar %"}; -const int32_t battery_style_values[COUNT_OF(battery_style_names)] = { - BatteryStyleOff, - BatteryStyleBar, - BatteryStylePercent, - BatteryStyleInvertedPercent, - BatteryStyleRetro3, - BatteryStyleRetro5, - BatteryStyleBarPercent}; -static void xtreme_app_scene_main_battery_style_changed(VariableItem* item) { +static void xtreme_app_scene_main_battery_icon_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, battery_style_names[index]); - XTREME_SETTINGS()->battery_style = battery_style_values[index]; + variable_item_set_current_value_text(item, battery_icon_names[index]); + XTREME_SETTINGS()->battery_icon = battery_icon_values[index]; app->settings_changed = true; } @@ -212,14 +204,14 @@ void xtreme_app_scene_main_on_enter(void* context) { item = variable_item_list_add( var_item_list, - "Battery Style", - COUNT_OF(battery_style_names), - xtreme_app_scene_main_battery_style_changed, + "Battery Icon", + BatteryIconCount, + xtreme_app_scene_main_battery_icon_changed, app); value_index = value_index_int32( - xtreme_settings->battery_style, battery_style_values, COUNT_OF(battery_style_names)); + xtreme_settings->battery_icon, battery_icon_values, BatteryIconCount); variable_item_set_current_value_index(item, value_index); - variable_item_set_current_value_text(item, battery_style_names[value_index]); + variable_item_set_current_value_text(item, battery_icon_names[value_index]); item = variable_item_list_add( var_item_list, "Status Bar", 2, xtreme_app_scene_main_status_bar_changed, app); From 537b22dd839830353df72fa2e26c54fca908c224 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 22:20:30 +0000 Subject: [PATCH 220/231] Update settings file --- applications/services/xtreme/settings.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index da4c1e839..58dd347d3 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -11,18 +11,23 @@ #define MAX_PACK_NAME_LEN 32 -#define XTREME_SETTINGS_VERSION (1) +#define XTREME_SETTINGS_VERSION (2) #define XTREME_SETTINGS_PATH INT_PATH(XTREME_SETTINGS_FILE_NAME) #define XTREME_SETTINGS_MAGIC (0x69) +// Some settings function backwards (logically) in +// order to fit the default value we want +// (values will default to 0 / false) typedef struct { + char asset_pack[MAX_PACK_NAME_LEN]; + uint16_t anim_speed; int32_t cycle_anims; bool unlock_anims; - char asset_pack[MAX_PACK_NAME_LEN]; BatteryIcon battery_icon; - uint16_t anim_speed; + bool status_icons; + bool status_bar_back; + bool status_bar_frames; bool sort_ignore_dirs; - bool status_bar; bool bad_bt; } XtremeSettings; From e41fa45878613c19ee246203962590c410b523b6 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 22:37:15 +0000 Subject: [PATCH 221/231] Swap status bar options --- applications/services/xtreme/settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index 58dd347d3..e270121b1 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -25,8 +25,8 @@ typedef struct { bool unlock_anims; BatteryIcon battery_icon; bool status_icons; - bool status_bar_back; bool status_bar_frames; + bool status_bar_back; bool sort_ignore_dirs; bool bad_bt; } XtremeSettings; From b525874ac72a5849de00b39c83e506dd062723e9 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 23:01:34 +0000 Subject: [PATCH 222/231] Add section dividers to xfw app --- .../xtreme_app/scenes/xtreme_app_scene_main.c | 59 +++++++++++-------- 1 file changed, 34 insertions(+), 25 deletions(-) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index ae7dd3405..70cd106e3 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -78,6 +78,20 @@ static void xtreme_app_scene_main_status_bar_changed(VariableItem* item) { app->settings_changed = true; } +static void xtreme_app_scene_main_subghz_extend_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_extend = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); + app->subghz_changed = true; +} + +static void xtreme_app_scene_main_subghz_bypass_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + app->subghz_bypass = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); + app->subghz_changed = true; +} + static void xtreme_app_scene_main_sort_folders_before_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); @@ -95,20 +109,6 @@ static void xtreme_app_scene_main_xp_level_changed(VariableItem* item) { app->level_changed = true; } -static void xtreme_app_scene_main_subghz_extend_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->subghz_extend = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); - app->subghz_changed = true; -} - -static void xtreme_app_scene_main_subghz_bypass_changed(VariableItem* item) { - XtremeApp* app = variable_item_get_context(item); - app->subghz_bypass = variable_item_get_current_value_index(item); - variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - app->subghz_changed = true; -} - void xtreme_app_scene_main_on_enter(void* context) { XtremeApp* app = context; XtremeSettings* xtreme_settings = XTREME_SETTINGS(); @@ -202,6 +202,9 @@ void xtreme_app_scene_main_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->unlock_anims); variable_item_set_current_value_text(item, xtreme_settings->unlock_anims ? "ON" : "OFF"); + + variable_item_list_add(var_item_list, " = Status Bar =", 0, NULL, app); + item = variable_item_list_add( var_item_list, "Battery Icon", @@ -218,6 +221,22 @@ void xtreme_app_scene_main_on_enter(void* context) { variable_item_set_current_value_index(item, xtreme_settings->status_bar); variable_item_set_current_value_text(item, xtreme_settings->status_bar ? "ON" : "OFF"); + + variable_item_list_add(var_item_list, " = Protocols =", 0, NULL, app); + + item = variable_item_list_add( + var_item_list, "SubGHz Extend", 2, xtreme_app_scene_main_subghz_extend_changed, app); + variable_item_set_current_value_index(item, app->subghz_extend); + variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_main_subghz_bypass_changed, app); + variable_item_set_current_value_index(item, app->subghz_bypass); + variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); + + + variable_item_list_add(var_item_list, " = Misc =", 0, NULL, app); + item = variable_item_list_add( var_item_list, "Sort Dirs First", @@ -238,19 +257,9 @@ void xtreme_app_scene_main_on_enter(void* context) { variable_item_set_current_value_index(item, app->dolphin_level - 1); variable_item_set_current_value_text(item, level_str); - item = variable_item_list_add( - var_item_list, "SubGHz Extend", 2, xtreme_app_scene_main_subghz_extend_changed, app); - variable_item_set_current_value_index(item, app->subghz_extend); - variable_item_set_current_value_text(item, app->subghz_extend ? "ON" : "OFF"); - - item = variable_item_list_add( - var_item_list, "SubGHz Bypass", 2, xtreme_app_scene_main_subghz_bypass_changed, app); - variable_item_set_current_value_index(item, app->subghz_bypass); - variable_item_set_current_value_text(item, app->subghz_bypass ? "ON" : "OFF"); - FuriString* version_tag = furi_string_alloc_printf( "%s %s", version_get_gitbranchnum(NULL), version_get_builddate(NULL)); - item = variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); + variable_item_list_add(var_item_list, furi_string_get_cstr(version_tag), 0, NULL, app); view_dispatcher_switch_to_view(app->view_dispatcher, XtremeAppViewVarItemList); } From 5b8ce5f789f8168771a63110e9e5bf1f932e1ba7 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 23:02:01 +0000 Subject: [PATCH 223/231] Fix battery icon value init --- .../settings/xtreme_app/scenes/xtreme_app_scene_main.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index 70cd106e3..c1c03672a 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -66,7 +66,7 @@ static void xtreme_app_scene_main_battery_icon_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, battery_icon_names[index]); - XTREME_SETTINGS()->battery_icon = battery_icon_values[index]; + XTREME_SETTINGS()->battery_icon = index; app->settings_changed = true; } @@ -211,9 +211,7 @@ void xtreme_app_scene_main_on_enter(void* context) { BatteryIconCount, xtreme_app_scene_main_battery_icon_changed, app); - value_index = value_index_int32( - xtreme_settings->battery_icon, battery_icon_values, BatteryIconCount); - variable_item_set_current_value_index(item, value_index); + value_index = xtreme_settings->battery_icon; variable_item_set_current_value_text(item, battery_icon_names[value_index]); item = variable_item_list_add( From 33f877e888eda74d4529bc1350695c0fe62002eb Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 23:38:09 +0000 Subject: [PATCH 224/231] Fix battery icon setting again --- .../services/power/power_service/power.h | 16 ++++++++-------- .../xtreme_app/scenes/xtreme_app_scene_main.c | 5 +++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/applications/services/power/power_service/power.h b/applications/services/power/power_service/power.h index 1752643d1..6e41d5c6e 100644 --- a/applications/services/power/power_service/power.h +++ b/applications/services/power/power_service/power.h @@ -26,14 +26,14 @@ typedef enum { } PowerEventType; typedef enum { - BatteryIconOff, - BatteryIconBar, - BatteryIconPercent, - BatteryIconInvertedPercent, - BatteryIconRetro3, - BatteryIconRetro5, - BatteryIconBarPercent, - BatteryIconCount, + BatteryIconOff = 1, + BatteryIconBar = 2, + BatteryIconPercent = 3, + BatteryIconInvertedPercent = 4, + BatteryIconRetro3 = 5, + BatteryIconRetro5 = 6, + BatteryIconBarPercent = 0, + BatteryIconCount = 7, } BatteryIcon; typedef union { diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index c1c03672a..e36c988d0 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -66,7 +66,7 @@ static void xtreme_app_scene_main_battery_icon_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); uint8_t index = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, battery_icon_names[index]); - XTREME_SETTINGS()->battery_icon = index; + XTREME_SETTINGS()->battery_icon = (index + 1) % BatteryIconCount; app->settings_changed = true; } @@ -211,7 +211,8 @@ void xtreme_app_scene_main_on_enter(void* context) { BatteryIconCount, xtreme_app_scene_main_battery_icon_changed, app); - value_index = xtreme_settings->battery_icon; + value_index = (xtreme_settings->battery_icon + BatteryIconCount - 1) % BatteryIconCount; + variable_item_set_current_value_index(item, value_index); variable_item_set_current_value_text(item, battery_icon_names[value_index]); item = variable_item_list_add( From 9266e9f47c9dd7964e1980b4cc78ac02446eb706 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 23:41:30 +0000 Subject: [PATCH 225/231] More statubar settings --- applications/services/gui/gui.c | 168 ++++++++++-------- applications/services/xtreme/settings.h | 4 +- .../xtreme_app/scenes/xtreme_app_scene_main.c | 36 +++- 3 files changed, 122 insertions(+), 86 deletions(-) diff --git a/applications/services/gui/gui.c b/applications/services/gui/gui.c index 2084b6337..3eadb41f9 100644 --- a/applications/services/gui/gui.c +++ b/applications/services/gui/gui.c @@ -60,7 +60,7 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { /* for support black theme - paint white area and * draw icon with transparent white color */ - if(xtreme_settings->status_bar) { + if(xtreme_settings->bar_background) { canvas_set_color(gui->canvas, ColorWhite); canvas_draw_box(gui->canvas, 1, 1, 9, 7); canvas_draw_box(gui->canvas, 7, 3, 58, 6); @@ -74,72 +74,76 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { } canvas_set_bitmap_mode(gui->canvas, 0); + uint8_t x; + // Right side - uint8_t x = GUI_DISPLAY_WIDTH - 1; - ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); - while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { - ViewPort* view_port = *ViewPortArray_ref(it); - if(view_port_is_enabled(view_port)) { - width = view_port_get_width(view_port); - if(!width) width = 8; - // Recalculate next position - right_used += (width + 2); - x -= (width + 2); - // Prepare work area background - canvas_frame_set( - gui->canvas, - x - 1, - GUI_STATUS_BAR_Y + 1, - width + 2, - GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - // Hide battery background - if(xtreme_settings->status_bar && xtreme_settings->battery_icon != BatteryIconOff) { - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); + if(xtreme_settings->battery_icon != BatteryIconOff) { + x = GUI_DISPLAY_WIDTH - 1; + ViewPortArray_it(it, gui->layers[GuiLayerStatusBarRight]); + while(!ViewPortArray_end_p(it) && right_used < GUI_STATUS_BAR_WIDTH) { + ViewPort* view_port = *ViewPortArray_ref(it); + if(view_port_is_enabled(view_port)) { + width = view_port_get_width(view_port); + if(!width) width = 8; + // Recalculate next position + right_used += (width + 2); + x -= (width + 2); + // Prepare work area background + canvas_frame_set( + gui->canvas, + x - 1, + GUI_STATUS_BAR_Y + 1, + width + 2, + GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); + // Hide battery background + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, -1, 0, canvas_width(gui->canvas) + 1, canvas_height(gui->canvas)); + } + canvas_set_color(gui->canvas, ColorBlack); + // ViewPort draw + canvas_frame_set( + gui->canvas, + x - xtreme_settings->bar_borders, + GUI_STATUS_BAR_Y + 2, + width, + GUI_STATUS_BAR_WORKAREA_HEIGHT); + view_port_draw(view_port, gui->canvas); } - canvas_set_color(gui->canvas, ColorBlack); - // ViewPort draw + ViewPortArray_next(it); + } + // Draw frame around icons on the right + if(right_used) { canvas_frame_set( gui->canvas, - x - xtreme_settings->status_bar, - GUI_STATUS_BAR_Y + 2, - width, - GUI_STATUS_BAR_WORKAREA_HEIGHT); - view_port_draw(view_port, gui->canvas); - } - ViewPortArray_next(it); - } - // Draw frame around icons on the right - if(right_used) { - canvas_frame_set( - gui->canvas, - GUI_DISPLAY_WIDTH - 4 - right_used, - GUI_STATUS_BAR_Y, - right_used + 4, - GUI_STATUS_BAR_HEIGHT); - // Disable battery border - if(xtreme_settings->status_bar && xtreme_settings->battery_icon != BatteryIconOff) { - canvas_set_color(gui->canvas, ColorBlack); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); + GUI_DISPLAY_WIDTH - 4 - right_used, + GUI_STATUS_BAR_Y, + right_used + 4, + GUI_STATUS_BAR_HEIGHT); + // Disable battery border + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorBlack); + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } } } // Left side - if(xtreme_settings->status_bar) { + if(xtreme_settings->status_icons) { x = 2; ViewPortArray_it(it, gui->layers[GuiLayerStatusBarLeft]); while(!ViewPortArray_end_p(it) && (right_used + left_used) < GUI_STATUS_BAR_WIDTH) { @@ -154,9 +158,11 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { GUI_STATUS_BAR_Y + 1, width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + } canvas_set_color(gui->canvas, ColorBlack); // ViewPort draw canvas_frame_set( @@ -178,9 +184,11 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { GUI_STATUS_BAR_Y + 1, width + 2, GUI_STATUS_BAR_WORKAREA_HEIGHT + 2); - canvas_set_color(gui->canvas, ColorWhite); - canvas_draw_box( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + if(xtreme_settings->bar_borders) { + canvas_set_color(gui->canvas, ColorWhite); + canvas_draw_box( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas)); + } canvas_set_color(gui->canvas, ColorBlack); // Draw Icon canvas_frame_set( @@ -193,20 +201,22 @@ static void gui_redraw_status_bar(Gui* gui, bool need_attention) { // Draw frame around icons on the left if(left_used) { canvas_frame_set(gui->canvas, 0, 0, left_used + 3, GUI_STATUS_BAR_HEIGHT); - canvas_draw_rframe( - gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); - canvas_draw_line( - gui->canvas, - canvas_width(gui->canvas) - 2, - 1, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); - canvas_draw_line( - gui->canvas, - 1, - canvas_height(gui->canvas) - 2, - canvas_width(gui->canvas) - 2, - canvas_height(gui->canvas) - 2); + if(xtreme_settings->bar_borders) { + canvas_draw_rframe( + gui->canvas, 0, 0, canvas_width(gui->canvas), canvas_height(gui->canvas), 1); + canvas_draw_line( + gui->canvas, + canvas_width(gui->canvas) - 2, + 1, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + canvas_draw_line( + gui->canvas, + 1, + canvas_height(gui->canvas) - 2, + canvas_width(gui->canvas) - 2, + canvas_height(gui->canvas) - 2); + } } } } diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index e270121b1..d8c0d904a 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -25,8 +25,8 @@ typedef struct { bool unlock_anims; BatteryIcon battery_icon; bool status_icons; - bool status_bar_frames; - bool status_bar_back; + bool bar_borders; + bool bar_background; bool sort_ignore_dirs; bool bad_bt; } XtremeSettings; diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index e36c988d0..6292c8e1d 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -70,11 +70,27 @@ static void xtreme_app_scene_main_battery_icon_changed(VariableItem* item) { app->settings_changed = true; } -static void xtreme_app_scene_main_status_bar_changed(VariableItem* item) { +static void xtreme_app_scene_main_status_icons_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); bool value = variable_item_get_current_value_index(item); variable_item_set_current_value_text(item, value ? "ON" : "OFF"); - XTREME_SETTINGS()->status_bar = value; + XTREME_SETTINGS()->status_icons = value; + app->settings_changed = true; +} + +static void xtreme_app_scene_main_bar_borders_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bar_borders = value; + app->settings_changed = true; +} + +static void xtreme_app_scene_main_bar_background_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "ON" : "OFF"); + XTREME_SETTINGS()->bar_background = value; app->settings_changed = true; } @@ -216,9 +232,19 @@ void xtreme_app_scene_main_on_enter(void* context) { variable_item_set_current_value_text(item, battery_icon_names[value_index]); item = variable_item_list_add( - var_item_list, "Status Bar", 2, xtreme_app_scene_main_status_bar_changed, app); - variable_item_set_current_value_index(item, xtreme_settings->status_bar); - variable_item_set_current_value_text(item, xtreme_settings->status_bar ? "ON" : "OFF"); + var_item_list, "Status Icons", 2, xtreme_app_scene_main_status_icons_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->status_icons); + variable_item_set_current_value_text(item, xtreme_settings->status_icons ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Bar Borders", 2, xtreme_app_scene_main_bar_borders_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bar_borders); + variable_item_set_current_value_text(item, xtreme_settings->bar_borders ? "ON" : "OFF"); + + item = variable_item_list_add( + var_item_list, "Bar Background", 2, xtreme_app_scene_main_bar_background_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bar_background); + variable_item_set_current_value_text(item, xtreme_settings->bar_background ? "ON" : "OFF"); variable_item_list_add(var_item_list, " = Protocols =", 0, NULL, app); From f95d1b2e96ea5efa35455304b08773ac46803a5e Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Mon, 13 Feb 2023 23:46:48 +0000 Subject: [PATCH 226/231] Add bad kb mode setting to xfw app --- applications/services/xtreme/settings.h | 2 +- .../xtreme_app/scenes/xtreme_app_scene_main.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/applications/services/xtreme/settings.h b/applications/services/xtreme/settings.h index d8c0d904a..b4f12b1b8 100644 --- a/applications/services/xtreme/settings.h +++ b/applications/services/xtreme/settings.h @@ -27,8 +27,8 @@ typedef struct { bool status_icons; bool bar_borders; bool bar_background; - bool sort_ignore_dirs; bool bad_bt; + bool sort_ignore_dirs; } XtremeSettings; XtremeSettings* XTREME_SETTINGS(); diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index 6292c8e1d..c3a05bc6b 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -94,6 +94,14 @@ static void xtreme_app_scene_main_bar_background_changed(VariableItem* item) { app->settings_changed = true; } +static void xtreme_app_scene_main_bad_bk_mode_changed(VariableItem* item) { + XtremeApp* app = variable_item_get_context(item); + bool value = variable_item_get_current_value_index(item); + variable_item_set_current_value_text(item, value ? "BT" : "USB"); + XTREME_SETTINGS()->bad_bt = value; + app->settings_changed = true; +} + static void xtreme_app_scene_main_subghz_extend_changed(VariableItem* item) { XtremeApp* app = variable_item_get_context(item); app->subghz_extend = variable_item_get_current_value_index(item); @@ -249,6 +257,11 @@ void xtreme_app_scene_main_on_enter(void* context) { variable_item_list_add(var_item_list, " = Protocols =", 0, NULL, app); + item = variable_item_list_add( + var_item_list, "Bad KB Mode", 2, xtreme_app_scene_main_bad_bk_mode_changed, app); + variable_item_set_current_value_index(item, xtreme_settings->bad_bt); + variable_item_set_current_value_text(item, xtreme_settings->bad_bt ? "BT" : "USB"); + item = variable_item_list_add( var_item_list, "SubGHz Extend", 2, xtreme_app_scene_main_subghz_extend_changed, app); variable_item_set_current_value_index(item, app->subghz_extend); From 6c561d8c675bf549217cd0c36c8d6f9a809449e8 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 14 Feb 2023 00:14:53 +0000 Subject: [PATCH 227/231] Fix subbrute symbols --- firmware/targets/f7/api_symbols.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 55773064f..5e7c08083 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -5095,7 +5095,7 @@ Variable,-,subghz_protocol_princeton_encoder,const SubGhzProtocolEncoder, Variable,+,subghz_protocol_raw,const SubGhzProtocol, Variable,+,subghz_protocol_raw_decoder,const SubGhzProtocolDecoder, Variable,+,subghz_protocol_raw_encoder,const SubGhzProtocolEncoder, -Variable,-,subghz_protocol_registry,const SubGhzProtocolRegistry, +Variable,+,subghz_protocol_registry,const SubGhzProtocolRegistry, Variable,-,subghz_protocol_scher_khan,const SubGhzProtocol, Variable,-,subghz_protocol_scher_khan_decoder,const SubGhzProtocolDecoder, Variable,-,subghz_protocol_scher_khan_encoder,const SubGhzProtocolEncoder, From a231ec0bbb9dd68178d1afcdde41f0d69f3b9892 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 14 Feb 2023 02:36:33 +0000 Subject: [PATCH 228/231] Fix asset pack sorting --- .../xtreme_app/scenes/xtreme_app_scene_main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c index c3a05bc6b..03602bb58 100644 --- a/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c +++ b/applications/settings/xtreme_app/scenes/xtreme_app_scene_main.c @@ -161,17 +161,16 @@ void xtreme_app_scene_main_on_enter(void* context) { File* folder = storage_file_alloc(storage); FileInfo info; char* name = malloc(MAX_PACK_NAME_LEN); - do { - if(!storage_dir_open(folder, PACKS_DIR)) break; - while(true) { - if(!storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) break; + if(storage_dir_open(folder, PACKS_DIR)) { + while(storage_dir_read(folder, &info, name, MAX_PACK_NAME_LEN)) { if(info.flags & FSF_DIRECTORY) { char* copy = malloc(MAX_PACK_NAME_LEN); strlcpy(copy, name, MAX_PACK_NAME_LEN); uint idx = 0; if(strcmp(copy, "NSFW") != 0) { for(; idx < asset_packs_size(app->asset_packs); idx++) { - if(strcasecmp(copy, *asset_packs_get(app->asset_packs, idx)) < 0) { + char* comp = *asset_packs_get(app->asset_packs, idx); + if(strcasecmp(copy, comp) < 0 && strcmp(comp, "NSFW") != 0) { break; } } @@ -184,7 +183,7 @@ void xtreme_app_scene_main_on_enter(void* context) { } } } - } while(false); + } free(name); storage_file_free(folder); furi_record_close(RECORD_STORAGE); From 4111070d1c3f4e858f4761e6475779f22c584e82 Mon Sep 17 00:00:00 2001 From: Willy-JL Date: Tue, 14 Feb 2023 03:47:02 +0000 Subject: [PATCH 229/231] Temp fix crash by bad manifest --- .../desktop/animations/animation_manager.c | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/applications/services/desktop/animations/animation_manager.c b/applications/services/desktop/animations/animation_manager.c index 5b548c5bc..2d100b2e1 100644 --- a/applications/services/desktop/animations/animation_manager.c +++ b/applications/services/desktop/animations/animation_manager.c @@ -372,11 +372,11 @@ static bool animation_manager_is_valid_idle_animation( static StorageAnimation* animation_manager_select_idle_animation(AnimationManager* animation_manager) { - const char* old_animation_name = NULL; - if(animation_manager->current_animation) { - old_animation_name = - animation_storage_get_meta(animation_manager->current_animation)->name; - } + // const char* avoid_animation = NULL; + // if(animation_manager->current_animation) { + // avoid_animation = animation_storage_get_meta(animation_manager->current_animation)->name; + // } + UNUSED(animation_manager); StorageAnimationList_t animation_list; StorageAnimationList_init(animation_list); @@ -385,6 +385,7 @@ static StorageAnimation* Dolphin* dolphin = furi_record_open(RECORD_DOLPHIN); DolphinStats stats = dolphin_stats(dolphin); furi_record_close(RECORD_DOLPHIN); + // avoid_animation = StorageAnimationList_size(animation_list) > 1 ? avoid_animation : NULL; uint32_t whole_weight = 0; StorageAnimationList_it_t it; @@ -395,11 +396,14 @@ static StorageAnimation* animation_storage_get_meta(storage_animation); bool valid = animation_manager_is_valid_idle_animation(manifest_info, &stats, unlock); - if(old_animation_name != NULL) { - if(strcmp(manifest_info->name, old_animation_name) == 0) { - valid = false; - } - } + // Avoid repeating animation + // Bad / empty manifests can crash flipper and (very rarely) require DFU + // Need better solution, disabled for now + // if(avoid_animation != NULL) { + // if(strcmp(manifest_info->name, avoid_animation) == 0) { + // valid = false; + // } + // } if(valid) { whole_weight += manifest_info->weight; From a718f1a43c567bf7bc33593c4344f22d1616adbd Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 14 Feb 2023 09:37:17 +0100 Subject: [PATCH 230/231] V41 Release pending --- firmware/targets/f7/api_symbols.csv | 2 +- lib/subghz/protocols/nice_flor_s.c | 158 ++++++++++++++++++---------- 2 files changed, 102 insertions(+), 58 deletions(-) diff --git a/firmware/targets/f7/api_symbols.csv b/firmware/targets/f7/api_symbols.csv index 5e7c08083..adca3b714 100644 --- a/firmware/targets/f7/api_symbols.csv +++ b/firmware/targets/f7/api_symbols.csv @@ -1,5 +1,5 @@ entry,status,name,type,params -Version,+,17.0,, +Version,+,14.0,, Header,+,applications/services/bt/bt_service/bt.h,, Header,+,applications/services/cli/cli.h,, Header,+,applications/services/cli/cli_vcp.h,, diff --git a/lib/subghz/protocols/nice_flor_s.c b/lib/subghz/protocols/nice_flor_s.c index 6447676cc..d3a2d6acd 100644 --- a/lib/subghz/protocols/nice_flor_s.c +++ b/lib/subghz/protocols/nice_flor_s.c @@ -100,7 +100,7 @@ void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name); } instance->encoder.repeat = 10; - instance->encoder.size_upload = 1728; //wrong!! upload 186*16 = 2976 - actual size about 1728 + instance->encoder.size_upload = 2400; //wrong!! upload 186*16 = 2976 - actual size about 1728 instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); instance->encoder.is_running = false; return instance; @@ -113,6 +113,8 @@ void subghz_protocol_encoder_nice_flor_s_free(void* context) { free(instance); } +static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit); + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance @@ -160,8 +162,8 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); //Send key data - for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { - if(bit_read(instance->generic.data, i - 1)) { + for(uint8_t j = 52; j > 0; j--) { + if(bit_read(instance->generic.data, j - 1)) { //send bit 1 instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); @@ -175,6 +177,35 @@ static void subghz_protocol_encoder_nice_flor_s_get_upload( false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); } } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + uint8_t add_data[10] = {0}; + for(size_t i = 0; i < 7; i++) { + add_data[i] = (instance->generic.data >> (48 - i * 8)) & 0xFF; + } + subghz_protocol_nice_one_get_data(add_data, loops[i], loops[i]); + instance->generic.data_2 = 0; + for(size_t j = 7; j < 10; j++) { + instance->generic.data_2 <<= 8; + instance->generic.data_2 += add_data[j]; + } + + //Send key data + for(uint8_t j = 24; j > 4; j--) { + if(bit_read(instance->generic.data_2, j - 1)) { + //send bit 1 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + } else { + //send bit 0 + instance->encoder.upload[index++] = level_duration_make( + true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short); + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long); + } + } + } //Send stop bit instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3); @@ -197,6 +228,8 @@ bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperForma //optional parameter parameter flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + // flipper_format_read_uint32( + // flipper_format, "Data", (uint32_t*)&instance->generic.data_2, 1); subghz_protocol_nice_flor_s_remote_controller( &instance->generic, instance->nice_flor_s_rainbow_table_file_name); @@ -216,6 +249,17 @@ bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperForma break; } + if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) { + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + break; + } + uint32_t temp = (instance->generic.data_2 >> 4) & 0xFFFFF; + if(!flipper_format_update_uint32(flipper_format, "Data", &temp, 1)) { + FURI_LOG_E(TAG, "Unable to add Data"); + } + } + instance->encoder.is_running = true; res = true; @@ -247,63 +291,63 @@ LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) { return ret; } -// /** -// * Read bytes from rainbow table -// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 -// * @return crc -// */ -// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { -// uint8_t crc = 0; -// uint8_t crc_data = 0xff; -// for(uint8_t i = 4; i < 68; i++) { -// if(subghz_protocol_blocks_get_bit_array(p, i)) { -// crc = crc_data ^ 1; -// } else { -// crc = crc_data; -// } -// crc_data >>= 1; -// if((crc & 0x01)) { -// crc_data ^= 0x97; -// } -// } -// crc = 0; -// for(uint8_t i = 0; i < 8; i++) { -// crc <<= 1; -// if((crc_data >> i) & 0x01) crc = crc | 1; -// } -// return crc; -// } +/** + * Read bytes from rainbow table + * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10 + * @return crc + */ +static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) { + uint8_t crc = 0; + uint8_t crc_data = 0xff; + for(uint8_t i = 4; i < 68; i++) { + if(subghz_protocol_blocks_get_bit_array(p, i)) { + crc = crc_data ^ 1; + } else { + crc = crc_data; + } + crc_data >>= 1; + if((crc & 0x01)) { + crc_data ^= 0x97; + } + } + crc = 0; + for(uint8_t i = 0; i < 8; i++) { + crc <<= 1; + if((crc_data >> i) & 0x01) crc = crc | 1; + } + return crc; +} -// /** -// * Read bytes from rainbow table -// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX -// * @param num_parcel parcel number 0..15 -// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down -// */ -// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { -// uint8_t k = 0; -// uint8_t crc = 0; -// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); -// if(num_parcel < 4) { -// k = 0x8f; -// } else { -// k = 0x80; -// } +/** + * Read bytes from rainbow table + * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX + * @param num_parcel parcel number 0..15 + * @param hold_bit 0 - the button was only pressed, 1 - the button was held down + */ +static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) { + uint8_t k = 0; + uint8_t crc = 0; + p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4); + if(num_parcel < 4) { + k = 0x8f; + } else { + k = 0x80; + } -// if(!hold_bit) { -// hold_bit = 0; -// } else { -// hold_bit = 0x10; -// } -// k = num_parcel ^ k; -// p[7] = k; -// p[8] = hold_bit ^ (k << 4); + if(!hold_bit) { + hold_bit = 0; + } else { + hold_bit = 0x10; + } + k = num_parcel ^ k; + p[7] = k; + p[8] = hold_bit ^ (k << 4); -// crc = subghz_protocol_nice_one_crc(p); + crc = subghz_protocol_nice_one_crc(p); -// p[8] |= crc >> 4; -// p[9] = crc << 4; -// } + p[8] |= crc >> 4; + p[9] = crc << 4; +} /** * Read bytes from rainbow table @@ -691,4 +735,4 @@ void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* o instance->generic.cnt, instance->generic.btn); } -} +} \ No newline at end of file From 508d046dfeef1f29d5e6ae5a2d2bc710331a1a7e Mon Sep 17 00:00:00 2001 From: VerstreuteSeele Date: Tue, 14 Feb 2023 09:41:17 +0100 Subject: [PATCH 231/231] V41 Release Candidate Changes --- fbt_options.py | 2 +- scripts/version.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fbt_options.py b/fbt_options.py index 8fb8f1429..d3670b01a 100644 --- a/fbt_options.py +++ b/fbt_options.py @@ -14,7 +14,7 @@ DEBUG = 0 # Suffix to add to files when building distribution # If OS environment has DIST_SUFFIX set, it will be used instead -DIST_SUFFIX = "XFW-0040_01252023" +DIST_SUFFIX = "XFW-0041_02142023" # Coprocessor firmware COPRO_OB_DATA = "scripts/ob.data" diff --git a/scripts/version.py b/scripts/version.py index 7f0a11349..ed2e83162 100644 --- a/scripts/version.py +++ b/scripts/version.py @@ -18,7 +18,7 @@ class GitVersion: ] def get_version_info(self): - commit = branch = branch_num = "XFW-0040" + commit = branch = branch_num = "XFW-0041" # We dont use an `or` in commands that we expect to fail. It will serve no function. # We also dont try;exept an entire block of code. This is bad practise. We only try the single part that we expect to fail!